Skip to content

Allow running multiple transpiles in parallel #1241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions c2rust-transpile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ smallvec = "1.0"
strum = "0.24"
strum_macros = "0.24"
syn = { version = "1.0", features = ["full", "extra-traits", "parsing", "printing"]}
tempfile = "3.5.0"

[features]
# Force static linking of LLVM
Expand Down
18 changes: 14 additions & 4 deletions c2rust-transpile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use itertools::Itertools;
use log::{info, warn};
use regex::Regex;
use serde_derive::Serialize;
pub use tempfile::TempDir;

use crate::c_ast::Printer;
use crate::c_ast::*;
Expand Down Expand Up @@ -226,8 +227,18 @@ fn get_module_name(
file.to_str().map(String::from)
}

pub fn create_temp_compile_commands(sources: &[PathBuf]) -> PathBuf {
let temp_path = std::env::temp_dir().join("compile_commands.json");
pub fn create_temp_compile_commands(sources: &[PathBuf]) -> (TempDir, PathBuf) {
// If we generate the same path here on every run, then we can't run
// multiple transpiles in parallel, so we need a unique path. But clang
// won't read this file unless it is named exactly "compile_commands.json",
// so we can't change the filename. Instead, create a temporary directory
// with a unique name, and put the file there.
let temp_dir = tempfile::Builder::new()
.prefix("c2rust-")
.tempdir()
.expect("Failed to create temporary directory for compile_commands.json");
let temp_path = temp_dir.path().join("compile_commands.json");

let compile_commands: Vec<CompileCmd> = sources
.iter()
.map(|source_file| {
Expand All @@ -252,8 +263,7 @@ pub fn create_temp_compile_commands(sources: &[PathBuf]) -> PathBuf {
File::create(&temp_path).expect("Failed to create temporary compile_commands.json");
file.write_all(json_content.as_bytes())
.expect("Failed to write to temporary compile_commands.json");

temp_path
(temp_dir, temp_path)
}

/// Main entry point to transpiler. Called from CLI tools with the result of
Expand Down
33 changes: 17 additions & 16 deletions c2rust/src/bin/c2rust-transpile.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::{Parser, ValueEnum};
use log::LevelFilter;
use regex::Regex;
use std::{fs, path::PathBuf};
use std::{ffi::OsStr, fs, path::PathBuf};

use c2rust_transpile::{Diagnostic, ReplaceMode, TranspilerConfig};

Expand Down Expand Up @@ -226,28 +226,29 @@ fn main() {
tcfg.emit_modules = true
};

let mut created_temp_compile_commands = false;
let mut temp_compile_commands_dir = None;

let compile_commands = if args.compile_commands.len() == 1
&& args.compile_commands[0].extension() == Some(std::ffi::OsStr::new("json"))
let compile_commands = if args
.compile_commands
.iter()
.any(|path| path.extension() == Some(OsStr::new("json")))
{
if args.compile_commands.len() != 1 {
// More than one file provided and at least one is a JSON file
panic!("Compile commands JSON and multiple sources provided.
Exactly one compile_commands.json file should be provided, or a list of source files, but not both.");
}
// Only one file provided and it's a JSON file
match fs::canonicalize(&args.compile_commands[0]) {
Ok(canonical_path) => canonical_path,
Err(e) => panic!("Failed to canonicalize path: {:?}", e),
}
} else if args
.compile_commands
.iter()
.any(|path| path.extension() == Some(std::ffi::OsStr::new("json")))
{
// More than one file provided and at least one is a JSON file
panic!("Compile commands JSON and multiple sources provided.
Exactly one compile_commands.json file should be provided, or a list of source files, but not both.");
} else {
// Handle as a list of source files
created_temp_compile_commands = true;
c2rust_transpile::create_temp_compile_commands(&args.compile_commands)
let (temp_dir, temp_path) =
c2rust_transpile::create_temp_compile_commands(&args.compile_commands);
temp_compile_commands_dir = Some(temp_dir);
temp_path
};

let extra_args = args
Expand All @@ -259,8 +260,8 @@ fn main() {
c2rust_transpile::transpile(tcfg, &compile_commands, &extra_args);

// Remove the temporary compile_commands.json if it was created
if created_temp_compile_commands {
std::fs::remove_file(&compile_commands)
if let Some(temp) = temp_compile_commands_dir {
temp.close()
.expect("Failed to remove temporary compile_commands.json");
}
}
Loading