Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fork process proxy for using workloads tools #283

Merged
merged 22 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nuke/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
using Nuke.Common.Tools.Git;
using Nuke.Common.Tools.GitVersion;
using vein.cmd;
using vein.compiler.shared;
using vein.json;
using vein.project;

Expand Down
1 change: 1 addition & 0 deletions .nuke/build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\tools\compiler\veinc.csproj" />
<ProjectReference Include="..\tools\rune-cli\rune-cli.csproj" />
</ItemGroup>

Expand Down
61 changes: 48 additions & 13 deletions lib/projectsystem/Workload.converters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ public override void WriteJson(JsonWriter writer, WorkloadKey value, JsonSeriali
public override WorkloadKey ReadJson(JsonReader reader, Type objectType, WorkloadKey existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
if (hasExistingValue)
throw new NotSupportedException();

return new((string)reader.Value);
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
Comment on lines +17 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve exception message for WorkloadKeyContactConverter.

The InvalidOperationException thrown when reader.Value is not a string lacks a descriptive message. Providing a message can help with debugging.

-    throw new InvalidOperationException();
+    throw new InvalidOperationException("Expected a string value for WorkloadKey.");
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
if (reader.Value is not string str)
throw new InvalidOperationException("Expected a string value for WorkloadKey.");
return new(str);

}
}
public class PackageKeyContactConverter : JsonConverter<PackageKey>
Expand All @@ -28,9 +27,9 @@ public override void WriteJson(JsonWriter writer, PackageKey value, JsonSerializ
public override PackageKey ReadJson(JsonReader reader, Type objectType, PackageKey existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
if (hasExistingValue)
throw new NotSupportedException();
return new((string)reader.Value);
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
Comment on lines +30 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve exception message for PackageKeyContactConverter.

The InvalidOperationException should include a descriptive message to aid in debugging.

-    throw new InvalidOperationException();
+    throw new InvalidOperationException("Expected a string value for PackageKey.");
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
if (reader.Value is not string str)
throw new InvalidOperationException("Expected a string value for PackageKey.");
return new(str);

}
}
public class PlatformKeyContactConverter : JsonConverter<PlatformKey>
Expand All @@ -41,9 +40,9 @@ public override void WriteJson(JsonWriter writer, PlatformKey value, JsonSeriali
public override PlatformKey ReadJson(JsonReader reader, Type objectType, PlatformKey existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
if (hasExistingValue)
throw new NotSupportedException();
return new((string)reader.Value);
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
Comment on lines +43 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve exception message for PlatformKeyContactConverter.

The InvalidOperationException should include a descriptive message to aid in debugging.

-    throw new InvalidOperationException();
+    throw new InvalidOperationException("Expected a string value for PlatformKey.");
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
if (reader.Value is not string str)
throw new InvalidOperationException("Expected a string value for PlatformKey.");
return new(str);

}
}
public class PackageKindKeyContactConverter : JsonConverter<PackageKindKey>
Expand All @@ -54,9 +53,9 @@ public override void WriteJson(JsonWriter writer, PackageKindKey value, JsonSeri
public override PackageKindKey ReadJson(JsonReader reader, Type objectType, PackageKindKey existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
if (hasExistingValue)
throw new NotSupportedException();
return new((string)reader.Value);
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
Comment on lines +56 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve exception message for PackageKindKeyContactConverter.

The InvalidOperationException should include a descriptive message to aid in debugging.

-    throw new InvalidOperationException();
+    throw new InvalidOperationException("Expected a string value for PackageKindKey.");
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (reader.Value is not string str)
throw new InvalidOperationException();
return new(str);
if (reader.Value is not string str)
throw new InvalidOperationException("Expected a string value for PackageKindKey.");
return new(str);

}
}
public class WorkloadPackageBaseConverter : JsonConverter<List<IWorkloadPackageBase>>
Expand Down Expand Up @@ -123,6 +122,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

foreach (var property in jsonObject.Properties())
{
property.Value["name"] = property.Name;
var workload = property.Value.ToObject<Workload>(serializer);
workloads[new WorkloadKey(property.Name)] = workload;
}
Expand Down Expand Up @@ -179,6 +179,40 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
}
}

public class DictionaryWithPackageKeyConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
=> typeof(Dictionary<PackageKey, T>).IsAssignableFrom(objectType);

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var packages = new Dictionary<PackageKey, T>();
var jsonObject = JObject.Load(reader);

foreach (var property in jsonObject.Properties())
{
var package = property.Value.ToObject<T>(serializer);
packages[new PackageKey(property.Name)] = package;
}

return packages;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var packages = (Dictionary<PackageKey, WorkloadPackage>)value;
writer.WriteStartObject();

