c part ready to go
This commit is contained in:
6
CxxBuilder/.gitignore
vendored
Normal file
6
CxxBuilder/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
build
|
||||
recovery
|
||||
test
|
||||
BeefSpace_User.toml
|
||||
|
||||
dist/*
|
||||
29
CxxBuilder/BeefProj.toml
Normal file
29
CxxBuilder/BeefProj.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
FileVersion = 1
|
||||
|
||||
[Project]
|
||||
Name = "CxxBuilder"
|
||||
StartupObject = "CxxBuilder.Program"
|
||||
|
||||
[Configs.Debug.Win32]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Release.Win32]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Release.Win64]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Paranoid.Win32]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Paranoid.Win64]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Test.Win32]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
|
||||
[Configs.Test.Win64]
|
||||
TargetDirectory = "$(ProjectDir)/dist"
|
||||
5
CxxBuilder/BeefSpace.toml
Normal file
5
CxxBuilder/BeefSpace.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
FileVersion = 1
|
||||
Projects = {CxxBuilder = {Path = "."}}
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "CxxBuilder"
|
||||
137
CxxBuilder/src/FileMatcher.bf
Normal file
137
CxxBuilder/src/FileMatcher.bf
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CxxBuilder;
|
||||
|
||||
static class FileMatcher
|
||||
{
|
||||
public static void HandleMatches(Span<StringView> patterns, StringView directory, delegate void(StringView match) callback)
|
||||
{
|
||||
Runtime.Assert(Directory.Exists(directory), scope $"No such directory {directory}");
|
||||
void Dir(StringView dir)
|
||||
{
|
||||
for (let element in Directory.Enumerate(dir))
|
||||
{
|
||||
let path = element.GetFilePath(..scope .());
|
||||
path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
if (element.IsDirectory)
|
||||
{
|
||||
Dir(path);
|
||||
continue;
|
||||
}
|
||||
let relpath = Path.GetRelativePath(path, directory, ..scope .());
|
||||
for (let pattern in patterns)
|
||||
if (IsMatch(pattern, relpath))
|
||||
{
|
||||
callback(relpath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Dir(directory);
|
||||
}
|
||||
|
||||
public static bool IsMatch(StringView pattern, StringView path)
|
||||
{
|
||||
var pattern, path;
|
||||
|
||||
bool Matches(StringView pattern, char8 c)
|
||||
{
|
||||
if (pattern.IsEmpty) return false;
|
||||
|
||||
switch (pattern[0])
|
||||
{
|
||||
case '*':
|
||||
if (pattern.Length > 1 && pattern[1] == '*')
|
||||
return true;
|
||||
return !Path.IsDirectorySeparatorChar(c);
|
||||
case '?': return true;
|
||||
case '\\':
|
||||
Runtime.Assert(pattern.Length > 1, "Expected escape sequence");
|
||||
return c == pattern[1];
|
||||
case '/': return Path.IsDirectorySeparatorChar(c);
|
||||
case '[':
|
||||
int i = 0;
|
||||
char8 Next()
|
||||
{
|
||||
if (pattern.Length <= ++i)
|
||||
Runtime.FatalError("Character class not closed");
|
||||
return pattern[i];
|
||||
}
|
||||
bool negated = false;
|
||||
char8 current;
|
||||
switch (Next())
|
||||
{
|
||||
case '^', '!': negated = true; fallthrough;
|
||||
case '\\': current = Next();
|
||||
case ']': return false;
|
||||
default: current = _;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (c == current) return !negated;
|
||||
switch (Next())
|
||||
{
|
||||
case ']': return negated;
|
||||
case '-':
|
||||
let end = Next();
|
||||
if (c >= current && c <= end)
|
||||
return !negated;
|
||||
current = Next();
|
||||
case '\\': current = Next();
|
||||
default: current = _;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return c == _;
|
||||
}
|
||||
}
|
||||
|
||||
reduce: while (true)
|
||||
{
|
||||
if (path.IsEmpty) return true;
|
||||
if (pattern.IsEmpty) return false;
|
||||
if (!Matches(pattern, path[0])) return false;
|
||||
switch (pattern[0])
|
||||
{
|
||||
case '*':
|
||||
StringView lazy;
|
||||
if (pattern.Length > 1)
|
||||
{
|
||||
lazy = pattern;
|
||||
lazy.RemoveFromStart(pattern[1] == '*' ? 2 : 1);
|
||||
}
|
||||
else lazy = .();
|
||||
defer { pattern = lazy; }
|
||||
while (true)
|
||||
{
|
||||
if (IsMatch(lazy, path)) continue reduce;
|
||||
if (!Matches(pattern, path[0])) continue reduce;
|
||||
path.RemoveFromStart(1);
|
||||
if (path.IsEmpty) return lazy.IsEmpty;
|
||||
}
|
||||
case '[':
|
||||
bool escaped = false;
|
||||
while (true)
|
||||
{
|
||||
let c = pattern[0];
|
||||
pattern.RemoveFromStart(1);
|
||||
if (c == ']' && !escaped)
|
||||
{
|
||||
path.RemoveFromStart(1);
|
||||
continue reduce;
|
||||
}
|
||||
escaped = c == '\\';
|
||||
}
|
||||
case '\\':
|
||||
pattern.RemoveFromStart(2);
|
||||
path.RemoveFromStart(1);
|
||||
default:
|
||||
pattern.RemoveFromStart(1);
|
||||
path.RemoveFromStart(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
CxxBuilder/src/Program.bf
Normal file
134
CxxBuilder/src/Program.bf
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CxxBuilder;
|
||||
|
||||
static class Program
|
||||
{
|
||||
[CLink] static extern int32 system(char8*);
|
||||
|
||||
public static int Main(String[] args)
|
||||
{
|
||||
mixin PrintUsage()
|
||||
{
|
||||
Console.WriteLine("""
|
||||
Simple and fast build tool using clang and ninja.
|
||||
|
||||
Usage: $(Var CxxBuilderExe) <patterns...> -- src=<> target=<> builddir=<> output=<> [cflags=<>]
|
||||
Example: $(Var CxxBuilderExe) **.c vk_*.cpp -- "src=$(ProjectDir)/MyLib/src" target=$(TargetTriple) "builddir=$(BuildDir)" output=MyLib cflags=-Isome/dir
|
||||
|
||||
src - The source dir, all patterns are rooted here
|
||||
target - the llvm target triple passed to clang
|
||||
builddir - contains build artifacts and ninja files
|
||||
output - the name of the output object archive $builddir/$output.lib on windows and $builddir/$output.a elsewhere
|
||||
cflags - additional flags passed to clang
|
||||
""");
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringView src = null, target = null, builddir = null, output = null, cflags = null;
|
||||
var iter = args.GetEnumerator();
|
||||
skipPatterns: do
|
||||
{
|
||||
for (let arg in iter)
|
||||
if (arg == "--") break skipPatterns;
|
||||
PrintUsage!();
|
||||
}
|
||||
for (let arg in iter)
|
||||
{
|
||||
var parts = arg.Split('=', 2);
|
||||
StringView key = parts.GetNext().Value;
|
||||
StringView value;
|
||||
switch (parts.GetNext())
|
||||
{
|
||||
case .Err: PrintUsage!();
|
||||
case .Ok(out value):
|
||||
}
|
||||
key.Trim();
|
||||
value.Trim();
|
||||
switch (key)
|
||||
{
|
||||
case "src": Runtime.Assert(src.IsNull, "Duplicate var 'src'"); src = value;
|
||||
case "target": Runtime.Assert(target.IsNull, "Duplicate var 'target'"); target = value;
|
||||
case "builddir": Runtime.Assert(builddir.IsNull, "Duplicate var 'builddir'"); builddir = value;
|
||||
case "output": Runtime.Assert(output.IsNull, "Duplicate var 'output'"); output = value;
|
||||
case "cflags": Runtime.Assert(cflags.IsNull, "Duplicate var 'cflags'"); cflags = value;
|
||||
default: PrintUsage!();
|
||||
}
|
||||
}
|
||||
Runtime.Assert(!src.IsNull, "Missing var 'src'");
|
||||
Runtime.Assert(!target.IsNull, "Missing var 'target'");
|
||||
Runtime.Assert(!builddir.IsNull, "Missing var 'builddir'");
|
||||
Runtime.Assert(!builddir.IsNull, "Missing var 'output'");
|
||||
|
||||
{
|
||||
StreamWriter writer = scope .()..Create(scope $"{builddir}/build.ninja");
|
||||
{
|
||||
String buffer = scope .(1024);
|
||||
let currentDir = Directory.GetCurrentDirectory(..scope .(128));
|
||||
void WriteVarPath(StringView key, StringView value)
|
||||
{
|
||||
buffer.Append(key);
|
||||
buffer.Append(" = ");
|
||||
Path.GetAbsolutePath(value, currentDir, buffer);
|
||||
buffer.Append('\n');
|
||||
}
|
||||
WriteVarPath("src", src);
|
||||
WriteVarPath("builddir", builddir);
|
||||
buffer.AppendF($"""
|
||||
target = {target}
|
||||
cflags = {cflags}
|
||||
""");
|
||||
writer.Write(buffer);
|
||||
}
|
||||
writer.Write("""
|
||||
\n
|
||||
cc = clang
|
||||
ar = ar
|
||||
|
||||
rule cc
|
||||
command = $cc $cflags -target $target -MD -MF $out.d -c -o $out $in
|
||||
deps = gcc
|
||||
depfile = $out.d
|
||||
description = Building $in
|
||||
|
||||
rule ar
|
||||
command = $ar crs $out $in
|
||||
description = Creating static lib $out
|
||||
|
||||
|
||||
""");
|
||||
String ar = scope .(1024);
|
||||
ar.AppendF($"\nbuild $builddir/{output}.{target.Contains("windows") ? "lib" : "a"}: ar");
|
||||
|
||||
int pcount = 0;
|
||||
for (let arg in args)
|
||||
{
|
||||
if (arg == "--") break;
|
||||
pcount++;
|
||||
}
|
||||
StringView[] patterns = scope .[pcount];
|
||||
for (var pattern in ref patterns)
|
||||
{
|
||||
pattern = args[@pattern];
|
||||
}
|
||||
|
||||
void HandleMatch(StringView match)
|
||||
{
|
||||
writer.Write($"build $builddir/{match}.o: cc $src/{match}\n");
|
||||
ar.Append(" $builddir/", match, ".o");
|
||||
}
|
||||
let abssrc = Path.GetAbsolutePath(src, Directory.GetCurrentDirectory(..scope .(128)), ..scope .(128));
|
||||
FileMatcher.HandleMatches(patterns, abssrc, scope => HandleMatch);
|
||||
|
||||
writer.WriteLine(ar);
|
||||
}
|
||||
|
||||
let ret = system(scope $"ninja -C {builddir}");
|
||||
if (ret != 0)
|
||||
Console.WriteLine($"Failed to build {output}");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user