feat: parallel compilation for all builders

This commit is contained in:
peoplemiau1
2026-05-13 18:50:42 +03:00
parent 2de836a421
commit cfc6be1e95
4 changed files with 389 additions and 175 deletions

View File

@@ -107,25 +107,75 @@ 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();
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::SeqCst);
if i >= sources.len() {
break;
}
let source = &sources[i];
let obj_path = &objects[i];
if let Err(e) = build_object(assembler, source, obj_path, ctx) {
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(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)
}

View File

@@ -192,15 +192,68 @@ 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();
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::SeqCst);
if i >= sources.len() {
break;
}
let source = &sources[i];
let obj_path = &objects[i];
if let Err(e) = build_object(compiler, source, obj_path, ctx) {
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(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,45 +261,50 @@ 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());
} else if let Some(stripped) = line.strip_prefix("Примечание: включение файла:") } else if let Some(stripped) = line.strip_prefix("Примечание: включение файла:") {
{
headers.push(stripped.trim().to_string()); headers.push(stripped.trim().to_string());
} else { } else {
clean_stdout.push_str(line); clean_stdout.push_str(line);
@@ -258,30 +316,30 @@ fn build_objects(
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)
} }

View File

@@ -107,36 +107,84 @@ 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 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);
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); std::thread::scope(|s| {
if let Some(parent) = Path::new(&obj_path).parent() { 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::SeqCst);
if i >= sources.len() {
break;
}
let source = &sources[i];
let obj_path = &objects[i];
if let Err(e) = build_object(assembler, source, obj_path, format, ctx) {
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(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(source).arg("-o").arg(obj_path);
.arg(format)
.arg(source)
.arg("-o")
.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 {
if let Some(p) = platform { if let Some(p) = platform {

View File

@@ -132,55 +132,113 @@ 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();
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::SeqCst);
if i >= sources.len() {
break;
}
let source = &sources[i];
let obj_path = &objects[i];
if let Err(e) = build_object(compiler, source, obj_path, ctx) {
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(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> {
let ext = Path::new(source).extension().and_then(|v| v.to_str())?; let ext = Path::new(source).extension().and_then(|v| v.to_str())?;