...
This commit is contained in:
19
build.sh
19
build.sh
@@ -1,19 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
dotnet run --project src/lang/Nub.Lang.CLI/ example
|
||||||
|
|
||||||
dotnet build src/lang/Nub.Lang.CLI
|
|
||||||
|
|
||||||
mkdir -p bin-int bin
|
|
||||||
rm -rf bin-int/* bin/*
|
|
||||||
|
|
||||||
nub example
|
|
||||||
|
|
||||||
find bin-int -name '*.s' | while read -r file; do
|
|
||||||
as "$file" -o "bin-int/$(basename "${file}" .s).o"
|
|
||||||
done
|
|
||||||
|
|
||||||
find src/runtime -name '*.s' | while read -r file; do
|
|
||||||
as "$file" -o "bin-int/$(basename "${file}" .s).o"
|
|
||||||
done
|
|
||||||
|
|
||||||
gcc -nostartfiles -o bin/out bin-int/*.o
|
|
||||||
@@ -6,6 +6,9 @@ using Nub.Lang.Frontend.Lexing;
|
|||||||
using Nub.Lang.Frontend.Parsing;
|
using Nub.Lang.Frontend.Parsing;
|
||||||
using Nub.Lang.Frontend.Typing;
|
using Nub.Lang.Frontend.Typing;
|
||||||
|
|
||||||
|
const string BIN_DIR = "bin";
|
||||||
|
const string BIN_INT_DIR = "bin-int";
|
||||||
|
|
||||||
if (args.Length != 1)
|
if (args.Length != 1)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Usage: nub <input-dir>");
|
Console.Error.WriteLine("Usage: nub <input-dir>");
|
||||||
@@ -21,17 +24,29 @@ if (!Directory.Exists(srcDir))
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = false;
|
Directory.CreateDirectory(BIN_INT_DIR);
|
||||||
|
Directory.CreateDirectory(BIN_DIR);
|
||||||
|
|
||||||
Dictionary<CompilationUnit, SourceText> sourceTexts = [];
|
foreach (var file in Directory.GetFiles(BIN_INT_DIR))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in Directory.GetFiles(BIN_DIR))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
var error = false;
|
||||||
List<CompilationUnit> compilationUnits = [];
|
List<CompilationUnit> compilationUnits = [];
|
||||||
|
Dictionary<CompilationUnit, SourceText> sourceTexts = [];
|
||||||
|
List<string> objectFiles = [];
|
||||||
|
|
||||||
foreach (var file in Directory.EnumerateFiles(srcDir, "*.nub", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(srcDir, "*.nub", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
var content = File.ReadAllText(file);
|
var content = File.ReadAllText(file);
|
||||||
|
|
||||||
var sourceText = new SourceText(file, content);
|
var sourceText = new SourceText(file, content);
|
||||||
|
|
||||||
var tokenizeResult = Lexer.Tokenize(sourceText);
|
var tokenizeResult = Lexer.Tokenize(sourceText);
|
||||||
tokenizeResult.PrintAllDiagnostics();
|
tokenizeResult.PrintAllDiagnostics();
|
||||||
error = error || tokenizeResult.HasErrors;
|
error = error || tokenizeResult.HasErrors;
|
||||||
@@ -72,11 +87,76 @@ foreach (var compilationUnit in compilationUnits)
|
|||||||
|
|
||||||
var sourceFileName = Path.GetFileNameWithoutExtension(sourceTexts[compilationUnit].Path);
|
var sourceFileName = Path.GetFileNameWithoutExtension(sourceTexts[compilationUnit].Path);
|
||||||
var sourceHash = Math.Abs(sourceTexts[compilationUnit].Path.GetHashCode()).ToString("x8");
|
var sourceHash = Math.Abs(sourceTexts[compilationUnit].Path.GetHashCode()).ToString("x8");
|
||||||
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString("x");
|
var baseOutputName = $"{sourceFileName}_{sourceHash}";
|
||||||
var baseOutputName = $"{sourceFileName}_{sourceHash}_{timestamp}";
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var assemblyCode = await InvokeQBE(ssaCode);
|
||||||
|
|
||||||
|
var asmPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.s");
|
||||||
|
await File.WriteAllTextAsync(asmPath, assemblyCode);
|
||||||
|
Console.Out.WriteLine($"Generated: {asmPath}");
|
||||||
|
|
||||||
|
var objPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.o");
|
||||||
|
await InvokeAssembler(asmPath, objPath);
|
||||||
|
objectFiles.Add(objPath);
|
||||||
|
Console.Out.WriteLine($"Assembled: {objPath}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error processing {sourceTexts[compilationUnit].Path}: {ex.Message}");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string RUNTIME_DIR = "src/runtime";
|
||||||
|
if (Directory.Exists(RUNTIME_DIR))
|
||||||
|
{
|
||||||
|
foreach (var runtimeFile in Directory.EnumerateFiles(RUNTIME_DIR, "*.s", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var runtimeFileName = Path.GetFileNameWithoutExtension(runtimeFile);
|
||||||
|
var objPath = Path.Combine(BIN_INT_DIR, $"runtime_{runtimeFileName}.o");
|
||||||
|
|
||||||
|
await InvokeAssembler(runtimeFile, objPath);
|
||||||
|
objectFiles.Add(objPath);
|
||||||
|
Console.Out.WriteLine($"Runtime assembled: {objPath}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error assembling runtime file {runtimeFile}: {ex.Message}");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var outputPath = Path.Combine(BIN_DIR, "out");
|
||||||
|
await InvokeLinker(objectFiles, outputPath);
|
||||||
|
Console.Out.WriteLine($"Linked executable: {outputPath}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error linking: {ex.Message}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
static async Task<string> InvokeQBE(string ssa)
|
||||||
|
{
|
||||||
using var qbeProcess = new Process();
|
using var qbeProcess = new Process();
|
||||||
qbeProcess.StartInfo = new ProcessStartInfo
|
qbeProcess.StartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
@@ -90,7 +170,7 @@ foreach (var compilationUnit in compilationUnits)
|
|||||||
|
|
||||||
qbeProcess.Start();
|
qbeProcess.Start();
|
||||||
|
|
||||||
await qbeProcess.StandardInput.WriteAsync(ssaCode);
|
await qbeProcess.StandardInput.WriteAsync(ssa);
|
||||||
qbeProcess.StandardInput.Close();
|
qbeProcess.StandardInput.Close();
|
||||||
|
|
||||||
var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync();
|
var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync();
|
||||||
@@ -100,20 +180,56 @@ foreach (var compilationUnit in compilationUnits)
|
|||||||
|
|
||||||
if (qbeProcess.ExitCode != 0)
|
if (qbeProcess.ExitCode != 0)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine($"QBE failed for {sourceTexts[compilationUnit].Path}:");
|
throw new Exception($"QBE errors:\n{qbeErrors}");
|
||||||
Console.Error.WriteLine(qbeErrors);
|
|
||||||
error = true;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var asmPath = Path.Combine("bin-int", $"{baseOutputName}.s");
|
return assemblyCode;
|
||||||
await File.WriteAllTextAsync(asmPath, assemblyCode);
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
static async Task InvokeAssembler(string asmPath, string objPath)
|
||||||
|
{
|
||||||
|
using var asProcess = new Process();
|
||||||
|
asProcess.StartInfo = new ProcessStartInfo("gcc", ["-c", asmPath, "-o", objPath])
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine($"Error invoking QBE for {sourceTexts[compilationUnit].Path}: {ex.Message}");
|
UseShellExecute = false,
|
||||||
error = true;
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
asProcess.Start();
|
||||||
|
|
||||||
|
var asOutput = await asProcess.StandardOutput.ReadToEndAsync();
|
||||||
|
var asErrors = await asProcess.StandardError.ReadToEndAsync();
|
||||||
|
|
||||||
|
await asProcess.WaitForExitAsync();
|
||||||
|
|
||||||
|
if (asProcess.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Assembler failed for {asmPath}:\n{asErrors}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return error ? 1 : 0;
|
static async Task InvokeLinker(List<string> objectFiles, string outputPath)
|
||||||
|
{
|
||||||
|
using var gccProcess = new Process();
|
||||||
|
gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-nostartfiles", "-o", outputPath, ..objectFiles])
|
||||||
|
{
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
gccProcess.Start();
|
||||||
|
|
||||||
|
var gccOutput = await gccProcess.StandardOutput.ReadToEndAsync();
|
||||||
|
var gccErrors = await gccProcess.StandardError.ReadToEndAsync();
|
||||||
|
|
||||||
|
await gccProcess.WaitForExitAsync();
|
||||||
|
|
||||||
|
if (gccProcess.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Linker failed:\n{gccErrors}");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user