114 lines
3.2 KiB
C#
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);
|