mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-22 20:29:36 +00:00
Compare commits
10 Commits
v0.6.5
...
9ab7ce15db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ab7ce15db | ||
|
|
954f2870ba | ||
|
|
9a24a80d59 | ||
|
|
a123f647c8 | ||
|
|
8f04a37eb0 | ||
| 60b9c4f983 | |||
|
|
f5fd273e29 | ||
| d6840fc7a2 | |||
|
|
f507148849 | ||
|
|
cfc6be1e95 |
@@ -12,6 +12,7 @@ pub fn help() -> i32 {
|
|||||||
println!(" init Initialize the current directory as a project");
|
println!(" init Initialize the current directory as a project");
|
||||||
println!(" build [--profile] Build the project (default: --debug)");
|
println!(" build [--profile] Build the project (default: --debug)");
|
||||||
println!(" run [--profile] Build and run the project (default: --debug)");
|
println!(" run [--profile] Build and run the project (default: --debug)");
|
||||||
|
println!(" tree Display the dependency tree");
|
||||||
println!(" test Run project tests (alias: tests)");
|
println!(" test Run project tests (alias: tests)");
|
||||||
println!(" clean Remove the target directory");
|
println!(" clean Remove the target directory");
|
||||||
println!(" gen <subcommand> Generate IDE integration files");
|
println!(" gen <subcommand> Generate IDE integration files");
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ pub mod new;
|
|||||||
pub mod run;
|
pub mod run;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
pub mod tree;
|
||||||
|
|||||||
118
src/cli/tree.rs
Normal file
118
src/cli/tree.rs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
use crate::core::config::Config;
|
||||||
|
use crate::utils::log::error;
|
||||||
|
use crate::utils::text::{BOLD_CYAN, printc};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use toml::Value;
|
||||||
|
|
||||||
|
pub fn tree(_args: &[String]) -> i32 {
|
||||||
|
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||||
|
let config = match Config::open("dcr.toml") {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => {
|
||||||
|
error("dcr.toml not found in current directory");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = config
|
||||||
|
.get("package.name")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
let version = config
|
||||||
|
.get("package.version")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("0.0.0");
|
||||||
|
|
||||||
|
printc(&format!("{} v{}", name, version), BOLD_CYAN);
|
||||||
|
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
seen.insert(name.to_string());
|
||||||
|
|
||||||
|
if let Some(deps) = config.get("dependencies").and_then(|v| v.as_table()) {
|
||||||
|
print_deps(deps, ¤t_dir, "", &mut seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_deps(
|
||||||
|
deps: &toml::value::Table,
|
||||||
|
base_path: &Path,
|
||||||
|
prefix: &str,
|
||||||
|
seen: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
|
let mut dep_list: Vec<_> = deps.iter().collect();
|
||||||
|
dep_list.sort_by_key(|(name, _)| *name);
|
||||||
|
|
||||||
|
for (i, (name, value)) in dep_list.iter().enumerate() {
|
||||||
|
let is_last = i == dep_list.len() - 1;
|
||||||
|
let connector = if is_last { "└── " } else { "├── " };
|
||||||
|
let child_prefix = if is_last { " " } else { "│ " };
|
||||||
|
|
||||||
|
let dep_path = resolve_dep_path(value, base_path);
|
||||||
|
let mut version = String::new();
|
||||||
|
let mut sub_deps = None;
|
||||||
|
let mut resolved_path = None;
|
||||||
|
|
||||||
|
if let Some(path) = &dep_path {
|
||||||
|
let dcr_toml = path.join("dcr.toml");
|
||||||
|
if dcr_toml.exists()
|
||||||
|
&& let Ok(config) = Config::open(&dcr_toml.to_string_lossy())
|
||||||
|
{
|
||||||
|
if let Some(v) = config.get("package.version").and_then(|v| v.as_str()) {
|
||||||
|
version = format!(" v{}", v);
|
||||||
|
}
|
||||||
|
sub_deps = config
|
||||||
|
.get("dependencies")
|
||||||
|
.and_then(|v| v.as_table())
|
||||||
|
.cloned();
|
||||||
|
resolved_path = Some(path.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if version.is_empty() {
|
||||||
|
version = match value {
|
||||||
|
Value::String(s) if s.starts_with("git:") || s.starts_with("github:") => {
|
||||||
|
format!(" ({})", s)
|
||||||
|
}
|
||||||
|
Value::Table(t) => {
|
||||||
|
if let Some(v) = t.get("version").and_then(|v| v.as_str()) {
|
||||||
|
format!(" v{}", v)
|
||||||
|
} else if let Some(git) = t.get("git").and_then(|v| v.as_str()) {
|
||||||
|
format!(" ({})", git)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => "".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}{}{}{}", prefix, connector, name, version);
|
||||||
|
|
||||||
|
if let Some(s_deps) = sub_deps
|
||||||
|
&& !seen.contains(*name)
|
||||||
|
{
|
||||||
|
seen.insert(name.to_string());
|
||||||
|
let new_prefix = format!("{}{}", prefix, child_prefix);
|
||||||
|
if let Some(path) = resolved_path {
|
||||||
|
print_deps(&s_deps, &path, &new_prefix, seen);
|
||||||
|
}
|
||||||
|
seen.remove(*name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_dep_path(value: &Value, base_path: &Path) -> Option<PathBuf> {
|
||||||
|
match value {
|
||||||
|
Value::String(s) => s
|
||||||
|
.strip_prefix("path:")
|
||||||
|
.map(|stripped| base_path.join(stripped)),
|
||||||
|
Value::Table(t) => t
|
||||||
|
.get("path")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|path| base_path.join(path)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -153,6 +153,49 @@ pub fn has_glob_magic(value: &str) -> bool {
|
|||||||
value.chars().any(|c| matches!(c, '*' | '?' | '['))
|
value.chars().any(|c| matches!(c, '*' | '?' | '['))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parallel_build<F>(total_tasks: usize, task_fn: F) -> Result<(), String>
|
||||||
|
where
|
||||||
|
F: Fn(usize) -> Result<(), String> + Sync + Send,
|
||||||
|
{
|
||||||
|
let num_threads = std::thread::available_parallelism()
|
||||||
|
.map(|n| n.get())
|
||||||
|
.unwrap_or(1);
|
||||||
|
|
||||||
|
let counter = std::sync::atomic::AtomicUsize::new(0);
|
||||||
|
let err_msg = std::sync::Mutex::new(None);
|
||||||
|
|
||||||
|
std::thread::scope(|s| {
|
||||||
|
for _ in 0..num_threads {
|
||||||
|
s.spawn(|| {
|
||||||
|
loop {
|
||||||
|
if err_msg.lock().unwrap().is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if i >= total_tasks {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = task_fn(i) {
|
||||||
|
let mut err = err_msg.lock().unwrap();
|
||||||
|
if err.is_none() {
|
||||||
|
*err = Some(e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(err) = err_msg.into_inner().unwrap() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn normalize_source_path(path: &Path) -> String {
|
pub fn normalize_source_path(path: &Path) -> String {
|
||||||
if !path.is_absolute() {
|
if !path.is_absolute() {
|
||||||
return path.to_string_lossy().to_string();
|
return path.to_string_lossy().to_string();
|
||||||
|
|||||||
@@ -107,25 +107,42 @@ fn build_objects(
|
|||||||
ctx: &BuildContext,
|
ctx: &BuildContext,
|
||||||
obj_ext: &str,
|
obj_ext: &str,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<Vec<String>, String> {
|
||||||
let mut objects = Vec::new();
|
let objects: Vec<String> = sources
|
||||||
for source in sources {
|
.iter()
|
||||||
let obj_path = common::object_path(obj_dir, source, obj_ext);
|
.map(|s| common::object_path(obj_dir, s, obj_ext))
|
||||||
if let Some(parent) = Path::new(&obj_path).parent() {
|
.collect();
|
||||||
|
|
||||||
|
common::parallel_build(sources.len(), |i| {
|
||||||
|
build_object(assembler, &sources[i], &objects[i], ctx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_object(
|
||||||
|
assembler: &str,
|
||||||
|
source: &str,
|
||||||
|
obj_path: &str,
|
||||||
|
ctx: &BuildContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if let Some(parent) = Path::new(obj_path).parent() {
|
||||||
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
||||||
}
|
}
|
||||||
if common::needs_rebuild(source, &obj_path) {
|
|
||||||
|
if !common::needs_rebuild(source, obj_path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut cmd = Command::new(assembler);
|
let mut cmd = Command::new(assembler);
|
||||||
cmd.arg(source).arg("-o").arg(&obj_path);
|
cmd.arg(source).arg("-o").arg(obj_path);
|
||||||
|
|
||||||
for flag in ctx.cflags {
|
for flag in ctx.cflags {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
match cmd.status() {
|
match cmd.status() {
|
||||||
Ok(status) if status.success() => {}
|
Ok(status) if status.success() => Ok(()),
|
||||||
Ok(_) => return Err("Build failed".to_string()),
|
Ok(_) => Err("Build failed".to_string()),
|
||||||
Err(err) => return Err(format!("Build failed: {err}")),
|
Err(err) => Err(format!("Build failed: {err}")),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
objects.push(obj_path);
|
|
||||||
}
|
|
||||||
Ok(objects)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,6 +185,8 @@ fn default_flags(profile: &str) -> &'static [&'static str] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static OUTPUT_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||||
|
|
||||||
fn build_objects(
|
fn build_objects(
|
||||||
compiler: &str,
|
compiler: &str,
|
||||||
sources: &[String],
|
sources: &[String],
|
||||||
@@ -192,15 +194,35 @@ fn build_objects(
|
|||||||
ctx: &BuildContext,
|
ctx: &BuildContext,
|
||||||
obj_ext: &str,
|
obj_ext: &str,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<Vec<String>, String> {
|
||||||
let mut objects = Vec::new();
|
let objects: Vec<String> = sources
|
||||||
for source in sources {
|
.iter()
|
||||||
let obj_path = common::object_path(obj_dir, source, obj_ext);
|
.map(|s| common::object_path(obj_dir, s, obj_ext))
|
||||||
if let Some(parent) = Path::new(&obj_path).parent() {
|
.collect();
|
||||||
|
|
||||||
|
common::parallel_build(sources.len(), |i| {
|
||||||
|
build_object(compiler, &sources[i], &objects[i], ctx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_object(
|
||||||
|
compiler: &str,
|
||||||
|
source: &str,
|
||||||
|
obj_path: &str,
|
||||||
|
ctx: &BuildContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if let Some(parent) = Path::new(obj_path).parent() {
|
||||||
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
||||||
}
|
}
|
||||||
if common::needs_rebuild(source, &obj_path) {
|
|
||||||
|
if !common::needs_rebuild(source, obj_path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut cmd = Command::new(compiler);
|
let mut cmd = Command::new(compiler);
|
||||||
cmd.arg("/nologo");
|
cmd.arg("/nologo");
|
||||||
|
|
||||||
match ctx.language.to_lowercase().as_str() {
|
match ctx.language.to_lowercase().as_str() {
|
||||||
"c" => {
|
"c" => {
|
||||||
cmd.arg("/TC");
|
cmd.arg("/TC");
|
||||||
@@ -208,40 +230,46 @@ fn build_objects(
|
|||||||
"c++" | "cpp" | "cxx" => {
|
"c++" | "cpp" | "cxx" => {
|
||||||
cmd.arg("/TP");
|
cmd.arg("/TP");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => return Err("Unsupported language".to_string()),
|
||||||
return Err("Unsupported language".to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.standard.is_empty() && ctx.language.to_lowercase() != "asm" {
|
if !ctx.standard.is_empty() && ctx.language.to_lowercase() != "asm" {
|
||||||
let std_flag = msvc_standard_flag(ctx.language, ctx.standard)?;
|
let std_flag = msvc_standard_flag(ctx.language, ctx.standard)?;
|
||||||
cmd.arg(std_flag);
|
cmd.arg(std_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(flag) = msvc_arch_flag(ctx.platform) {
|
if let Some(flag) = msvc_arch_flag(ctx.platform) {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.cflags.is_empty() {
|
if ctx.cflags.is_empty() {
|
||||||
for flag in default_flags(ctx.profile) {
|
for flag in default_flags(ctx.profile) {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in ctx.cflags {
|
for flag in ctx.cflags {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in ctx.include_dirs {
|
for dir in ctx.include_dirs {
|
||||||
cmd.arg(format!("/I{dir}"));
|
cmd.arg(format!("/I{dir}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.arg("/c").arg(source).arg(format!("/Fo:{}", obj_path));
|
cmd.arg("/c").arg(source).arg(format!("/Fo:{}", obj_path));
|
||||||
cmd.arg("/showIncludes");
|
cmd.arg("/showIncludes");
|
||||||
|
|
||||||
if std::env::var("DCR_DEBUG").is_ok() {
|
if std::env::var("DCR_DEBUG").is_ok() {
|
||||||
eprintln!("[dcr] {:?}", cmd);
|
eprintln!("[dcr] {:?}", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = cmd.output().map_err(|err| format!("Build failed: {err}"))?;
|
let output = cmd.output().map_err(|err| format!("Build failed: {err}"))?;
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
|
||||||
let mut headers = Vec::new();
|
let mut headers = Vec::new();
|
||||||
let mut clean_stdout = String::new();
|
let mut clean_stdout = String::new();
|
||||||
|
|
||||||
for line in stdout.lines() {
|
for line in stdout.lines() {
|
||||||
if let Some(stripped) = line.strip_prefix("Note: including file:") {
|
if let Some(stripped) = line.strip_prefix("Note: including file:") {
|
||||||
headers.push(stripped.trim().to_string());
|
headers.push(stripped.trim().to_string());
|
||||||
@@ -254,34 +282,36 @@ fn build_objects(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _lock = OUTPUT_MUTEX.lock().unwrap();
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
eprint!("{}", clean_stdout);
|
eprint!("{}", clean_stdout);
|
||||||
eprint!("{}", stderr);
|
eprint!("{}", stderr);
|
||||||
return Err("Build failed".to_string());
|
return Err("Build failed".to_string());
|
||||||
} else {
|
}
|
||||||
|
|
||||||
let trimmed_out = clean_stdout.trim();
|
let trimmed_out = clean_stdout.trim();
|
||||||
let trimmed_err = stderr.trim();
|
let trimmed_err = stderr.trim();
|
||||||
let src_filename = Path::new(source)
|
let src_filename = Path::new(source)
|
||||||
.file_name()
|
.file_name()
|
||||||
.and_then(|v| v.to_str())
|
.and_then(|v| v.to_str())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
|
|
||||||
if !trimmed_out.is_empty() && trimmed_out != src_filename {
|
if !trimmed_out.is_empty() && trimmed_out != src_filename {
|
||||||
print!("{}", clean_stdout);
|
print!("{}", clean_stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !trimmed_err.is_empty() {
|
if !trimmed_err.is_empty() {
|
||||||
eprintln!("{}", trimmed_err);
|
eprintln!("{}", trimmed_err);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let d_path = Path::new(&obj_path).with_extension("d");
|
let d_path = Path::new(obj_path).with_extension("d");
|
||||||
let mut d_content = format!("{}: \\\n", obj_path.replace('\\', "/"));
|
let mut d_content = format!("{}: \\\n", obj_path.replace('\\', "/"));
|
||||||
for h in headers {
|
for h in headers {
|
||||||
let escaped = h.replace('\\', "/").replace(" ", "\\ ");
|
let escaped = h.replace('\\', "/").replace(" ", "\\ ");
|
||||||
d_content.push_str(&format!(" {} \\\n", escaped));
|
d_content.push_str(&format!(" {} \\\n", escaped));
|
||||||
}
|
}
|
||||||
std::fs::write(&d_path, d_content).map_err(|err| format!("d file error: {err}"))?;
|
fs::write(&d_path, d_content).map_err(|err| format!("d file error: {err}"))?;
|
||||||
}
|
|
||||||
objects.push(obj_path);
|
Ok(())
|
||||||
}
|
|
||||||
Ok(objects)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,35 +107,55 @@ fn build_objects(
|
|||||||
ctx: &BuildContext,
|
ctx: &BuildContext,
|
||||||
obj_ext: &str,
|
obj_ext: &str,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<Vec<String>, String> {
|
||||||
let mut objects = Vec::new();
|
let objects: Vec<String> = sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| common::object_path(obj_dir, s, obj_ext))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let format = nasm_format(ctx.platform);
|
let format = nasm_format(ctx.platform);
|
||||||
for source in sources {
|
|
||||||
let obj_path = common::object_path(obj_dir, source, obj_ext);
|
common::parallel_build(sources.len(), |i| {
|
||||||
if let Some(parent) = Path::new(&obj_path).parent() {
|
build_object(assembler, &sources[i], &objects[i], format, ctx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_object(
|
||||||
|
assembler: &str,
|
||||||
|
source: &str,
|
||||||
|
obj_path: &str,
|
||||||
|
format: &str,
|
||||||
|
ctx: &BuildContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if let Some(parent) = Path::new(obj_path).parent() {
|
||||||
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
||||||
}
|
}
|
||||||
if common::needs_rebuild(source, &obj_path) {
|
|
||||||
|
if !common::needs_rebuild(source, obj_path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut cmd = Command::new(assembler);
|
let mut cmd = Command::new(assembler);
|
||||||
cmd.arg("-f")
|
cmd.arg("-f")
|
||||||
.arg(format)
|
.arg(format)
|
||||||
.arg(source)
|
.arg(source)
|
||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg(&obj_path);
|
.arg(obj_path);
|
||||||
|
|
||||||
for flag in ctx.cflags {
|
for flag in ctx.cflags {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if std::env::var("DCR_DEBUG").is_ok() {
|
if std::env::var("DCR_DEBUG").is_ok() {
|
||||||
eprintln!("[dcr] {:?}", cmd);
|
eprintln!("[dcr] {:?}", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
match cmd.status() {
|
match cmd.status() {
|
||||||
Ok(status) if status.success() => {}
|
Ok(status) if status.success() => Ok(()),
|
||||||
Ok(_) => return Err("Build failed".to_string()),
|
Ok(_) => Err("Build failed".to_string()),
|
||||||
Err(err) => return Err(format!("Build failed: {err}")),
|
Err(err) => Err(format!("Build failed: {err}")),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
objects.push(obj_path);
|
|
||||||
}
|
|
||||||
Ok(objects)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nasm_format(platform: Option<&str>) -> &'static str {
|
fn nasm_format(platform: Option<&str>) -> &'static str {
|
||||||
|
|||||||
@@ -132,54 +132,79 @@ fn build_objects(
|
|||||||
ctx: &BuildContext,
|
ctx: &BuildContext,
|
||||||
obj_ext: &str,
|
obj_ext: &str,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<Vec<String>, String> {
|
||||||
let mut objects = Vec::new();
|
let objects: Vec<String> = sources
|
||||||
for source in sources {
|
.iter()
|
||||||
let obj_path = common::object_path(obj_dir, source, obj_ext);
|
.map(|s| common::object_path(obj_dir, s, obj_ext))
|
||||||
if let Some(parent) = Path::new(&obj_path).parent() {
|
.collect();
|
||||||
|
|
||||||
|
common::parallel_build(sources.len(), |i| {
|
||||||
|
build_object(compiler, &sources[i], &objects[i], ctx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_object(
|
||||||
|
compiler: &str,
|
||||||
|
source: &str,
|
||||||
|
obj_path: &str,
|
||||||
|
ctx: &BuildContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if let Some(parent) = Path::new(obj_path).parent() {
|
||||||
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
fs::create_dir_all(parent).map_err(|err| format!("obj dir error: {err}"))?;
|
||||||
}
|
}
|
||||||
if common::needs_rebuild(source, &obj_path) {
|
|
||||||
|
if !common::needs_rebuild(source, obj_path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut cmd = Command::new(compiler);
|
let mut cmd = Command::new(compiler);
|
||||||
cmd.arg("-c").arg(source).arg("-o").arg(&obj_path);
|
cmd.arg("-c").arg(source).arg("-o").arg(obj_path);
|
||||||
|
|
||||||
if ctx.kind == "sharedlib" {
|
if ctx.kind == "sharedlib" {
|
||||||
cmd.arg("-fPIC");
|
cmd.arg("-fPIC");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(flag) = asm_lang_flag(source) {
|
if let Some(flag) = asm_lang_flag(source) {
|
||||||
cmd.arg("-x").arg(flag);
|
cmd.arg("-x").arg(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(platform) = ctx.platform
|
if let Some(platform) = ctx.platform
|
||||||
&& !platform.trim().is_empty()
|
&& !platform.trim().is_empty()
|
||||||
{
|
{
|
||||||
cmd.arg(format!("-march={}", platform));
|
cmd.arg(format!("-march={}", platform));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.standard.is_empty() && ctx.language.to_lowercase() != "asm" {
|
if !ctx.standard.is_empty() && ctx.language.to_lowercase() != "asm" {
|
||||||
cmd.arg(format!("-std={}", ctx.standard));
|
cmd.arg(format!("-std={}", ctx.standard));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.cflags.is_empty() {
|
if ctx.cflags.is_empty() {
|
||||||
for flag in default_flags(ctx.profile) {
|
for flag in default_flags(ctx.profile) {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in ctx.cflags {
|
for flag in ctx.cflags {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in ctx.include_dirs {
|
for dir in ctx.include_dirs {
|
||||||
cmd.arg(format!("-I{dir}"));
|
cmd.arg(format!("-I{dir}"));
|
||||||
}
|
}
|
||||||
let d_path = Path::new(&obj_path).with_extension("d");
|
|
||||||
|
let d_path = Path::new(obj_path).with_extension("d");
|
||||||
cmd.arg("-MMD").arg("-MF").arg(&d_path);
|
cmd.arg("-MMD").arg("-MF").arg(&d_path);
|
||||||
|
|
||||||
if std::env::var("DCR_DEBUG").is_ok() {
|
if std::env::var("DCR_DEBUG").is_ok() {
|
||||||
eprintln!("[dcr] {:?}", cmd);
|
eprintln!("[dcr] {:?}", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
match cmd.status() {
|
match cmd.status() {
|
||||||
Ok(status) if status.success() => {}
|
Ok(status) if status.success() => Ok(()),
|
||||||
Ok(_) => return Err("Build failed".to_string()),
|
Ok(_) => Err("Build failed".to_string()),
|
||||||
Err(err) => return Err(format!("Build failed: {err}")),
|
Err(err) => Err(format!("Build failed: {err}")),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
objects.push(obj_path);
|
|
||||||
}
|
|
||||||
Ok(objects)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn asm_lang_flag(source: &str) -> Option<&'static str> {
|
fn asm_lang_flag(source: &str) -> Option<&'static str> {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ fn main() {
|
|||||||
"add" => cli::add::add(rest),
|
"add" => cli::add::add(rest),
|
||||||
"build" => cli::build::build(rest),
|
"build" => cli::build::build(rest),
|
||||||
"run" => cli::run::run(rest),
|
"run" => cli::run::run(rest),
|
||||||
|
"tree" => cli::tree::tree(rest),
|
||||||
"test" | "tests" => cli::test::test(rest),
|
"test" | "tests" => cli::test::test(rest),
|
||||||
"clean" => cli::clean::clean(rest),
|
"clean" => cli::clean::clean(rest),
|
||||||
"gen" => cli::r#gen::r#gen(rest),
|
"gen" => cli::r#gen::r#gen(rest),
|
||||||
|
|||||||
Reference in New Issue
Block a user