mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Initial work on new netstandard symlink
This commit is contained in:
@@ -114,7 +114,7 @@ namespace Velopack.Json
|
||||
|
||||
internal static class CompiledJson
|
||||
{
|
||||
public static readonly JsonSerializerSettings Options = new JsonSerializerSettings {
|
||||
private static readonly JsonSerializerSettings Options = new JsonSerializerSettings {
|
||||
Converters = { new StringEnumConverter(), new SemanticVersionConverter() },
|
||||
ContractResolver = new JsonNameContractResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
|
||||
@@ -39,14 +39,30 @@ namespace Velopack
|
||||
: targetPath;
|
||||
|
||||
if (Directory.Exists(targetPath)) {
|
||||
#if NETFRAMEWORK || NETSTANDARD
|
||||
#if NETSTANDARD
|
||||
if (VelopackRuntimeInfo.IsWindows) {
|
||||
if (!CreateSymbolicLink(linkPath, finalTarget, SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
|
||||
ThrowLastWin32Error("Unable to create junction point / symlink.");
|
||||
} else {
|
||||
var linkInfo = new Mono.Unix.UnixSymbolicLinkInfo(linkPath);
|
||||
linkInfo.CreateSymbolicLinkTo(targetPath);
|
||||
}
|
||||
#elif NETFRAMEWORK
|
||||
if (!CreateSymbolicLink(linkPath, finalTarget, SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
|
||||
ThrowLastWin32Error("Unable to create junction point / symlink.");
|
||||
#else
|
||||
Directory.CreateSymbolicLink(linkPath, finalTarget);
|
||||
#endif
|
||||
} else if (File.Exists(targetPath)) {
|
||||
#if NETFRAMEWORK || NETSTANDARD
|
||||
#if NETSTANDARD
|
||||
if (VelopackRuntimeInfo.IsWindows) {
|
||||
if (!CreateSymbolicLink(linkPath, finalTarget, SYMBOLIC_LINK_FLAG_FILE | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
|
||||
ThrowLastWin32Error("Unable to create junction point / symlink.");
|
||||
} else {
|
||||
var fileInfo = new Mono.Unix.UnixFileInfo(targetPath);
|
||||
fileInfo.CreateSymbolicLink(linkPath);
|
||||
}
|
||||
#elif NETFRAMEWORK
|
||||
if (!CreateSymbolicLink(linkPath, finalTarget, SYMBOLIC_LINK_FLAG_FILE | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
|
||||
ThrowLastWin32Error("Unable to create junction point / symlink.");
|
||||
#else
|
||||
@@ -74,7 +90,7 @@ namespace Velopack
|
||||
{
|
||||
var isLink = TryGetLinkFsi(linkPath, out var fsi);
|
||||
if (fsi != null && !isLink) {
|
||||
throw new IOException("Path is not a junction point.");
|
||||
throw new IOException("Path is not a junction point / symlink.");
|
||||
} else {
|
||||
fsi?.Delete();
|
||||
}
|
||||
@@ -106,13 +122,19 @@ namespace Velopack
|
||||
private static string GetUnresolvedTarget(string linkPath)
|
||||
{
|
||||
if (TryGetLinkFsi(linkPath, out var fsi)) {
|
||||
#if NETFRAMEWORK || NETSTANDARD
|
||||
|
||||
#if NETSTANDARD
|
||||
if (VelopackRuntimeInfo.IsWindows) {
|
||||
return GetTargetWin32(linkPath);
|
||||
} else {
|
||||
return Mono.Unix.UnixPath.ReadLink(linkPath);
|
||||
}
|
||||
#elif NETFRAMEWORK
|
||||
return GetTargetWin32(linkPath);
|
||||
#else
|
||||
return fsi.LinkTarget!;
|
||||
#endif
|
||||
}
|
||||
|
||||
throw new IOException("Path does not exist or is not a junction point / symlink.");
|
||||
}
|
||||
|
||||
@@ -140,6 +162,67 @@ namespace Velopack
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK || NETSTANDARD
|
||||
private static string ToggleRelative(string basePath, string toggledPath)
|
||||
{
|
||||
// from https://github.com/RT-Projects/RT.Util/blob/master/RT.Util.Core/Paths/PathUtil.cs#L297
|
||||
if (basePath.Length == 0)
|
||||
throw new Exception("InvalidBasePath");
|
||||
if (toggledPath.Length == 0)
|
||||
throw new Exception("InvalidToggledPath");
|
||||
if (!Path.IsPathRooted(basePath))
|
||||
throw new Exception("BasePathNotAbsolute");
|
||||
|
||||
try { basePath = Path.GetFullPath(basePath + "\\"); } catch { throw new Exception("InvalidBasePath"); }
|
||||
|
||||
if (!Path.IsPathRooted(toggledPath)) {
|
||||
try {
|
||||
return StripTrailingSeparator(Path.GetFullPath(Path.Combine(basePath, toggledPath)));
|
||||
} catch {
|
||||
throw new Exception("InvalidToggledPath");
|
||||
}
|
||||
}
|
||||
|
||||
// Both basePath and toggledPath are absolute. Need to relativize toggledPath.
|
||||
try { toggledPath = Path.GetFullPath(toggledPath + "\\"); } catch { throw new Exception("InvalidToggledPath"); }
|
||||
|
||||
int prevPos = -1;
|
||||
int pos = toggledPath.IndexOf(Path.DirectorySeparatorChar);
|
||||
while (pos != -1 && pos < basePath.Length &&
|
||||
basePath.Substring(0, pos + 1).Equals(toggledPath.Substring(0, pos + 1), StringComparison.OrdinalIgnoreCase)) {
|
||||
prevPos = pos;
|
||||
pos = toggledPath.IndexOf(Path.DirectorySeparatorChar, pos + 1);
|
||||
}
|
||||
|
||||
if (prevPos == -1)
|
||||
throw new Exception("PathsOnDifferentDrives");
|
||||
var piece = basePath.Substring(prevPos + 1);
|
||||
var result = StripTrailingSeparator(
|
||||
(".." + Path.DirectorySeparatorChar).Repeat(piece.Count(ch => ch == Path.DirectorySeparatorChar))
|
||||
+ toggledPath.Substring(prevPos + 1));
|
||||
return result.Length == 0 ? "." : result;
|
||||
}
|
||||
|
||||
private static string Repeat(this string input, int numTimes)
|
||||
{
|
||||
if (numTimes == 0) return "";
|
||||
if (numTimes == 1) return input;
|
||||
if (numTimes == 2) return input + input;
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < numTimes; i++)
|
||||
sb.Append(input);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string StripTrailingSeparator(string path)
|
||||
{
|
||||
if (path.Length < 1)
|
||||
return path;
|
||||
if (path[path.Length - 1] == '/' || path[path.Length - 1] == '\\')
|
||||
return (path.Length == 3 && path[1] == ':') ? path : path.Substring(0, path.Length - 1);
|
||||
else
|
||||
return path;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum EFileAttributes : uint
|
||||
{
|
||||
@@ -194,7 +277,8 @@ namespace Velopack
|
||||
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
private static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);
|
||||
private static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath,
|
||||
uint dwFlags);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
@@ -281,63 +365,6 @@ namespace Velopack
|
||||
{
|
||||
throw new IOException(message, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
|
||||
}
|
||||
|
||||
private static string ToggleRelative(string basePath, string toggledPath)
|
||||
{
|
||||
// from https://github.com/RT-Projects/RT.Util/blob/master/RT.Util.Core/Paths/PathUtil.cs#L297
|
||||
if (basePath.Length == 0)
|
||||
throw new Exception("InvalidBasePath");
|
||||
if (toggledPath.Length == 0)
|
||||
throw new Exception("InvalidToggledPath");
|
||||
if (!Path.IsPathRooted(basePath))
|
||||
throw new Exception("BasePathNotAbsolute");
|
||||
|
||||
try { basePath = Path.GetFullPath(basePath + "\\"); } catch { throw new Exception("InvalidBasePath"); }
|
||||
|
||||
if (!Path.IsPathRooted(toggledPath)) {
|
||||
try {
|
||||
return StripTrailingSeparator(Path.GetFullPath(Path.Combine(basePath, toggledPath)));
|
||||
} catch {
|
||||
throw new Exception("InvalidToggledPath");
|
||||
}
|
||||
}
|
||||
|
||||
// Both basePath and toggledPath are absolute. Need to relativize toggledPath.
|
||||
try { toggledPath = Path.GetFullPath(toggledPath + "\\"); } catch { throw new Exception("InvalidToggledPath"); }
|
||||
int prevPos = -1;
|
||||
int pos = toggledPath.IndexOf(Path.DirectorySeparatorChar);
|
||||
while (pos != -1 && pos < basePath.Length && basePath.Substring(0, pos + 1).Equals(toggledPath.Substring(0, pos + 1), StringComparison.OrdinalIgnoreCase)) {
|
||||
prevPos = pos;
|
||||
pos = toggledPath.IndexOf(Path.DirectorySeparatorChar, pos + 1);
|
||||
}
|
||||
if (prevPos == -1)
|
||||
throw new Exception("PathsOnDifferentDrives");
|
||||
var piece = basePath.Substring(prevPos + 1);
|
||||
var result = StripTrailingSeparator((".." + Path.DirectorySeparatorChar).Repeat(piece.Count(ch => ch == Path.DirectorySeparatorChar))
|
||||
+ toggledPath.Substring(prevPos + 1));
|
||||
return result.Length == 0 ? "." : result;
|
||||
}
|
||||
|
||||
private static string Repeat(this string input, int numTimes)
|
||||
{
|
||||
if (numTimes == 0) return "";
|
||||
if (numTimes == 1) return input;
|
||||
if (numTimes == 2) return input + input;
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < numTimes; i++)
|
||||
sb.Append(input);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string StripTrailingSeparator(string path)
|
||||
{
|
||||
if (path.Length < 1)
|
||||
return path;
|
||||
if (path[path.Length - 1] == '/' || path[path.Length - 1] == '\\')
|
||||
return (path.Length == 3 && path[1] == ':') ? path : path.Substring(0, path.Length - 1);
|
||||
else
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="[13.0.1,)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[2.2.0,)" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" $(TargetFramework) == 'net6.0' ">
|
||||
|
||||
@@ -1,36 +1,43 @@
|
||||
namespace Velopack.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
|
||||
public class SimpleJson
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
private static readonly System.Text.Json.JsonSerializerOptions Options = new System.Text.Json.JsonSerializerOptions {
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = System.Text.Json.JsonCommentHandling.Skip,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
|
||||
Converters = {
|
||||
new System.Text.Json.Serialization.JsonStringEnumConverter(),
|
||||
new SemanticVersionConverter(),
|
||||
},
|
||||
private static readonly JsonSerializerSettings Options = new JsonSerializerSettings {
|
||||
Converters = { new StringEnumConverter(), new SemanticVersionConverter() },
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
#endif
|
||||
|
||||
public static T DeserializeObject<T>(string json)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return System.Text.Json.JsonSerializer.Deserialize<T>(json, Options);
|
||||
#else
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json, CompiledJson.Options);
|
||||
#endif
|
||||
return JsonConvert.DeserializeObject<T>(json, Options);
|
||||
}
|
||||
|
||||
public static string SerializeObject<T>(T obj)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return System.Text.Json.JsonSerializer.Serialize(obj, Options);
|
||||
#else
|
||||
return Newtonsoft.Json.JsonConvert.SerializeObject(obj, CompiledJson.Options);
|
||||
#endif
|
||||
return JsonConvert.SerializeObject(obj, Options);
|
||||
}
|
||||
}
|
||||
|
||||
private class SemanticVersionConverter : JsonConverter<SemanticVersion>
|
||||
{
|
||||
public override SemanticVersion ReadJson(JsonReader reader, Type objectType, SemanticVersion existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
string s = reader.Value as string;
|
||||
if (s == null) return null;
|
||||
return SemanticVersion.Parse(s);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, SemanticVersion value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null) {
|
||||
writer.WriteValue(value.ToFullString());
|
||||
} else {
|
||||
writer.WriteNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="Markdig" Version="0.37.0" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Text.Json;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Json;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Sources;
|
||||
using JsonPropertyNameAttribute = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Velopack.Windows;
|
||||
|
||||
namespace Velopack.Tests;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public class ShortcutTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
@@ -74,8 +74,8 @@ public class SymbolicLinkTests
|
||||
|
||||
SymbolicLink.Create(symFile, tmpFile, true);
|
||||
|
||||
Assert.True(File.Exists(symFile), "Symfile point exists now.");
|
||||
Assert.True(SymbolicLink.Exists(symFile), "Junction point exists now.");
|
||||
Assert.True(File.Exists(symFile), "Symlink should exist now.");
|
||||
Assert.True(SymbolicLink.Exists(symFile), "Symlink should exist now.");
|
||||
|
||||
Assert.Equal(tmpFile, SymbolicLink.GetTarget(symFile));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
using System.Text;
|
||||
using Velopack.Json;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Sources;
|
||||
|
||||
namespace Velopack.Tests.TestHelpers;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Text;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Json;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Locators;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Tests.TestHelpers;
|
||||
|
||||
@@ -48,7 +48,6 @@ public class UtilityTests
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
[SupportedOSPlatform("windows")]
|
||||
public void SetAppIdOnShortcutTest()
|
||||
{
|
||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);CA1416</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Choose>
|
||||
<When Condition="$([MSBuild]::IsOSPlatform('Windows'))">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net48</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0;net8.0;net48</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
@@ -20,7 +24,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Packaging" Version="8.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) ">
|
||||
@@ -30,8 +33,17 @@
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(TargetFramework)' == 'net6.0'">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj" SetTargetFramework="TargetFramework=netstandard2.0" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj" />
|
||||
</ItemGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user