feat(build): add flat-bin kind and FAT [archive] packing for OS-dev

This commit is contained in:
2026-07-21 17:42:50 +08:00
parent 9d030cba0c
commit a9dd8c1631
32 changed files with 1295 additions and 75 deletions

View File

@@ -19,9 +19,130 @@ use crate::core::build::builder::BuildContext;
use crate::core::build::common;
use crate::platform;
use crate::utils::build::is_bare_metal_target;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::Instant;
pub(crate) fn artifact_dir(ctx: &BuildContext) -> PathBuf {
match ctx.target_dir {
Some(dir) => Path::new(dir).to_path_buf(),
None => Path::new("./target").join(ctx.profile),
}
}
pub(crate) fn flat_output_path(ctx: &BuildContext) -> String {
let name = ctx.output_filename.unwrap_or(ctx.project_name);
let ext = ctx.output_extension.unwrap_or("bin");
let file = if ext.is_empty() {
name.to_string()
} else {
format!("{name}.{ext}")
};
artifact_dir(ctx).join(file).to_string_lossy().to_string()
}
pub(crate) fn flat_source_output_path(ctx: &BuildContext, source: &str) -> String {
let stem = Path::new(source)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("out");
let ext = ctx.output_extension.unwrap_or("bin");
let file = if ext.is_empty() {
stem.to_string()
} else {
format!("{stem}.{ext}")
};
artifact_dir(ctx).join(file).to_string_lossy().to_string()
}
fn resolve_objcopy() -> Result<String, String> {
for candidate in ["llvm-objcopy", "objcopy", "gobjcopy"] {
if Command::new(candidate)
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
{
return Ok(candidate.to_string());
}
}
Err(
"flat-bin requires llvm-objcopy, objcopy, or gobjcopy in PATH \
(install binutils or LLVM tools)"
.to_string(),
)
}
pub(crate) fn objcopy_binary(ctx: &BuildContext, input: &str, output: &str) -> Result<(), String> {
if let Some(parent) = Path::new(output).parent() {
std::fs::create_dir_all(parent).map_err(|e| format!("output dir error: {e}"))?;
}
if !common::needs_rebuild(input, output) {
return Ok(());
}
let tool = resolve_objcopy()?;
let mut cmd = Command::new(&tool);
cmd.arg("-O").arg("binary").arg(input).arg(output);
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd);
}
common::run_command_sync_output(&mut cmd)
}
pub(crate) fn link_flat_binary(
ctx: &BuildContext,
objects: &[String],
default_linker: &str,
start_time: Instant,
) -> Result<f64, String> {
let out_path = flat_output_path(ctx);
if !common::needs_link(objects, &out_path) {
return Ok(common::elapsed_secs(start_time));
}
let intermediate = {
let dir = match ctx.target_dir {
Some(dir) => Path::new(dir).join("obj"),
None => Path::new("./target").join(ctx.profile).join("obj"),
};
std::fs::create_dir_all(&dir).map_err(|e| format!("obj dir error: {e}"))?;
dir.join(format!("{}.flat.elf", ctx.project_name))
.to_string_lossy()
.to_string()
};
let mut cmd = Command::new(ctx.linker.unwrap_or(default_linker));
cmd.arg("-nostdlib").arg("-static");
for obj in objects {
cmd.arg(obj);
}
for dir in ctx.lib_dirs {
cmd.arg(format!("-L{dir}"));
}
for lib in ctx.libs {
cmd.arg(format!("-l{lib}"));
}
for flag in ctx.ldflags {
cmd.arg(flag);
}
cmd.arg("-o").arg(&intermediate);
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd);
}
let output = cmd.output().map_err(|err| format!("Build failed: {err}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
return Err(format!(
"flat-bin link failed:\nstdout: {stdout}\nstderr: {stderr}"
));
}
objcopy_binary(ctx, &intermediate, &out_path)?;
Ok(common::elapsed_secs(start_time))
}
pub(crate) fn archive_static(
ctx: &BuildContext,
objects: &[String],
@@ -56,6 +177,10 @@ pub(crate) fn link_binary(
default_linker: &str,
start_time: Instant,
) -> Result<f64, String> {
if crate::utils::build::is_flat_bin(ctx.kind) {
return link_flat_binary(ctx, objects, default_linker, start_time);
}
let mut cmd = Command::new(ctx.linker.unwrap_or(default_linker));
if ctx.kind == "sharedlib" {
if cfg!(target_os = "macos") {