foreach (var kvp in packages)
{
writer.WritePropertyName(kvp.Key.key);
serializer.Serialize(writer, kvp.Value);
}

writer.WriteEndObject();
}
}

public class WorkloadPackageConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
Expand All @@ -191,6 +225,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

foreach (var property in jsonObject.Properties())
{
property.Value["name"] = property.Name;
var package = property.Value.ToObject<WorkloadPackage>(serializer);
packages[new PackageKey(property.Name)] = package;
}
Expand Down
9 changes: 7 additions & 2 deletions lib/projectsystem/Workload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ namespace vein.project;
using NuGet.Versioning;


public record Workload([field: JsonIgnore] WorkloadKey name)
public record Workload
{
public required WorkloadKey name { get; init; }
public required List<PlatformKey> Platforms { get; init; } = new();
public required List<PackageKey> Packages { get; init; } = new();
public required string Description { get; init; }
}

public record WorkloadPackage([field: JsonIgnore] PackageKey name)
public record WorkloadPackage
{
public required PackageKey name { get; init; }
public required PackageKindKey Kind { get; init; }
[JsonConverter(typeof(DictionaryAliasesConverter))]
public required Dictionary<PlatformKey, string> Aliases { get; init; } = new();
Expand Down Expand Up @@ -131,6 +133,9 @@ public static PlatformKey GetCurrentPlatform()
[JsonConverter(typeof(PackageKeyContactConverter))]
public readonly record struct PackageKey(string key)
{
public static PackageKey VeinCompilerPackage = new("vein-compiler-package");
public static PackageKey VeinRuntimePackage = new("vein-runtime-package");

public override string ToString() => key;
}

Expand Down
16 changes: 16 additions & 0 deletions lib/vein.cli.core/RawConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,20 @@ public static IAnsiConsole Create() =>
UseDefaultEnrichers = false,
},
});

public static IAnsiConsole CreateForkConsole() =>
AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.Yes,
ColorSystem = (ColorSystemSupport)ColorSystem.TrueColor,
Out = new ForkConsole(Console.Out),
Interactive = InteractionSupport.Yes
});
}

public class ForkConsole(TextWriter writer) : AnsiConsoleOutput(writer)
{
public override bool IsTerminal => true;
public override int Width => int.Parse(Environment.GetEnvironmentVariable("FORK_CONSOLE_W")!);
public override int Height => int.Parse(Environment.GetEnvironmentVariable("FORK_CONSOLE_H")!);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ namespace vein;

public class ShardStorage : IShardStorage
{
public static readonly DirectoryInfo RootFolder =
public static readonly DirectoryInfo VeinRootFolder =
new(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".vein"));

public static readonly DirectoryInfo ShardRootFolder =
new(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".vein",
"shards"));

Expand All @@ -27,7 +30,7 @@ public class ShardStorage : IShardStorage

private void EnsureDefaultDirectory()
{
if (!RootFolder.Exists) RootFolder.Create();
if (!ShardRootFolder.Exists) ShardRootFolder.Create();
if (!RootFolderWorkloads.Exists) RootFolderWorkloads.Create();
if (!RootFolderNugets.Exists) RootFolderNugets.Create();
}
Expand Down Expand Up @@ -74,16 +77,16 @@ public DirectoryInfo GetPackageSpace(NuspecReader package)
=> ToNugetFolder(package.GetId(), package.GetVersion());

public DirectoryInfo GetPackageSpace(string name, NuGetVersion version)
=> RootFolder
=> ShardRootFolder
.SubDirectory(name)
.SubDirectory(version.ToNormalizedString());

public void Prune() =>
RootFolder.EnumerateFiles("*.*", SearchOption.AllDirectories)
ShardRootFolder.EnumerateFiles("*.*", SearchOption.AllDirectories)
.Pipe(x => x.Delete());

public List<NuGetVersion> GetAvailableVersions(string name) =>
RootFolder
ShardRootFolder
.SubDirectory(name)
.EnumerateDirectories()
.Where(x => NuGetVersion.TryParse(x.Name, out _))
Expand Down
122 changes: 122 additions & 0 deletions lib/vein.cli.core/di/SpectreConsoleHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
namespace vein.cli;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Spectre.Console.Cli;
using ITypeResolver = Spectre.Console.Cli.ITypeResolver;

