Skip to content
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

support c2rust <path/to/*.c> in lieu of compile_commands.json #1037

Merged
merged 16 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 13 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
12 changes: 6 additions & 6 deletions c2rust-transpile/src/compile_cmds/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use std::rc::Rc;
use failure::Error;
use log::warn;
use regex::Regex;
use serde_derive::Deserialize;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Default, Clone)]
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
pub struct CompileCmd {
/// The working directory of the compilation. All paths specified in the command
/// or file fields must be either absolute or relative to this directory.
directory: PathBuf,
pub directory: PathBuf,
/// The main translation unit source processed by this compilation step. This is
/// used by tools as the key into the compilation database. There can be multiple
/// command objects for the same file, for example if the same source file is compiled
Expand All @@ -23,13 +23,13 @@ pub struct CompileCmd {
/// the build system uses. Parameters use shell quoting and shell escaping of quotes,
/// with ‘"’ and ‘\’ being the only special characters. Shell expansion is not supported.
#[serde(skip_deserializing)]
_command: Option<String>,
pub command: Option<String>,
/// The compile command executed as list of strings. Either arguments or command is required.
#[serde(default, skip_deserializing)]
_arguments: Vec<String>,
pub arguments: Vec<String>,
/// The name of the output created by this compilation step. This field is optional. It can
/// be used to distinguish different processing modes of the same input file.
output: Option<String>,
pub output: Option<String>,
}

impl CompileCmd {
Expand Down
39 changes: 36 additions & 3 deletions c2rust-transpile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process;

use crate::compile_cmds::CompileCmd;
use failure::Error;
use itertools::Itertools;
use log::{info, warn};
Expand Down Expand Up @@ -225,12 +226,44 @@ 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");
let compile_commands: Vec<CompileCmd> = sources
.iter()
.map(|source_file| {
let absolute_path = fs::canonicalize(source_file)
.unwrap_or_else(|_| panic!("Could not canonicalize {}", source_file.display()));

CompileCmd {
directory: PathBuf::from("."),
file: absolute_path.clone(),
arguments: vec![
"clang".to_string(),
absolute_path.to_str().unwrap().to_owned(),
],
command: None,
output: None,
}
})
.collect();

let json_content = serde_json::to_string(&compile_commands).unwrap();
let mut file =
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
}

/// Main entry point to transpiler. Called from CLI tools with the result of
/// clap::App::get_matches().
pub fn transpile(tcfg: TranspilerConfig, cc_db: &Path, extra_clang_args: &[&str]) {
diagnostics::init(tcfg.enabled_warnings.clone(), tcfg.log_level);

let lcmds = get_compile_commands(cc_db, &tcfg.filter).unwrap_or_else(|_| {
let build_dir = get_build_dir(&tcfg, cc_db);

let lcmds = get_compile_commands(&cc_db, &tcfg.filter).unwrap_or_else(|_| {
panic!(
"Could not parse compile commands from {}",
cc_db.to_string_lossy()
Expand All @@ -246,7 +279,7 @@ pub fn transpile(tcfg: TranspilerConfig, cc_db: &Path, extra_clang_args: &[&str]
let mut workspace_members = vec![];
let mut num_transpiled_files = 0;
let mut transpiled_modules = Vec::new();
let build_dir = get_build_dir(&tcfg, cc_db);

for lcmd in &lcmds {
let cmds = &lcmd.cmd_inputs;
let lcmd_name = lcmd
Expand Down Expand Up @@ -297,7 +330,7 @@ pub fn transpile(tcfg: TranspilerConfig, cc_db: &Path, extra_clang_args: &[&str]
cmd.abs_file(),
&ancestor_path,
&build_dir,
cc_db,
&cc_db,
&clang_args,
)
})
Expand Down
42 changes: 29 additions & 13 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::path::{Path, PathBuf};
use std::path::PathBuf;

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

Expand Down Expand Up @@ -84,9 +84,9 @@ struct Args {
#[clap(long = "ddebug-labels")]
debug_labels: bool,

/// Input compile_commands.json file
#[clap()]
compile_commands: PathBuf,
/// Path to compile_commands.json, or a list of source files
#[clap(parse(from_os_str), multiple_values = true)]
compile_commands: Vec<PathBuf>,

/// How to handle violated invariants or invalid code
#[clap(long, value_enum, default_value_t = InvalidCodes::CompileError)]
Expand Down Expand Up @@ -129,7 +129,7 @@ struct Args {
reorganize_definitions: bool,

/// Extra arguments to pass to clang frontend during parsing the input C file
#[clap(multiple = true)]
#[clap(multiple = true, last(true))]
extra_clang_args: Vec<String>,

/// Enable the specified warning (all enables all warnings)
Expand Down Expand Up @@ -226,19 +226,35 @@ fn main() {
tcfg.emit_modules = true
};

let cc_json_path = Path::new(&args.compile_commands);
let cc_json_path = cc_json_path.canonicalize().unwrap_or_else(|_| {
panic!(
"Could not find compile_commands.json file at path: {}",
cc_json_path.display()
)
});
let compile_commands = if args.compile_commands.len() == 1
&& args.compile_commands[0].extension() == Some(std::ffi::OsStr::new("json"))
{
// Only one file provided and it's a compile commands JSON file
args.compile_commands[0].clone()
aneksteind marked this conversation as resolved.
Show resolved Hide resolved
} 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
c2rust_transpile::create_temp_compile_commands(&args.compile_commands)
};

let extra_args = args
.extra_clang_args
.iter()
.map(AsRef::as_ref)
.collect::<Vec<_>>();

c2rust_transpile::transpile(tcfg, &cc_json_path, &extra_args);
c2rust_transpile::transpile(tcfg, &compile_commands, &extra_args);

// Remove the temporary compile_commands.json if it was created
if args.compile_commands.len() > 0 {
std::fs::remove_file(&compile_commands)
.expect("Failed to remove temporary compile_commands.json");
}
}
Loading