diff --git a/example/.gitignore b/example/.gitignore index b65ea6d..54f46fc 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,2 +1,2 @@ -bin -bin-int +build +out \ No newline at end of file diff --git a/example/makefile b/example/makefile index 81bd9f9..57fcdcc 100644 --- a/example/makefile +++ b/example/makefile @@ -1,17 +1,18 @@ +CC = clang NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc -out: $(NUBC) main.nub - $(NUBC) main.nub +out: build/out.a + $(CC) -o out build/out.a + +build/out.a: $(NUBC) src/main.nub + $(NUBC) src/main.nub $(NUBC): dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj run: out - out + ./out clean: + @rm -r build 2>/dev/null || true @rm out 2>/dev/null || true - @rm out.a 2>/dev/null || true - @find . -name "*.o" -type f -delete - @find . -name "*.s" -type f -delete - @find . -name "*.ssa" -type f -delete diff --git a/example/src/main.nub b/example/src/main.nub new file mode 100644 index 0000000..8bf7d2e --- /dev/null +++ b/example/src/main.nub @@ -0,0 +1,8 @@ +// c +extern func puts(text: cstring) + +func main(args: []cstring): i64 +{ + puts("Hello, World") + return 0 +} diff --git a/src/compiler/NubLang.CLI/GCC.cs b/src/compiler/NubLang.CLI/GCC.cs index 05b0eae..941ce17 100644 --- a/src/compiler/NubLang.CLI/GCC.cs +++ b/src/compiler/NubLang.CLI/GCC.cs @@ -27,28 +27,4 @@ public static class GCC return process.ExitCode == 0; } - - public static async Task Link(string executablePath, params IEnumerable objectFiles) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("gcc", ["-nostartfiles", "-o", executablePath, ..objectFiles]) - { - UseShellExecute = false, - 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; - } } \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/Options.cs b/src/compiler/NubLang.CLI/Options.cs index ac3c44e..acef2bc 100644 --- a/src/compiler/NubLang.CLI/Options.cs +++ b/src/compiler/NubLang.CLI/Options.cs @@ -5,9 +5,5 @@ namespace NubLang.CLI; public class Options { public string? OutputPath { get; set; } - public bool Link { get; set; } = true; public List Files { get; } = []; - public bool EmitSsa { get; set; } = false; - public bool EmitAsm { get; set; } = false; - public bool EmitObj { get; set; } = false; } \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/Program.cs b/src/compiler/NubLang.CLI/Program.cs index 7bf8359..2b9f706 100644 --- a/src/compiler/NubLang.CLI/Program.cs +++ b/src/compiler/NubLang.CLI/Program.cs @@ -15,37 +15,24 @@ var options = new Options(); for (var i = 0; i < args.Length; i++) { var arg = args[i]; - - if (arg == "-o") + switch (arg) { - i++; - if (i >= args.Length - 1) + case "-o": { - Console.Error.WriteLine($"{arg} must be followed by a value"); - return 1; - } + ++i; + if (i >= args.Length) + { + return 1; + } - options.OutputPath = args[i]; - } - else if (arg == "-c") - { - options.Link = false; - } - else if (arg == "-ssa") - { - options.EmitSsa = true; - } - else if (arg == "-asm") - { - options.EmitAsm = true; - } - else if (arg == "-obj") - { - options.EmitObj = true; - } - else - { - options.Files.Add(new SourceFile(arg)); + options.OutputPath = args[i]; + break; + } + default: + { + options.Files.Add(new SourceFile(arg)); + break; + } } } @@ -104,35 +91,31 @@ var objectFiles = new List(); for (var i = 0; i < typedSyntaxTrees.Count; i++) { var syntaxTree = typedSyntaxTrees[i]; - var outFileName = Path.ChangeExtension(options.Files[i].Path, null); + var outFileName = Path.Combine("build", "code", Path.ChangeExtension(options.Files[i].Path, null)); + + var outFileDir = Path.GetDirectoryName(outFileName); + if (!string.IsNullOrEmpty(outFileDir)) + { + Directory.CreateDirectory(outFileDir); + } var generator = new QBEGenerator(syntaxTree, typedDefinitionTable); var ssa = generator.Emit(); - var ssaFilePath = $"{outFileName}.ssa"; + var ssaFilePath = Path.ChangeExtension(outFileName, "ssa"); File.WriteAllText(ssaFilePath, ssa); - var asmFilePath = $"{outFileName}.s"; + var asmFilePath = Path.ChangeExtension(outFileName, "s"); var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath); - if (!options.EmitSsa) - { - File.Delete(ssaFilePath); - } - if (!qbeSuccess) { return 1; } - var objFilePath = $"{outFileName}.o"; + var objFilePath = Path.ChangeExtension(outFileName, "o"); var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath); - if (!options.EmitAsm) - { - File.Delete(asmFilePath); - } - if (!asmSuccess) { return 1; @@ -141,50 +124,43 @@ for (var i = 0; i < typedSyntaxTrees.Count; i++) objectFiles.Add(objFilePath); } -var runtimeFilePath = "libruntime_x64.a"; +const string runtimeName = "libruntime_x64.a"; var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); -var runtime = resources.First(r => r.EndsWith(runtimeFilePath)); +var runtime = resources.First(r => r.EndsWith(runtimeName)); await using var reader = Assembly.GetExecutingAssembly().GetManifestResourceStream(runtime); if (reader == null) { - Console.Error.WriteLine($"Cannot open stream to '{runtimeFilePath}'"); + Console.Error.WriteLine($"Cannot open stream to '{runtimeName}'"); return 1; } -await using var writer = new FileStream(runtimeFilePath, FileMode.Create); +var runtimePath = Path.Combine("build", "runtime", runtimeName); +var runtimeDir = Path.GetDirectoryName(runtimePath); +if (!string.IsNullOrEmpty(runtimeDir)) +{ + Directory.CreateDirectory(runtimeDir); +} + +await using var writer = new FileStream(runtimePath, FileMode.Create); reader.CopyTo(writer); -objectFiles.Add(runtimeFilePath); +objectFiles.Add(runtimePath); -if (options.Link) +var outPath = options.OutputPath ?? Path.Combine("build", "out.a"); +var outDir = Path.GetDirectoryName(outPath); +if (!string.IsNullOrEmpty(outDir)) { - var outPath = options.OutputPath ?? "out"; - var linkResult = await GCC.Link(outPath, objectFiles); - if (!linkResult) - { - return 1; - } -} -else -{ - var outPath = options.OutputPath ?? "out.a"; - var archiveResult = await Archive.Invoke(outPath, objectFiles); - if (!archiveResult) - { - return 1; - } + Directory.CreateDirectory(outDir); } -if (!options.EmitObj) +var archiveResult = await Archive.Invoke(outPath, objectFiles); +if (!archiveResult) { - foreach (var objectFile in objectFiles) - { - File.Delete(objectFile); - } + return 1; } return 0; \ No newline at end of file