Phase 6: P-Code-Bibliotheken und Linker abschließen, Cross-Buildplan festhalten
This commit is contained in:
@@ -21,11 +21,12 @@ fn main() -> ExitCode {
|
||||
match args.first().map(String::as_str) {
|
||||
Some("run") => cmd_run(&args[1..]),
|
||||
Some("build") => cmd_build(&args[1..]),
|
||||
Some("link") => cmd_link(&args[1..]),
|
||||
Some("check") => cmd_check(&args[1..]),
|
||||
Some("convert-frm") => cmd_convert_frm(&args[1..]),
|
||||
_ => {
|
||||
eprintln!(
|
||||
"Aufruf: tbc run|build|check <datei.bas|datei.frm|projekt.mak|datei.tbc> | tbc convert-frm <quelle.frm> <ziel.frm>"
|
||||
"Aufruf: tbc run|build|link|check <datei.bas|datei.frm|projekt.mak|datei.tbc> | tbc convert-frm <quelle.frm> <ziel.frm>"
|
||||
);
|
||||
ExitCode::from(1)
|
||||
}
|
||||
@@ -79,7 +80,7 @@ fn compile(
|
||||
path_arg: Option<&String>,
|
||||
) -> Result<(PathBuf, tb_vm::bytecode::CompiledModule), ExitCode> {
|
||||
let Some(path) = path_arg else {
|
||||
eprintln!("Aufruf: tbc run|build|check <datei.bas|datei.frm|projekt.mak|datei.tbc>");
|
||||
eprintln!("Aufruf: tbc run|build|link|check <datei.bas|datei.frm|projekt.mak|datei.tbc>");
|
||||
return Err(ExitCode::from(1));
|
||||
};
|
||||
let path = PathBuf::from(path);
|
||||
@@ -118,6 +119,10 @@ fn compile(
|
||||
}
|
||||
|
||||
fn cmd_check(args: &[String]) -> ExitCode {
|
||||
if args.len() != 1 {
|
||||
eprintln!("Aufruf: tbc check <Projekt>");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
match compile(args.first()) {
|
||||
Ok(_) => ExitCode::SUCCESS,
|
||||
Err(code) => code,
|
||||
@@ -125,7 +130,13 @@ fn cmd_check(args: &[String]) -> ExitCode {
|
||||
}
|
||||
|
||||
fn cmd_build(args: &[String]) -> ExitCode {
|
||||
match build(args) {
|
||||
cmd_output(args, false)
|
||||
}
|
||||
fn cmd_link(args: &[String]) -> ExitCode {
|
||||
cmd_output(args, true)
|
||||
}
|
||||
fn cmd_output(args: &[String], link: bool) -> ExitCode {
|
||||
match build(args, link) {
|
||||
Ok(path) => {
|
||||
println!("{}", path.display());
|
||||
ExitCode::SUCCESS
|
||||
@@ -137,9 +148,10 @@ fn cmd_build(args: &[String]) -> ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn build(args: &[String]) -> anyhow::Result<PathBuf> {
|
||||
fn build(args: &[String], link: bool) -> anyhow::Result<PathBuf> {
|
||||
use anyhow::{bail, ensure, Context};
|
||||
let mut source = None;
|
||||
let mut inputs = Vec::new();
|
||||
let mut library = false;
|
||||
let mut exe = false;
|
||||
let mut force = false;
|
||||
let mut output = None;
|
||||
@@ -148,6 +160,13 @@ fn build(args: &[String]) -> anyhow::Result<PathBuf> {
|
||||
let mut args = args.iter();
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--library" => {
|
||||
ensure!(
|
||||
!library && !link,
|
||||
"--library ist nur bei build zulässig und darf nicht doppelt sein"
|
||||
);
|
||||
library = true;
|
||||
}
|
||||
"--exe" => {
|
||||
ensure!(!exe, "Doppeltes --exe");
|
||||
exe = true;
|
||||
@@ -171,30 +190,74 @@ fn build(args: &[String]) -> anyhow::Result<PathBuf> {
|
||||
"Doppelte Option {arg}"
|
||||
);
|
||||
}
|
||||
"--" => {
|
||||
for value in args.by_ref() {
|
||||
ensure!(
|
||||
source.replace(value.clone()).is_none(),
|
||||
"Mehrere Quelldateien angegeben"
|
||||
);
|
||||
}
|
||||
}
|
||||
"--" => inputs.extend(args.by_ref().cloned()),
|
||||
value if value.starts_with('-') => bail!("Unbekannte Build-Option: {value}"),
|
||||
_ => {
|
||||
ensure!(
|
||||
source.replace(arg.clone()).is_none(),
|
||||
"Mehrere Quelldateien angegeben"
|
||||
);
|
||||
}
|
||||
_ => inputs.push(arg.clone()),
|
||||
}
|
||||
}
|
||||
let source = source.context(
|
||||
"Aufruf: tbc build <Quelle> [--exe --target <Triple> --template <tbrt> -o <Ziel> --force]",
|
||||
)?;
|
||||
ensure!(!inputs.is_empty(), "Aufruf: tbc build|link <Quelle> [Bibliothek.tbl ...] [--library | --exe] [-o Ziel] [--force]");
|
||||
ensure!(!(exe && library), "--library und --exe schließen sich aus");
|
||||
ensure!(
|
||||
exe || (target.is_none() && template.is_none() && output.is_none() && !force),
|
||||
"Native Ausgabeoptionen benötigen --exe"
|
||||
exe || (target.is_none() && template.is_none()),
|
||||
"--target/--template benötigen --exe"
|
||||
);
|
||||
ensure!(
|
||||
exe || library || link || output.is_none(),
|
||||
"Ausgabeoptionen benötigen --exe, --library oder link"
|
||||
);
|
||||
let source = &inputs[0];
|
||||
let path = PathBuf::from(source);
|
||||
let tbc = tb_vm::project_io::has_extension(&path, "tbc");
|
||||
ensure!(!tbc || (!library && inputs.len()==1 && (!link || exe)), "Fertiges TBC kann nur ohne zusätzliche TBL mit link --exe verpackt werden; es ist kein Library-Produkt");
|
||||
let mut protected = vec![path.clone()];
|
||||
let mut compiler = tb_vm::project::ProjectCompiler::default();
|
||||
let loaded = if tbc {
|
||||
None
|
||||
} else {
|
||||
let mut project = SourceLoader::default()
|
||||
.load(&path)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
for input in &inputs[1..] {
|
||||
project
|
||||
.add_library(Path::new(input))
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
}
|
||||
protected.extend(project.protected_inputs());
|
||||
Some(project)
|
||||
};
|
||||
let has_libraries = loaded.as_ref().is_some_and(|p| !p.libraries.is_empty());
|
||||
let diags = |errors: Vec<tb_frontend::Diagnostic>| {
|
||||
anyhow::anyhow!(errors
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"))
|
||||
};
|
||||
let cancel = export_cancel::ExportCancel::new()?;
|
||||
if library {
|
||||
let product = loaded
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.compile_library(&mut compiler)
|
||||
.map_err(diags)?;
|
||||
let bytes = product.to_tbl().map_err(anyhow::Error::msg)?;
|
||||
let out = output
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| path.with_extension("tbl"));
|
||||
tb_export::publish(
|
||||
&out,
|
||||
force,
|
||||
&protected,
|
||||
&|| cancel.cancelled(),
|
||||
|temporary| {
|
||||
std::fs::write(temporary, &bytes)?;
|
||||
tb_vm::library::Library::from_tbl(&std::fs::read(temporary)?)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
return Ok(out);
|
||||
}
|
||||
let selected = if exe {
|
||||
Some(match target {
|
||||
Some(s) => tb_export::Target::parse(&s)?,
|
||||
@@ -203,8 +266,14 @@ fn build(args: &[String]) -> anyhow::Result<PathBuf> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (path, module) =
|
||||
compile(Some(&source)).map_err(|_| anyhow::anyhow!("Build fehlgeschlagen"))?;
|
||||
let module = if let Some(project) = loaded {
|
||||
project
|
||||
.compile(&mut compiler, &module_name(&path))
|
||||
.map_err(diags)?
|
||||
} else {
|
||||
tb_vm::bytecode::CompiledModule::from_tbc(&std::fs::read(&path)?)
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))?
|
||||
};
|
||||
if let Some(target) = selected {
|
||||
let out = output.map(PathBuf::from).unwrap_or_else(|| {
|
||||
path.with_extension(if target == tb_export::Target::WindowsAmd64 {
|
||||
@@ -222,7 +291,6 @@ fn build(args: &[String]) -> anyhow::Result<PathBuf> {
|
||||
.join(target.triple())
|
||||
.join(target.runtime_name()),
|
||||
};
|
||||
let cancel = export_cancel::ExportCancel::new()?;
|
||||
tb_export::export(
|
||||
tb_export::Export {
|
||||
module: &module,
|
||||
@@ -230,14 +298,37 @@ fn build(args: &[String]) -> anyhow::Result<PathBuf> {
|
||||
template: &template,
|
||||
output: &out,
|
||||
overwrite: force,
|
||||
protected: &[path],
|
||||
protected: &protected,
|
||||
},
|
||||
&|| cancel.cancelled(),
|
||||
)?;
|
||||
Ok(out)
|
||||
} else {
|
||||
let out = path.with_extension("tbc");
|
||||
std::fs::write(&out, module.to_tbc())?;
|
||||
let legacy = !link && !has_libraries && output.is_none() && !force && inputs.len() == 1;
|
||||
let out = output
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| path.with_extension("tbc"));
|
||||
if legacy
|
||||
&& !tbc
|
||||
&& protected.iter().all(|p| {
|
||||
tb_vm::project_io::identity(p).ok() != tb_vm::project_io::identity(&out).ok()
|
||||
})
|
||||
{
|
||||
std::fs::write(&out, module.to_tbc())?;
|
||||
} else {
|
||||
tb_export::publish(
|
||||
&out,
|
||||
force,
|
||||
&protected,
|
||||
&|| cancel.cancelled(),
|
||||
|temporary| {
|
||||
std::fs::write(temporary, module.to_tbc())?;
|
||||
tb_vm::bytecode::CompiledModule::from_tbc(&std::fs::read(temporary)?)
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user