9e515c157d
- manifest.bux: parse [dependencies] section with up to 8 deps - manifest.bux: Manifest_AddDep, Manifest_RemoveDep, Manifest_ToString - cli.bux: bux add <name> <url> — adds dependency to bux.toml - cli.bux: bux remove <name> — removes dependency from bux.toml - cli.bux: bux fetch — git clone/pull dependencies into deps/ - cli.bux: Cli_BuildProject now merges deps/<name>/src/*.bux before user code - Selfhost bootstrap loop verified: C output deterministic ✓
254 lines
9.7 KiB
Plaintext
254 lines
9.7 KiB
Plaintext
// manifest.bux — Manifest parser for bux.toml files
|
|
// Parses package metadata: name, version, type, build output.
|
|
module Manifest {
|
|
|
|
extern func bux_strlen(s: String) -> uint;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Manifest struct
|
|
// ---------------------------------------------------------------------------
|
|
struct Manifest {
|
|
name: String;
|
|
version: String;
|
|
pkgType: String;
|
|
output: String;
|
|
depCount: int;
|
|
depName0: String;
|
|
depUrl0: String;
|
|
depName1: String;
|
|
depUrl1: String;
|
|
depName2: String;
|
|
depUrl2: String;
|
|
depName3: String;
|
|
depUrl3: String;
|
|
depName4: String;
|
|
depUrl4: String;
|
|
depName5: String;
|
|
depUrl5: String;
|
|
depName6: String;
|
|
depUrl6: String;
|
|
depName7: String;
|
|
depUrl7: String;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Simple TOML parser (handles [Package] and [Build] sections)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func Manifest_Parse(content: String) -> Manifest {
|
|
var m: Manifest;
|
|
m.name = "";
|
|
m.version = "0.1.0";
|
|
m.pkgType = "bin";
|
|
m.output = "Bin";
|
|
m.depCount = 0;
|
|
|
|
if String_Eq(content, "") { return m; }
|
|
|
|
var currentSection: String = "";
|
|
let count: uint = String_SplitCount(content, "\n");
|
|
var i: uint = 0;
|
|
while i < count {
|
|
let line: String = String_SplitPart(content, "\n", i);
|
|
|
|
// Skip empty lines and comments
|
|
if String_Eq(line, "") { i = i + 1; continue; }
|
|
if String_StartsWith(line, "#") { i = i + 1; continue; }
|
|
|
|
// Section header: [Section]
|
|
if String_StartsWith(line, "[") {
|
|
if String_StartsWith(line, "[Package]") {
|
|
currentSection = "Package";
|
|
} else if String_StartsWith(line, "[Build]") {
|
|
currentSection = "Build";
|
|
} else if String_StartsWith(line, "[dependencies]") {
|
|
currentSection = "dependencies";
|
|
} else {
|
|
currentSection = "";
|
|
}
|
|
i = i + 1; continue;
|
|
}
|
|
|
|
// Key = Value
|
|
let eqCount: uint = String_SplitCount(line, "=");
|
|
if eqCount >= 2 {
|
|
let key: String = String_Trim(String_SplitPart(line, "=", 0));
|
|
let rawVal: String = String_Trim(String_SplitPart(line, "=", 1));
|
|
|
|
// Strip quotes from value
|
|
var val: String = rawVal;
|
|
if String_StartsWith(val, "\"") && String_EndsWith(val, "\"") {
|
|
let vlen: uint = bux_strlen(val);
|
|
if vlen >= 2 {
|
|
val = String_Slice(val, 1, vlen - 2);
|
|
}
|
|
}
|
|
|
|
if String_Eq(currentSection, "Package") {
|
|
if String_Eq(key, "Name") { m.name = val; }
|
|
if String_Eq(key, "Version") { m.version = val; }
|
|
if String_Eq(key, "Type") { m.pkgType = val; }
|
|
} else if String_Eq(currentSection, "Build") {
|
|
if String_Eq(key, "Output") { m.output = val; }
|
|
} else if String_Eq(currentSection, "dependencies") {
|
|
if m.depCount < 8 {
|
|
if m.depCount == 0 { m.depName0 = key; m.depUrl0 = val; }
|
|
else if m.depCount == 1 { m.depName1 = key; m.depUrl1 = val; }
|
|
else if m.depCount == 2 { m.depName2 = key; m.depUrl2 = val; }
|
|
else if m.depCount == 3 { m.depName3 = key; m.depUrl3 = val; }
|
|
else if m.depCount == 4 { m.depName4 = key; m.depUrl4 = val; }
|
|
else if m.depCount == 5 { m.depName5 = key; m.depUrl5 = val; }
|
|
else if m.depCount == 6 { m.depName6 = key; m.depUrl6 = val; }
|
|
else if m.depCount == 7 { m.depName7 = key; m.depUrl7 = val; }
|
|
m.depCount = m.depCount + 1;
|
|
}
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Dependency helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func Manifest_HasDep(m: Manifest, name: String) -> bool {
|
|
var i: int = 0;
|
|
while i < m.depCount {
|
|
var depName: String = "";
|
|
if i == 0 { depName = m.depName0; }
|
|
else if i == 1 { depName = m.depName1; }
|
|
else if i == 2 { depName = m.depName2; }
|
|
else if i == 3 { depName = m.depName3; }
|
|
else if i == 4 { depName = m.depName4; }
|
|
else if i == 5 { depName = m.depName5; }
|
|
else if i == 6 { depName = m.depName6; }
|
|
else if i == 7 { depName = m.depName7; }
|
|
if String_Eq(depName, name) { return true; }
|
|
i = i + 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
func Manifest_GetDepUrl(m: Manifest, name: String) -> String {
|
|
var i: int = 0;
|
|
while i < m.depCount {
|
|
var depName: String = "";
|
|
var depUrl: String = "";
|
|
if i == 0 { depName = m.depName0; depUrl = m.depUrl0; }
|
|
else if i == 1 { depName = m.depName1; depUrl = m.depUrl1; }
|
|
else if i == 2 { depName = m.depName2; depUrl = m.depUrl2; }
|
|
else if i == 3 { depName = m.depName3; depUrl = m.depUrl3; }
|
|
else if i == 4 { depName = m.depName4; depUrl = m.depUrl4; }
|
|
else if i == 5 { depName = m.depName5; depUrl = m.depUrl5; }
|
|
else if i == 6 { depName = m.depName6; depUrl = m.depUrl6; }
|
|
else if i == 7 { depName = m.depName7; depUrl = m.depUrl7; }
|
|
if String_Eq(depName, name) { return depUrl; }
|
|
i = i + 1;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
func Manifest_AddDep(m: *Manifest, name: String, url: String) -> bool {
|
|
if Manifest_HasDep(*m, name) { return false; }
|
|
if m.depCount >= 8 { return false; }
|
|
if m.depCount == 0 { m.depName0 = name; m.depUrl0 = url; }
|
|
else if m.depCount == 1 { m.depName1 = name; m.depUrl1 = url; }
|
|
else if m.depCount == 2 { m.depName2 = name; m.depUrl2 = url; }
|
|
else if m.depCount == 3 { m.depName3 = name; m.depUrl3 = url; }
|
|
else if m.depCount == 4 { m.depName4 = name; m.depUrl4 = url; }
|
|
else if m.depCount == 5 { m.depName5 = name; m.depUrl5 = url; }
|
|
else if m.depCount == 6 { m.depName6 = name; m.depUrl6 = url; }
|
|
else if m.depCount == 7 { m.depName7 = name; m.depUrl7 = url; }
|
|
m.depCount = m.depCount + 1;
|
|
return true;
|
|
}
|
|
|
|
func Manifest_RemoveDep(m: *Manifest, name: String) -> bool {
|
|
var found: int = -1;
|
|
var i: int = 0;
|
|
while i < m.depCount {
|
|
var depName: String = "";
|
|
if i == 0 { depName = m.depName0; }
|
|
else if i == 1 { depName = m.depName1; }
|
|
else if i == 2 { depName = m.depName2; }
|
|
else if i == 3 { depName = m.depName3; }
|
|
else if i == 4 { depName = m.depName4; }
|
|
else if i == 5 { depName = m.depName5; }
|
|
else if i == 6 { depName = m.depName6; }
|
|
else if i == 7 { depName = m.depName7; }
|
|
if String_Eq(depName, name) { found = i; break; }
|
|
i = i + 1;
|
|
}
|
|
if found < 0 { return false; }
|
|
i = found;
|
|
while i < m.depCount - 1 {
|
|
if i == 0 { m.depName0 = m.depName1; m.depUrl0 = m.depUrl1; }
|
|
else if i == 1 { m.depName1 = m.depName2; m.depUrl1 = m.depUrl2; }
|
|
else if i == 2 { m.depName2 = m.depName3; m.depUrl2 = m.depUrl3; }
|
|
else if i == 3 { m.depName3 = m.depName4; m.depUrl3 = m.depUrl4; }
|
|
else if i == 4 { m.depName4 = m.depName5; m.depUrl4 = m.depUrl5; }
|
|
else if i == 5 { m.depName5 = m.depName6; m.depUrl5 = m.depUrl6; }
|
|
else if i == 6 { m.depName6 = m.depName7; m.depUrl6 = m.depUrl7; }
|
|
i = i + 1;
|
|
}
|
|
m.depCount = m.depCount - 1;
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Serialize manifest back to TOML
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func Manifest_ToString(m: Manifest) -> String {
|
|
let sb: StringBuilder = StringBuilder_New();
|
|
StringBuilder_Append(&sb, "[Package]\n");
|
|
StringBuilder_Append(&sb, "Name = \"");
|
|
StringBuilder_Append(&sb, m.name);
|
|
StringBuilder_Append(&sb, "\"\n");
|
|
StringBuilder_Append(&sb, "Version = \"");
|
|
StringBuilder_Append(&sb, m.version);
|
|
StringBuilder_Append(&sb, "\"\n");
|
|
StringBuilder_Append(&sb, "Type = \"");
|
|
StringBuilder_Append(&sb, m.pkgType);
|
|
StringBuilder_Append(&sb, "\"\n\n");
|
|
StringBuilder_Append(&sb, "[Build]\n");
|
|
StringBuilder_Append(&sb, "Output = \"");
|
|
StringBuilder_Append(&sb, m.output);
|
|
StringBuilder_Append(&sb, "\"\n");
|
|
if m.depCount > 0 {
|
|
StringBuilder_Append(&sb, "\n[dependencies]\n");
|
|
var i: int = 0;
|
|
while i < m.depCount {
|
|
var depName: String = "";
|
|
var depUrl: String = "";
|
|
if i == 0 { depName = m.depName0; depUrl = m.depUrl0; }
|
|
else if i == 1 { depName = m.depName1; depUrl = m.depUrl1; }
|
|
else if i == 2 { depName = m.depName2; depUrl = m.depUrl2; }
|
|
else if i == 3 { depName = m.depName3; depUrl = m.depUrl3; }
|
|
else if i == 4 { depName = m.depName4; depUrl = m.depUrl4; }
|
|
else if i == 5 { depName = m.depName5; depUrl = m.depUrl5; }
|
|
else if i == 6 { depName = m.depName6; depUrl = m.depUrl6; }
|
|
else if i == 7 { depName = m.depName7; depUrl = m.depUrl7; }
|
|
StringBuilder_Append(&sb, depName);
|
|
StringBuilder_Append(&sb, " = \"");
|
|
StringBuilder_Append(&sb, depUrl);
|
|
StringBuilder_Append(&sb, "\"\n");
|
|
i = i + 1;
|
|
}
|
|
}
|
|
return StringBuilder_Build(&sb);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Load manifest from file
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func Manifest_Load(path: String) -> Manifest {
|
|
let content: String = ReadFile(path);
|
|
return Manifest_Parse(content);
|
|
}
|
|
}
|