public static class SpectreConsoleHostBuilderExtensions
{
public static IHostBuilder UseSpectreConsole(this IHostBuilder builder, Action<IConfigurator> configureCommandApp)
{
builder = builder ?? throw new ArgumentNullException(nameof(builder));

builder.ConfigureServices((_, collection) =>
{
var command = new CommandApp(new TypeRegistrar(collection));
command.Configure(configureCommandApp);
collection.AddSingleton<ICommandApp>(command);
collection.AddHostedService<SpectreConsoleWorker>();
}
);

return builder;
}

public static IHostBuilder UseSpectreConsole<TDefaultCommand>(this IHostBuilder builder,
Action<IConfigurator>? configureCommandApp = null)
where TDefaultCommand : class, ICommand
{
builder = builder ?? throw new ArgumentNullException(nameof(builder));

builder.ConfigureServices((_, collection) =>
{
var command = new CommandApp<TDefaultCommand>(new TypeRegistrar(collection));
if (configureCommandApp != null)
{
command.Configure(configureCommandApp);
}

collection.AddSingleton<ICommandApp>(command);
collection.AddHostedService<SpectreConsoleWorker>();
}
);

return builder;
}
}
public sealed class TypeResolver(IServiceProvider serviceProvider) : ITypeResolver, IDisposable
{
private readonly IServiceProvider _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));

public void Dispose()
{
if (_serviceProvider is IDisposable disposable)
{
disposable.Dispose();
}
}

public object? Resolve(Type? type)
{
if (type == null)
{
return null;
}

return _serviceProvider.GetService(type) ?? Activator.CreateInstance(type);
}
}
public sealed class TypeRegistrar(IServiceCollection builder) : ITypeRegistrar
{
public ITypeResolver Build() => new TypeResolver(builder.BuildServiceProvider());

public void Register(Type service, Type implementation) => builder.AddSingleton(service, implementation);

public void RegisterInstance(Type service, object implementation) => builder.AddSingleton(service, implementation);

public void RegisterLazy(Type service, Func<object> func)
{
if (func is null)
throw new ArgumentNullException(nameof(func));

builder.AddSingleton(service, _ => func());
}
}

public class SpectreConsoleWorker(
ILogger<SpectreConsoleWorker> logger,
ICommandApp commandApp,
IHostApplicationLifetime hostLifetime)
: IHostedService
{
private int _exitCode;

public Task StartAsync(CancellationToken cancellationToken) =>
Task.Factory.StartNew(async () => {
try
{
var args = GetArgs();
await Task.Delay(100, cancellationToken);
_exitCode = await commandApp.RunAsync(args);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred");
_exitCode = 1;
}
finally
{
hostLifetime.StopApplication();
}
}, cancellationToken);

public Task StopAsync(CancellationToken cancellationToken)
{
Environment.ExitCode = _exitCode;
return Task.CompletedTask;
}

private static string[] GetArgs() => Environment.GetCommandLineArgs().Skip(1).Where(x => !x.StartsWith("+")).ToArray();
}
4 changes: 4 additions & 0 deletions lib/vein.cli.core/vein.cli.core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<PackageReference Include="DeviceId.Windows.Wmi" Version="6.6.0" />
<PackageReference Include="Flurl.Http" Version="4.0.2" />
<PackageReference Include="Flurl.Http.Newtonsoft" Version="0.9.1" />
<PackageReference Include="MessagePipe.Interprocess" Version="1.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="NuGet.Protocol" Version="6.11.0" />
<PackageReference Include="Tomlyn.Extensions.Configuration" Version="1.0.6" />
<PackageReference Include="xunit.assert" Version="2.8.0" />
</ItemGroup>
<ItemGroup>
Expand Down
44 changes: 44 additions & 0 deletions lib/vein.compiler.shared/CompileSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace vein.compiler.shared;

using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Cli;

[ExcludeFromCodeCoverage]
public class CompileSettings : CommandSettings, IProjectSettingProvider
{
[Description("Path to vproj file")]
[CommandArgument(0, "[PROJECT]")]
public string Project { get; set; }

[Description("Display exported types table")]
[CommandOption("--print-result-types")]
public bool PrintResultType { get; set; }

[Description("Display exported types table")]
[CommandOption("--disable-optimization|-O")]
public bool DisableOptimization { get; set; }

[Description("Compile into single file")]
[CommandOption("--single-file|-s")]
public bool HasSingleFile { get; set; }

[Description("Wait to attach debbugger (ONLY DEBUG COMPILER)")]
[CommandOption("--sys-debugger")]
public bool IsNeedDebuggerAttach { get; set; }
[Description("Enable stacktrace printing when error.")]
[CommandOption("--sys-stack-trace")]
public bool DisplayStacktraceGenerator { get; set; }

[Description("Generate shard package.")]
[CommandOption("--gen-shard")]
public bool GeneratePackageOutput { get; set; }

[Description("Ignore cache.")]
[CommandOption("--ignore-cache")]
public bool IgnoreCache { get; set; }

[Description("Override generated version")]
[CommandOption("--override-version", IsHidden = true)]
public string OverrideVersion { get; set; }
}
Loading
Loading