From edbab69c00fab2da864e3c4d6e9c2fadcf4db30c Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 29 Jun 2025 17:40:17 +0200 Subject: [PATCH] ... --- example/makefile | 4 +-- src/compiler/CLI/Archive.cs | 2 +- src/compiler/CLI/GCC.cs | 33 ++++++++++++++++++- src/compiler/CLI/Options.cs | 3 ++ src/compiler/CLI/Program.cs | 65 ++++++++++++++++++++++++++----------- 5 files changed, 83 insertions(+), 24 deletions(-) diff --git a/example/makefile b/example/makefile index 2762a2a..328e6d5 100644 --- a/example/makefile +++ b/example/makefile @@ -2,9 +2,7 @@ CFLAGS = -g bin/out: src/main.nub src/c.nub dotnet build ../src/compiler/CLI/CLI.csproj -c Release - ../src/compiler/CLI/bin/Debug/net9.0/nubc src/main.nub src/c.nub - mkdir -p bin - gcc $(CFLAGS) -o bin/out bin-int/out/out.a + ../src/compiler/CLI/bin/Release/net9.0/nubc src/main.nub src/c.nub run: bin/out bin/out diff --git a/src/compiler/CLI/Archive.cs b/src/compiler/CLI/Archive.cs index 989bbdf..872ffdb 100644 --- a/src/compiler/CLI/Archive.cs +++ b/src/compiler/CLI/Archive.cs @@ -4,7 +4,7 @@ namespace CLI; public static class Archive { - public static async Task Invoke(string fileName, IEnumerable objectFiles) + public static async Task Invoke(string fileName, params IEnumerable objectFiles) { using var process = new Process(); process.StartInfo = new ProcessStartInfo("ar", ["rcs", fileName, ..objectFiles]) diff --git a/src/compiler/CLI/GCC.cs b/src/compiler/CLI/GCC.cs index caa69d1..c94c5f1 100644 --- a/src/compiler/CLI/GCC.cs +++ b/src/compiler/CLI/GCC.cs @@ -13,7 +13,7 @@ public static class GCC } using var process = new Process(); - process.StartInfo = new ProcessStartInfo("gcc", ["-g", "-xassembler", "-c", "-o", objPath, "-"]) + process.StartInfo = new ProcessStartInfo("gcc", ["-xassembler", "-c", "-o", objPath, "-"]) { UseShellExecute = false, RedirectStandardInput = true, @@ -37,4 +37,35 @@ public static class GCC return process.ExitCode == 0; } + + public static async Task Link(string executablePath, params IEnumerable objectFiles) + { + var dir = Path.GetDirectoryName(executablePath); + if (dir != null) + { + Directory.CreateDirectory(dir); + } + + using var process = new Process(); + process.StartInfo = new ProcessStartInfo("gcc", ["-nostartfiles", "-o", executablePath, ..objectFiles]) + { + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + + process.Start(); + + await process.WaitForExitAsync(); + + var errors = await process.StandardError.ReadToEndAsync(); + if (!string.IsNullOrWhiteSpace(errors)) + { + await Console.Error.WriteLineAsync("gcc error when linking:\n" + errors); + } + + return process.ExitCode == 0; + } } diff --git a/src/compiler/CLI/Options.cs b/src/compiler/CLI/Options.cs index 947ed50..81e0612 100644 --- a/src/compiler/CLI/Options.cs +++ b/src/compiler/CLI/Options.cs @@ -3,4 +3,7 @@ namespace CLI; public class Options { public string? CustomRuntime { get; set; } + public string? OutputPath { get; set; } + public bool Link { get; set; } = true; + public List Files { get; } = []; } \ No newline at end of file diff --git a/src/compiler/CLI/Program.cs b/src/compiler/CLI/Program.cs index 44d1be2..56b20b8 100644 --- a/src/compiler/CLI/Program.cs +++ b/src/compiler/CLI/Program.cs @@ -8,28 +8,26 @@ using Syntax.Tokenization; using Syntax.Typing; using Binder = Syntax.Typing.Binder; +const string BIN_DIR = "bin"; const string INT_DIR = "bin-int"; -var BUILTIN_DIR = Path.Join(INT_DIR, "builtin"); -var OBJECT_DIR = Path.Join(INT_DIR, "obj"); -var OUT_DIR = Path.Join(INT_DIR, "out"); +var INT_BUILTIN_DIR = Path.Join(INT_DIR, "builtin"); +var INT_OBJECT_DIR = Path.Join(INT_DIR, "obj"); if (Directory.Exists(INT_DIR)) { Directory.Delete(INT_DIR, true); } -Directory.CreateDirectory(BUILTIN_DIR); -Directory.CreateDirectory(OBJECT_DIR); -Directory.CreateDirectory(OUT_DIR); +Directory.CreateDirectory(BIN_DIR); +Directory.CreateDirectory(INT_BUILTIN_DIR); +Directory.CreateDirectory(INT_OBJECT_DIR); var options = new Options(); -var files = new List(); - for (var i = 0; i < args.Length; i++) { var arg = args[i]; - if (arg.StartsWith("-r") || arg.StartsWith("--runtime")) + if (arg == "-r") { i++; if (i >= args.Length - 1) @@ -40,16 +38,32 @@ for (var i = 0; i < args.Length; i++) options.CustomRuntime = args[i]; } + else if (arg == "-o") + { + i++; + if (i >= args.Length - 1) + { + Console.Error.WriteLine($"{arg} must be followed by a value"); + return 1; + } + + options.OutputPath = args[i]; + } + else if (arg == "-c") + { + i++; + options.Link = false; + } else { - files.Add(arg); + options.Files.Add(arg); } } var diagnostics = new List(); var syntaxTrees = new Dictionary(); -foreach (var file in files) +foreach (var file in options.Files) { if (!File.Exists(file)) { @@ -58,7 +72,7 @@ foreach (var file in files) } } -foreach (var file in files) +foreach (var file in options.Files) { var content = File.ReadAllText(file); var sourceText = new SourceText(file, content); @@ -100,7 +114,7 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro var objectFiles = new List(); -foreach (var file in files) +foreach (var file in options.Files) { var ssa = QBEGenerator.Emit(boundSyntaxTrees[file], boundDefinitionTable); var asm = await QBE.Invoke(ssa); @@ -109,7 +123,7 @@ foreach (var file in files) return 1; } - var fileName = Path.Join(OBJECT_DIR, $"{HexString.CreateUnique(8)}_{Path.GetFileNameWithoutExtension(file)}.o"); + var fileName = Path.Join(INT_OBJECT_DIR, $"{HexString.CreateUnique(8)}_{Path.GetFileNameWithoutExtension(file)}.o"); var asmSuccess = await GCC.Assemble(asm, fileName); if (!asmSuccess) { @@ -142,10 +156,23 @@ else objectFiles.Add(options.CustomRuntime); } -var archiveResult = await Archive.Invoke(Path.Join(OUT_DIR, "out.a"), objectFiles); -if (!archiveResult) +if (options.Link) { - return 1; + var outPath = options.OutputPath ?? Path.Join(BIN_DIR, "out"); + var linkResult = await GCC.Link(outPath, objectFiles); + if (!linkResult) + { + return 1; + } +} +else +{ + var outPath = options.OutputPath ?? Path.Join(BIN_DIR, "out.a"); + var archiveResult = await Archive.Invoke(outPath, objectFiles); + if (!archiveResult) + { + return 1; + } } return 0; @@ -164,7 +191,7 @@ async Task CreateBuiltinRuntime() throw new RuntimeCreationException($"Cannot open stream to '{RUNTIME_NAME}'"); } - var runtimePath = Path.Combine(BUILTIN_DIR, RUNTIME_NAME); + var runtimePath = Path.Combine(INT_BUILTIN_DIR, RUNTIME_NAME); await using var writer = new FileStream(runtimePath, FileMode.Create); reader.CopyTo(writer); @@ -172,4 +199,4 @@ async Task CreateBuiltinRuntime() return runtimePath; } -class RuntimeCreationException(string message) : Exception(message); \ No newline at end of file +internal class RuntimeCreationException(string message) : Exception(message); \ No newline at end of file