quorate/packages/fsharp-runner/hostexec/GnuProxy.cs

114 lines
3.2 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
namespace Quorate.HostExec;
[SuppressMessage("Maintainability", "CA1515", Justification = "Loaded by F# actions.")]
public enum GnuTool
{
C,
Cpp,
Gcc,
Gxx
}
[SuppressMessage("Maintainability", "CA1515", Justification = "Loaded by F# actions.")]
public sealed class ToolRoot
{
private ToolRoot(string path)
{
Path = path;
}
public string Path { get; }
public static ToolRoot Open(string path)
{
ArgumentException.ThrowIfNullOrWhiteSpace(path);
if (!System.IO.Path.IsPathFullyQualified(path))
{
throw new ArgumentException("tool root must be absolute", nameof(path));
}
var fullPath = System.IO.Path.GetFullPath(path);
if (!Directory.Exists(fullPath))
{
throw new DirectoryNotFoundException(fullPath);
}
return new ToolRoot(fullPath);
}
internal string Resolve(string relative)
{
if (System.IO.Path.IsPathFullyQualified(relative))
{
throw new ArgumentException("tool path must be relative", nameof(relative));
}
var path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, relative));
var prefix = Path + System.IO.Path.DirectorySeparatorChar;
if (!path.StartsWith(prefix, StringComparison.Ordinal))
{
throw new ArgumentException("tool path escapes its root", nameof(relative));
}
return path;
}
}
[SuppressMessage("Maintainability", "CA1515", Justification = "Loaded by F# actions.")]
public static class GnuProxy
{
private const string Launcher = "#!/usr/share/dotnet/dotnet /opt/fsi/packages/HostExec.dll\n";
private static readonly JsonSerializerOptions Json = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public static string Install(ToolRoot root, GnuTool tool)
{
ArgumentNullException.ThrowIfNull(root);
var name = Name(tool);
var relative = $"usr/bin/{name}";
var executable = root.Resolve(relative);
if (!File.Exists(executable))
{
throw new FileNotFoundException("GNU tool is unavailable", executable);
}
var target = root.Resolve($"usr/local/bin/{name}");
Directory.CreateDirectory(System.IO.Path.GetDirectoryName(target)!);
File.WriteAllText(target, Launcher);
File.SetUnixFileMode(
target,
UnixFileMode.UserRead
| UnixFileMode.UserWrite
| UnixFileMode.UserExecute
| UnixFileMode.GroupRead
| UnixFileMode.GroupExecute
| UnixFileMode.OtherRead
| UnixFileMode.OtherExecute
);
File.WriteAllText($"{target}.json", JsonSerializer.Serialize(new ProxyFile(1, "gnu", relative), Json));
return target;
}
private static string Name(GnuTool tool) =>
tool switch
{
GnuTool.C => "cc",
GnuTool.Cpp => "c++",
GnuTool.Gcc => "gcc",
GnuTool.Gxx => "g++",
_ => throw new ArgumentOutOfRangeException(nameof(tool))
};
}
internal sealed record ProxyFile(int Version, string Kind, string File);