tools/corrosion/generator/src/subcommands/gen_cmake.rs
branchtransitional_engine
changeset 16071 14b83df1832b
parent 16070 fc5258d7ecb2
parent 16067 d903f8d2395a
equal deleted inserted replaced
16070:fc5258d7ecb2 16071:14b83df1832b
     1 use std::{
       
     2     fs::{create_dir_all, File},
       
     3     io::{stdout, Write},
       
     4     path::Path,
       
     5     rc::Rc,
       
     6 };
       
     7 
       
     8 use clap::{App, Arg, ArgMatches, SubCommand};
       
     9 
       
    10 mod target;
       
    11 
       
    12 // Command name
       
    13 pub const GEN_CMAKE: &str = "gen-cmake";
       
    14 
       
    15 // Options
       
    16 const OUT_FILE: &str = "out-file";
       
    17 const CONFIGURATION_ROOT: &str = "configuration-root";
       
    18 const CRATES: &str = "crates";
       
    19 const IMPORTED_CRATES: &str = "imported-crates";
       
    20 const CRATE_TYPE: &str = "crate-type";
       
    21 const PASSTHROUGH_ADD_CARGO_BUILD: &str = "passthrough-acb";
       
    22 
       
    23 pub fn subcommand() -> App<'static, 'static> {
       
    24     SubCommand::with_name(GEN_CMAKE)
       
    25         .arg(
       
    26             Arg::with_name(CONFIGURATION_ROOT)
       
    27                 .long("configuration-root")
       
    28                 .value_name("DIRECTORY")
       
    29                 .takes_value(true)
       
    30                 .help(
       
    31                     "Specifies a root directory for configuration folders. E.g. Win32 \
       
    32                  in VS Generator.",
       
    33                 ),
       
    34         )
       
    35         .arg(
       
    36             Arg::with_name(CRATES)
       
    37                 .long("crates")
       
    38                 .value_name("crates")
       
    39                 .takes_value(true)
       
    40                 .multiple(true)
       
    41                 .require_delimiter(true)
       
    42                 .help("Specifies which crates of the workspace to import"),
       
    43         )
       
    44         .arg(
       
    45             Arg::with_name(CRATE_TYPE)
       
    46                 .long(CRATE_TYPE)
       
    47                 .value_name("kind")
       
    48                 .possible_values(&["staticlib", "cdylib", "bin"])
       
    49                 .multiple(true)
       
    50                 .value_delimiter(";")
       
    51                 .help("Only import the specified crate types")
       
    52         )
       
    53         .arg(
       
    54             Arg::with_name(OUT_FILE)
       
    55                 .short("o")
       
    56                 .long("out-file")
       
    57                 .value_name("FILE")
       
    58                 .help("Output CMake file name. Defaults to stdout."),
       
    59         )
       
    60         .arg(
       
    61             Arg::with_name(IMPORTED_CRATES)
       
    62                 .long(IMPORTED_CRATES)
       
    63                 .value_name("variable_name")
       
    64                 .takes_value(true)
       
    65                 .help("Save a list of the imported target names into c CMake variable with the given name"),
       
    66         )
       
    67         .arg(
       
    68             Arg::with_name(PASSTHROUGH_ADD_CARGO_BUILD)
       
    69                 .long(PASSTHROUGH_ADD_CARGO_BUILD)
       
    70                 .takes_value(true)
       
    71                 .multiple(true)
       
    72                 .value_delimiter(std::char::from_u32(0x1f).unwrap().to_string().as_str())
       
    73                 .help("Passthrough arguments to the _add_cargo_build invocation(s) in CMake")
       
    74         )
       
    75 }
       
    76 
       
    77 pub fn invoke(
       
    78     args: &crate::GeneratorSharedArgs,
       
    79     matches: &ArgMatches,
       
    80 ) -> Result<(), Box<dyn std::error::Error>> {
       
    81     let mut out_file: Box<dyn Write> = if let Some(path) = matches.value_of(OUT_FILE) {
       
    82         let path = Path::new(path);
       
    83         if let Some(parent) = path.parent() {
       
    84             create_dir_all(parent).expect("Failed to create directory!");
       
    85         }
       
    86         let file = File::create(path).expect("Unable to open out-file!");
       
    87         Box::new(file)
       
    88     } else {
       
    89         Box::new(stdout())
       
    90     };
       
    91 
       
    92     writeln!(
       
    93         out_file,
       
    94         "\
       
    95 cmake_minimum_required(VERSION 3.15)
       
    96 "
       
    97     )?;
       
    98 
       
    99     let crates = matches
       
   100         .values_of(CRATES)
       
   101         .map_or(Vec::new(), |c| c.collect());
       
   102     let crate_kinds: Option<Vec<&str>> = matches.values_of(CRATE_TYPE).map(|c| c.collect());
       
   103     let workspace_manifest_path = Rc::new(args.manifest_path.clone());
       
   104     let targets: Vec<_> = args
       
   105         .metadata
       
   106         .packages
       
   107         .iter()
       
   108         .filter(|p| {
       
   109             args.metadata.workspace_members.contains(&p.id)
       
   110                 && (crates.is_empty() || crates.contains(&p.name.as_str()))
       
   111         })
       
   112         .cloned()
       
   113         .map(Rc::new)
       
   114         .flat_map(|package| {
       
   115             package
       
   116                 .targets
       
   117                 .iter()
       
   118                 .filter_map(|t| {
       
   119                     target::CargoTarget::from_metadata(
       
   120                         package.clone(),
       
   121                         t.clone(),
       
   122                         workspace_manifest_path.clone(),
       
   123                         &crate_kinds,
       
   124                     )
       
   125                 })
       
   126                 .collect::<Vec<_>>()
       
   127         })
       
   128         .collect();
       
   129 
       
   130     let passthrough_args: Vec<String> = matches
       
   131         .values_of(PASSTHROUGH_ADD_CARGO_BUILD)
       
   132         .map(|values| {
       
   133             // Add quotes around each argument for CMake to preserve which arguments belong together.
       
   134             values
       
   135                 .filter(|val| !val.is_empty())
       
   136                 .map(|val| format!("\"{}\"", val))
       
   137                 .collect()
       
   138         })
       
   139         .unwrap_or_default();
       
   140     let passthrough_str = passthrough_args.join(" ");
       
   141 
       
   142     for target in &targets {
       
   143         target
       
   144             .emit_cmake_target(&mut out_file, &passthrough_str)
       
   145             .unwrap();
       
   146     }
       
   147     if let Some(imported_crate_list_name) = matches.value_of(IMPORTED_CRATES) {
       
   148         let imported_targets: Vec<_> = targets.iter().map(|target| target.target_name()).collect();
       
   149         let imported_targets_list = imported_targets.join(";");
       
   150         writeln!(
       
   151             out_file,
       
   152             "set({} \"{}\")",
       
   153             imported_crate_list_name, imported_targets_list
       
   154         )?;
       
   155     }
       
   156 
       
   157     writeln!(out_file)?;
       
   158 
       
   159     std::process::exit(0);
       
   160 }