@@ -14,19 +14,20 @@ pub mod with_stmts;
1414
1515use std:: collections:: HashSet ;
1616use std:: fs:: { self , File } ;
17- use std:: io:: prelude :: * ;
17+ use std:: io:: Write ;
1818use std:: path:: { Path , PathBuf } ;
1919use std:: process:: Command ;
2020use std:: { env, io} ;
2121
2222use crate :: compile_cmds:: CompileCmd ;
2323use c2rust_rust_tools:: rustfmt;
24- use failure:: Error ;
24+ use failure:: { format_err , Error } ;
2525use itertools:: Itertools ;
2626use log:: { info, warn} ;
2727use regex:: Regex ;
2828use serde_derive:: Serialize ;
2929pub use tempfile:: TempDir ;
30+ use which:: which;
3031
3132use crate :: c_ast:: Printer ;
3233use crate :: c_ast:: * ;
@@ -106,6 +107,9 @@ pub struct TranspilerConfig {
106107 pub preserve_unused_functions : bool ,
107108 pub log_level : log:: LevelFilter ,
108109
110+ /// Run `c2rust-postprocess` after transpiling and potentially refactoring.
111+ pub postprocess : bool ,
112+
109113 // Options that control build files
110114 /// Emit `Cargo.toml` and `lib.rs`
111115 pub emit_build_files : bool ,
@@ -438,8 +442,10 @@ pub fn transpile(tcfg: TranspilerConfig, cc_db: &Path, extra_clang_args: &[&str]
438442 top_level_ccfg = Some ( ccfg) ;
439443 } else {
440444 let crate_file = emit_build_files ( & tcfg, & build_dir, Some ( ccfg) , None ) ;
445+ let crate_file = crate_file. as_deref ( ) ;
441446 reorganize_definitions ( & tcfg, & build_dir, crate_file)
442447 . unwrap_or_else ( |e| warn ! ( "Reorganizing definitions failed: {}" , e) ) ;
448+ run_postprocess ( & tcfg, & build_dir, crate_file) . unwrap_or_else ( |e| warn ! ( "{e}" ) ) ;
443449 workspace_members. push ( lcmd_name) ;
444450 }
445451 }
@@ -453,8 +459,10 @@ pub fn transpile(tcfg: TranspilerConfig, cc_db: &Path, extra_clang_args: &[&str]
453459 if tcfg. emit_build_files {
454460 let crate_file =
455461 emit_build_files ( & tcfg, & build_dir, top_level_ccfg, Some ( workspace_members) ) ;
462+ let crate_file = crate_file. as_deref ( ) ;
456463 reorganize_definitions ( & tcfg, & build_dir, crate_file)
457464 . unwrap_or_else ( |e| warn ! ( "Reorganizing definitions failed: {}" , e) ) ;
465+ run_postprocess ( & tcfg, & build_dir, crate_file) . unwrap_or_else ( |e| warn ! ( "{e}" ) ) ;
458466 }
459467
460468 tcfg. check_if_all_binaries_used ( & transpiled_modules) ;
@@ -535,7 +543,7 @@ fn invoke_refactor(build_dir: &Path) -> Result<(), Error> {
535543fn reorganize_definitions (
536544 tcfg : & TranspilerConfig ,
537545 build_dir : & Path ,
538- crate_file : Option < PathBuf > ,
546+ crate_file : Option < & Path > ,
539547) -> Result < ( ) , Error > {
540548 // We only run the reorganization refactoring if we emitted a fresh crate file
541549 if crate_file. is_none ( ) || tcfg. disable_refactoring || !tcfg. reorganize_definitions {
@@ -558,7 +566,80 @@ fn reorganize_definitions(
558566 Ok ( ( ) )
559567}
560568
561- /// Transpile one input C file, writing transpilation output to the filesystem.
569+ /// Invokes `c2rust-postprocess`.
570+ ///
571+ /// This assumes the subcommand executable is either in `$PATH`
572+ /// or in the same relative directory as it is in the repo
573+ /// and the current executable is in `target/$profile/`.
574+ fn invoke_postprocess ( crate_file : & Path , build_dir : & Path ) -> Result < ( ) , Error > {
575+ let subcommand = "c2rust-postprocess" ;
576+ let subcommand_path_buf;
577+ let subcommand_path = match which ( subcommand) {
578+ Ok ( _) => Path :: new ( subcommand) ,
579+ Err ( _) => {
580+ let current_exe = env:: current_exe ( ) ?;
581+ let current_exe_dir = current_exe. parent ( ) ;
582+ let current_exe = current_exe. display ( ) ;
583+ let target_dir = current_exe_dir
584+ . ok_or_else ( || format_err ! ( "no parent of {current_exe}" ) ) ?
585+ . parent ( )
586+ . ok_or_else ( || format_err ! ( "no grandparent of {current_exe}" ) ) ?;
587+ let target_dir_name = target_dir
588+ . file_name ( )
589+ . unwrap_or_default ( )
590+ . to_str ( )
591+ . unwrap_or_default ( ) ;
592+ if target_dir_name != "target" {
593+ Err ( format_err ! (
594+ "{current_exe} not in `target/$profile/` and {subcommand} not in $PATH"
595+ ) ) ?;
596+ }
597+ let repo_root = target_dir
598+ . parent ( )
599+ . ok_or_else ( || format_err ! ( "no repo root ancestor of {current_exe}" ) ) ?;
600+ subcommand_path_buf = repo_root. join ( subcommand) . join ( subcommand) ;
601+ & subcommand_path_buf
602+ }
603+ } ;
604+
605+ let mut cmd = Command :: new ( subcommand_path) ;
606+ cmd. arg ( "--update-rust" )
607+ . arg ( crate_file)
608+ . current_dir ( build_dir) ;
609+ let status = cmd
610+ . status ( )
611+ . map_err ( |e| {
612+ let path = subcommand_path. display ( ) ;
613+ failure:: format_err!( "unable to run {path}: {e}\n Note that {subcommand} must be installed separately from c2rust and c2rust-transpile.\
614+ It must be either installed in $PATH or c2rust/c2rust-transpile must be in `target/$profile/` from the c2rust repo.")
615+ } ) ?;
616+
617+ if !status. success ( ) {
618+ Err ( format_err ! ( "postprocess failed: {cmd:?}" ) ) ?;
619+ }
620+
621+ Ok ( ( ) )
622+ }
623+
624+ fn run_postprocess (
625+ tcfg : & TranspilerConfig ,
626+ build_dir : & Path ,
627+ crate_file : Option < & Path > ,
628+ ) -> Result < ( ) , Error > {
629+ let crate_file = match crate_file {
630+ Some ( crate_file) => crate_file,
631+ None => return Ok ( ( ) ) ,
632+ } ;
633+ if !tcfg. postprocess {
634+ return Ok ( ( ) ) ;
635+ }
636+
637+ invoke_postprocess ( crate_file, build_dir) ?;
638+
639+ Ok ( ( ) )
640+ }
641+
642+ /// Transpiles one input C file, writing transpilation output to the filesystem.
562643fn transpile_single (
563644 tcfg : & TranspilerConfig ,
564645 input_path : & Path ,
0 commit comments