Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 1 | // Copyright 2020 Google LLC |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| 6 | // option. This file may not be copied, modified, or distributed |
| 7 | // except according to those terms. |
| 8 | |
| 9 | #![forbid(unsafe_code)] |
| 10 | |
| 11 | use std::{ |
| 12 | borrow::Cow, |
| 13 | fs::File, |
| 14 | io::Write, |
| 15 | os::unix::prelude::PermissionsExt, |
| 16 | path::{Path, PathBuf}, |
| 17 | }; |
| 18 | |
| 19 | use autocxx_engine::{get_clang_path, make_clang_args, preprocess}; |
| 20 | use autocxx_parser::IncludeCppConfig; |
| 21 | use clap::{crate_authors, crate_version, Arg, ArgMatches, Command}; |
| 22 | use indexmap::IndexSet; |
| 23 | use indoc::indoc; |
| 24 | use itertools::Itertools; |
| 25 | use quote::ToTokens; |
| 26 | use regex::Regex; |
| 27 | use tempfile::TempDir; |
| 28 | |
| 29 | static LONG_HELP: &str = indoc! {" |
| 30 | Command line utility to minimize autocxx bug cases. |
| 31 | |
| 32 | This is a wrapper for creduce. |
| 33 | |
| 34 | Example command-line: |
| 35 | autocxx-reduce file -I my-inc-dir -h my-header -d 'generate!(\"MyClass\")' -k -- --n 64 |
| 36 | "}; |
| 37 | |
| 38 | fn main() { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 39 | // Assemble some defaults for command line arguments |
| 40 | let current_exe = std::env::current_exe().unwrap(); |
| 41 | let our_dir = current_exe.parent().unwrap(); |
| 42 | let default_gen_cmd = our_dir.join("autocxx-gen").to_str().unwrap().to_string(); |
| 43 | let rust_libs_path1 = our_dir.to_str().unwrap().to_string(); |
| 44 | let rust_libs_path2 = our_dir.join("deps").to_str().unwrap().to_string(); |
| 45 | let default_rlibs = &[rust_libs_path1.as_str(), rust_libs_path2.as_str()]; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 46 | let matches = Command::new("autocxx-reduce") |
| 47 | .version(crate_version!()) |
| 48 | .author(crate_authors!()) |
| 49 | .about("Reduce a C++ test case") |
| 50 | .long_about(LONG_HELP) |
| 51 | .subcommand(Command::new("file") |
| 52 | .about("reduce a header file") |
| 53 | |
| 54 | .arg( |
| 55 | Arg::new("inc") |
| 56 | .short('I') |
| 57 | .long("inc") |
| 58 | .multiple_occurrences(true) |
| 59 | .number_of_values(1) |
| 60 | .value_name("INCLUDE DIRS") |
| 61 | .help("include path") |
| 62 | .takes_value(true), |
| 63 | ) |
| 64 | .arg( |
| 65 | Arg::new("define") |
| 66 | .short('D') |
| 67 | .long("define") |
| 68 | .multiple_occurrences(true) |
| 69 | .number_of_values(1) |
| 70 | .value_name("DEFINE") |
| 71 | .help("macro definition") |
| 72 | .takes_value(true), |
| 73 | ) |
| 74 | .arg( |
| 75 | Arg::new("header") |
| 76 | .long("header") |
| 77 | .multiple_occurrences(true) |
| 78 | .number_of_values(1) |
| 79 | .required(true) |
| 80 | .value_name("HEADER") |
| 81 | .help("header file name") |
| 82 | .takes_value(true), |
| 83 | ) |
| 84 | |
| 85 | .arg( |
| 86 | Arg::new("directive") |
| 87 | .short('d') |
| 88 | .long("directive") |
| 89 | .multiple_occurrences(true) |
| 90 | .number_of_values(1) |
| 91 | .value_name("DIRECTIVE") |
| 92 | .help("directives to put within include_cpp!") |
| 93 | .takes_value(true), |
| 94 | ) |
| 95 | ) |
| 96 | .subcommand(Command::new("repro") |
| 97 | .about("reduce a repro case JSON file") |
| 98 | .arg( |
| 99 | Arg::new("repro") |
| 100 | .short('r') |
| 101 | .long("repro") |
| 102 | .required(true) |
| 103 | .value_name("REPRODUCTION CASE JSON") |
| 104 | .help("reproduction case JSON file name") |
| 105 | .takes_value(true), |
| 106 | ) |
| 107 | .arg( |
| 108 | Arg::new("header") |
| 109 | .long("header") |
| 110 | .multiple_occurrences(true) |
| 111 | .number_of_values(1) |
| 112 | .value_name("HEADER") |
| 113 | .help("header file name; specify to resume a part-completed run") |
| 114 | .takes_value(true), |
| 115 | ) |
| 116 | ) |
| 117 | .arg( |
| 118 | Arg::new("problem") |
| 119 | .short('p') |
| 120 | .long("problem") |
| 121 | .required(true) |
| 122 | .value_name("PROBLEM") |
| 123 | .help("problem string we're looking for... may be in logs, or in generated C++, or generated .rs") |
| 124 | .takes_value(true), |
| 125 | ) |
| 126 | .arg( |
| 127 | Arg::new("creduce") |
| 128 | .long("creduce") |
| 129 | .value_name("PATH") |
| 130 | .help("creduce binary location") |
| 131 | .default_value("creduce") |
| 132 | .takes_value(true), |
| 133 | ) |
| 134 | .arg( |
| 135 | Arg::new("output") |
| 136 | .short('o') |
| 137 | .long("output") |
| 138 | .value_name("OUTPUT") |
| 139 | .help("where to write minimized output") |
| 140 | .takes_value(true), |
| 141 | ) |
| 142 | .arg( |
| 143 | Arg::new("gen-cmd") |
| 144 | .short('g') |
| 145 | .long("gen-cmd") |
| 146 | .value_name("GEN-CMD") |
| 147 | .help("where to find autocxx-gen") |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 148 | .default_value(&default_gen_cmd) |
| 149 | .takes_value(true), |
| 150 | ) |
| 151 | .arg( |
| 152 | Arg::new("rustc") |
| 153 | .long("rustc") |
| 154 | .value_name("RUSTC") |
| 155 | .help("where to find rustc") |
| 156 | .default_value("rustc") |
| 157 | .takes_value(true), |
| 158 | ) |
| 159 | .arg( |
| 160 | Arg::new("rlibs") |
| 161 | .long("rlibs") |
| 162 | .value_name("LIBDIR") |
| 163 | .help("where to find rlibs/rmetas for cxx and autocxx") |
| 164 | .default_values(default_rlibs) |
| 165 | .multiple_values(true) |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 166 | .takes_value(true), |
| 167 | ) |
| 168 | .arg( |
| 169 | Arg::new("keep") |
| 170 | .short('k') |
| 171 | .long("keep-dir") |
| 172 | .help("keep the temporary directory for debugging purposes"), |
| 173 | ) |
| 174 | .arg( |
| 175 | Arg::new("clang-args") |
| 176 | .short('c') |
| 177 | .long("clang-arg") |
| 178 | .multiple_occurrences(true) |
| 179 | .value_name("CLANG_ARG") |
| 180 | .help("Extra arguments to pass to Clang"), |
| 181 | ) |
| 182 | .arg( |
| 183 | Arg::new("creduce-args") |
| 184 | .long("creduce-arg") |
| 185 | .multiple_occurrences(true) |
| 186 | .value_name("CREDUCE_ARG") |
| 187 | .help("Extra arguments to pass to Clang"), |
| 188 | ) |
| 189 | .arg( |
| 190 | Arg::new("no-precompile") |
| 191 | .long("no-precompile") |
| 192 | .help("Do not precompile the C++ header before passing to autocxxgen"), |
| 193 | ) |
| 194 | .arg( |
| 195 | Arg::new("no-postcompile") |
| 196 | .long("no-postcompile") |
| 197 | .help("Do not post-compile the C++ generated by autocxxgen"), |
| 198 | ) |
| 199 | .arg( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 200 | Arg::new("no-rustc") |
| 201 | .long("no-rustc") |
| 202 | .help("Do not compile the rust generated by autocxxgen"), |
| 203 | ) |
| 204 | .arg( |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 205 | Arg::new("suppress-cxx-inclusions") |
| 206 | .long("suppress-cxx-inclusions") |
| 207 | .takes_value(true) |
| 208 | .possible_value("yes") |
| 209 | .possible_value("no") |
| 210 | .possible_value("auto") |
| 211 | .default_value("auto") |
| 212 | .help("Whether the preprocessed header already includes cxx.h. If so, we'll try to suppress the natural behavior of cxx to include duplicate definitions of some of the types within gen0.cc.") |
| 213 | ) |
| 214 | .arg_required_else_help(true) |
| 215 | .get_matches(); |
| 216 | run(matches).unwrap(); |
| 217 | } |
| 218 | |
| 219 | fn run(matches: ArgMatches) -> Result<(), std::io::Error> { |
| 220 | let keep_tmp = matches.is_present("keep"); |
| 221 | let tmp_dir = TempDir::new()?; |
| 222 | let r = do_run(matches, &tmp_dir); |
| 223 | if keep_tmp { |
| 224 | println!( |
| 225 | "Keeping temp dir created at: {}", |
| 226 | tmp_dir.into_path().to_str().unwrap() |
| 227 | ); |
| 228 | } |
| 229 | r |
| 230 | } |
| 231 | |
| 232 | #[derive(serde_derive::Deserialize)] |
| 233 | struct ReproCase { |
| 234 | config: String, |
| 235 | header: String, |
| 236 | } |
| 237 | |
| 238 | fn do_run(matches: ArgMatches, tmp_dir: &TempDir) -> Result<(), std::io::Error> { |
| 239 | let rs_path = tmp_dir.path().join("input.rs"); |
| 240 | let concat_path = tmp_dir.path().join("concat.h"); |
| 241 | match matches.subcommand_matches("repro") { |
| 242 | None => { |
| 243 | let submatches = matches.subcommand_matches("file").unwrap(); |
| 244 | let incs: Vec<_> = submatches |
| 245 | .values_of("inc") |
| 246 | .unwrap_or_default() |
| 247 | .map(PathBuf::from) |
| 248 | .collect(); |
| 249 | let defs: Vec<_> = submatches.values_of("define").unwrap_or_default().collect(); |
| 250 | let headers: Vec<_> = submatches.values_of("header").unwrap_or_default().collect(); |
| 251 | assert!(!headers.is_empty()); |
| 252 | let listing_path = tmp_dir.path().join("listing.h"); |
| 253 | create_concatenated_header(&headers, &listing_path)?; |
| 254 | announce_progress(&format!( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 255 | "Preprocessing {listing_path:?} to {concat_path:?}" |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 256 | )); |
| 257 | preprocess(&listing_path, &concat_path, &incs, &defs)?; |
| 258 | let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string()) |
| 259 | .chain( |
| 260 | submatches |
| 261 | .values_of("directive") |
| 262 | .unwrap_or_default() |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 263 | .map(|s| format!("{s}\n")), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 264 | ) |
| 265 | .collect(); |
| 266 | create_rs_file(&rs_path, &directives)?; |
| 267 | } |
| 268 | Some(submatches) => { |
| 269 | let case: ReproCase = serde_json::from_reader(File::open(PathBuf::from( |
| 270 | submatches.value_of("repro").unwrap(), |
| 271 | ))?) |
| 272 | .unwrap(); |
| 273 | // Replace the headers in the config |
| 274 | let mut config: IncludeCppConfig = syn::parse_str(&case.config).unwrap(); |
| 275 | config.replace_included_headers("concat.h"); |
| 276 | create_file( |
| 277 | &rs_path, |
| 278 | &format!("autocxx::include_cpp!({});", config.to_token_stream()), |
| 279 | )?; |
| 280 | if let Some(header) = submatches.value_of("header") { |
| 281 | std::fs::copy(PathBuf::from(header), &concat_path)?; |
| 282 | } else { |
| 283 | create_file(&concat_path, &case.header)? |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | let suppress_cxx_classes = match matches.value_of("suppress-cxx-inclusions").unwrap() { |
| 289 | "yes" => true, |
| 290 | "no" => false, |
| 291 | "auto" => detect_cxx_h(&concat_path)?, |
| 292 | _ => panic!("unexpected value"), |
| 293 | }; |
| 294 | |
| 295 | let cxx_suppressions = if suppress_cxx_classes { |
| 296 | get_cxx_suppressions() |
| 297 | } else { |
| 298 | Vec::new() |
| 299 | }; |
| 300 | |
| 301 | let extra_clang_args: Vec<_> = matches |
| 302 | .values_of("clang-args") |
| 303 | .unwrap_or_default() |
| 304 | .map(Cow::Borrowed) |
| 305 | .chain(cxx_suppressions.into_iter().map(Cow::Owned)) |
| 306 | .collect(); |
| 307 | let extra_clang_args: Vec<&str> = extra_clang_args.iter().map(|s| s.as_ref()).collect_vec(); |
| 308 | |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 309 | let gen_cmd = matches.value_of("gen-cmd").unwrap(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 310 | if !Path::new(gen_cmd).exists() { |
| 311 | panic!( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 312 | "autocxx-gen not found in {gen_cmd}. hint: autocxx-reduce --gen-cmd /path/to/autocxx-gen" |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 313 | ); |
| 314 | } |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 315 | |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 316 | run_sample_gen_cmd(gen_cmd, &rs_path, tmp_dir.path(), &extra_clang_args)?; |
| 317 | // Create and run an interestingness test which does not filter its output through grep. |
| 318 | let demo_interestingness_test_dir = tmp_dir.path().join("demo-interestingness-test"); |
| 319 | std::fs::create_dir(&demo_interestingness_test_dir).unwrap(); |
| 320 | let interestingness_test = demo_interestingness_test_dir.join("test-demo.sh"); |
| 321 | create_interestingness_test( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 322 | &matches, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 323 | gen_cmd, |
| 324 | &interestingness_test, |
| 325 | None, |
| 326 | &rs_path, |
| 327 | &extra_clang_args, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 328 | )?; |
| 329 | let demo_dir_concat_path = demo_interestingness_test_dir.join("concat.h"); |
| 330 | std::fs::copy(&concat_path, demo_dir_concat_path).unwrap(); |
| 331 | run_demo_interestingness_test(&demo_interestingness_test_dir, &interestingness_test).unwrap(); |
| 332 | |
| 333 | // Now the main interestingness test |
| 334 | let interestingness_test = tmp_dir.path().join("test.sh"); |
| 335 | create_interestingness_test( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 336 | &matches, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 337 | gen_cmd, |
| 338 | &interestingness_test, |
| 339 | Some(matches.value_of("problem").unwrap()), |
| 340 | &rs_path, |
| 341 | &extra_clang_args, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 342 | )?; |
| 343 | run_creduce( |
| 344 | matches.value_of("creduce").unwrap(), |
| 345 | &interestingness_test, |
| 346 | &concat_path, |
| 347 | matches.values_of("creduce-args").unwrap_or_default(), |
| 348 | ); |
| 349 | announce_progress("creduce completed"); |
| 350 | let output_path = matches.value_of("output"); |
| 351 | match output_path { |
| 352 | None => print_minimized_case(&concat_path)?, |
| 353 | Some(output_path) => { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 354 | std::fs::copy(&concat_path, PathBuf::from(output_path))?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 355 | } |
| 356 | }; |
| 357 | Ok(()) |
| 358 | } |
| 359 | |
| 360 | /// Try to detect whether the preprocessed source code already contains |
| 361 | /// a preprocessed version of cxx.h. This is hard because all the comments |
| 362 | /// and preprocessor symbols may have been removed, and in fact if we're |
| 363 | /// part way through reduction, parts of the code may have been removed too. |
| 364 | fn detect_cxx_h(concat_path: &Path) -> Result<bool, std::io::Error> { |
| 365 | let haystack = std::fs::read_to_string(concat_path)?; |
| 366 | Ok(["class Box", "class Vec", "class Slice"] |
| 367 | .iter() |
| 368 | .all(|needle| haystack.contains(needle))) |
| 369 | } |
| 370 | |
| 371 | fn announce_progress(msg: &str) { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 372 | println!("=== {msg} ==="); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 373 | } |
| 374 | |
| 375 | fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> { |
| 376 | announce_progress("Completed. Minimized test case:"); |
| 377 | let contents = std::fs::read_to_string(concat_path)?; |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 378 | println!("{contents}"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 379 | Ok(()) |
| 380 | } |
| 381 | |
| 382 | /// Arguments we pass to creduce if supported. This pass always seems to cause a crash |
| 383 | /// as far as I can tell, so always exclude it. It may be environment-dependent, |
| 384 | /// of course, but as I'm the primary user of this tool I am ruthlessly removing it. |
| 385 | const REMOVE_PASS_LINE_MARKERS: &[&str] = &["--remove-pass", "pass_line_markers", "*"]; |
| 386 | const SKIP_INITIAL_PASSES: &[&str] = &["--skip-initial-passes"]; |
| 387 | |
| 388 | fn creduce_supports_remove_pass(creduce_cmd: &str) -> bool { |
| 389 | let cmd = std::process::Command::new(creduce_cmd) |
| 390 | .arg("--help") |
| 391 | .output(); |
| 392 | let msg = match cmd { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 393 | Err(error) => panic!("failed to run creduce. creduce_cmd = {creduce_cmd}. hint: autocxx-reduce --creduce /path/to/creduce. error = {error}"), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 394 | Ok(result) => result.stdout |
| 395 | }; |
| 396 | let msg = std::str::from_utf8(&msg).unwrap(); |
| 397 | msg.contains("--remove-pass") |
| 398 | } |
| 399 | |
| 400 | fn run_creduce<'a>( |
| 401 | creduce_cmd: &str, |
| 402 | interestingness_test: &'a Path, |
| 403 | concat_path: &'a Path, |
| 404 | creduce_args: impl Iterator<Item = &'a str>, |
| 405 | ) { |
| 406 | announce_progress("creduce"); |
| 407 | let args = std::iter::once(interestingness_test.to_str().unwrap()) |
| 408 | .chain(std::iter::once(concat_path.to_str().unwrap())) |
| 409 | .chain(creduce_args) |
| 410 | .chain( |
| 411 | if creduce_supports_remove_pass(creduce_cmd) { |
| 412 | REMOVE_PASS_LINE_MARKERS |
| 413 | } else { |
| 414 | SKIP_INITIAL_PASSES |
| 415 | } |
| 416 | .iter() |
| 417 | .copied(), |
| 418 | ) |
| 419 | .collect::<Vec<_>>(); |
| 420 | println!("Command: {} {}", creduce_cmd, args.join(" ")); |
| 421 | std::process::Command::new(creduce_cmd) |
| 422 | .args(args) |
| 423 | .status() |
| 424 | .expect("failed to creduce"); |
| 425 | } |
| 426 | |
| 427 | fn run_sample_gen_cmd( |
| 428 | gen_cmd: &str, |
| 429 | rs_file: &Path, |
| 430 | tmp_dir: &Path, |
| 431 | extra_clang_args: &[&str], |
| 432 | ) -> Result<(), std::io::Error> { |
| 433 | let args = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap(), extra_clang_args); |
| 434 | let args = args.collect::<Vec<_>>(); |
| 435 | let args_str = args.join(" "); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 436 | announce_progress(&format!("Running sample gen cmd: {gen_cmd} {args_str}")); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 437 | std::process::Command::new(gen_cmd).args(args).status()?; |
| 438 | Ok(()) |
| 439 | } |
| 440 | |
| 441 | fn run_demo_interestingness_test(demo_dir: &Path, test: &Path) -> Result<(), std::io::Error> { |
| 442 | announce_progress(&format!( |
| 443 | "Running demo interestingness test in {}", |
| 444 | demo_dir.to_string_lossy() |
| 445 | )); |
| 446 | std::process::Command::new(test) |
| 447 | .current_dir(demo_dir) |
| 448 | .status()?; |
| 449 | Ok(()) |
| 450 | } |
| 451 | |
| 452 | fn format_gen_cmd<'a>( |
| 453 | rs_file: &Path, |
| 454 | dir: &str, |
| 455 | extra_clang_args: &'a [&str], |
| 456 | ) -> impl Iterator<Item = String> + 'a { |
| 457 | let args = [ |
| 458 | "-o".to_string(), |
| 459 | dir.to_string(), |
| 460 | "-I".to_string(), |
| 461 | dir.to_string(), |
| 462 | rs_file.to_str().unwrap().to_string(), |
| 463 | "--gen-rs-include".to_string(), |
| 464 | "--gen-cpp".to_string(), |
| 465 | "--suppress-system-headers".to_string(), |
| 466 | "--".to_string(), |
| 467 | ] |
| 468 | .to_vec(); |
| 469 | args.into_iter() |
| 470 | .chain(extra_clang_args.iter().map(|s| s.to_string())) |
| 471 | } |
| 472 | |
| 473 | fn create_interestingness_test( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 474 | matches: &ArgMatches, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 475 | gen_cmd: &str, |
| 476 | test_path: &Path, |
| 477 | problem: Option<&str>, |
| 478 | rs_file: &Path, |
| 479 | extra_clang_args: &[&str], |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 480 | ) -> Result<(), std::io::Error> { |
| 481 | announce_progress("Creating interestingness test"); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 482 | let precompile = !matches.is_present("no-precompile"); |
| 483 | let postcompile = !matches.is_present("no-postcompile"); |
| 484 | let rustc = !matches.is_present("no-rustc"); |
| 485 | |
| 486 | let rustc_path = matches.value_of("rustc").unwrap(); |
| 487 | |
| 488 | let rust_libs_path: Vec<String> = matches |
| 489 | .get_many::<String>("rlibs") |
| 490 | .expect("No rlib path specified") |
| 491 | .cloned() |
| 492 | .collect(); |
| 493 | |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 494 | // Ensure we refer to the input header by relative path |
| 495 | // because creduce will invoke us in some other directory with |
| 496 | // a copy thereof. |
| 497 | let mut args = format_gen_cmd(rs_file, "$(pwd)", extra_clang_args); |
| 498 | let args = args.join(" "); |
| 499 | let precompile_step = make_compile_step(precompile, "concat.h", extra_clang_args); |
| 500 | // For the compile afterwards, we have to avoid including any system headers. |
| 501 | // We rely on equivalent content being hermetically inside concat.h. |
| 502 | let postcompile_step = make_compile_step(postcompile, "gen0.cc", extra_clang_args); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 503 | let rustc_step = if rustc { |
| 504 | let rust_libs_path = rust_libs_path.iter().map(|p| format!(" -L{p}")).join(" "); |
| 505 | format!("{rustc_path} --extern cxx --extern autocxx {rust_libs_path} --crate-type rlib --emit=metadata --edition=2021 autocxx-ffi-default-gen.rs 2>&1") |
| 506 | } else { |
| 507 | "echo Skipping rustc".to_string() |
| 508 | }; |
| 509 | // -q below to exit immediately as soon as a match is found, to avoid |
| 510 | // extra compile/codegen steps |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 511 | let problem_grep = problem |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 512 | .map(|problem| format!("| grep -q \"{problem}\" >/dev/null 2>&1")) |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 513 | .unwrap_or_default(); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 514 | // We formerly had a 'trap' below but it seems to have caused problems |
| 515 | // (trap \"if [[ \\$? -eq 139 ]]; then echo Segfault; fi\" CHLD; {} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1 ) {} |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 516 | let content = format!( |
| 517 | indoc! {" |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 518 | #!/bin/bash |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 519 | set -e |
| 520 | echo Precompile |
| 521 | {} |
| 522 | echo Move |
| 523 | mv concat.h concat-body.h |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 524 | (echo \"#ifndef __CONCAT_H__\"; echo \"#define __CONCAT_H__\"; echo '#include \"concat-body.h\"'; echo \"#endif\") > concat.h |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 525 | echo Codegen |
| 526 | ({} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1) {} |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 527 | echo Remove |
| 528 | rm concat.h |
| 529 | echo Swap back |
| 530 | mv concat-body.h concat.h |
| 531 | echo Done |
| 532 | "}, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 533 | precompile_step, gen_cmd, args, rustc_step, postcompile_step, problem_grep |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 534 | ); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 535 | println!("Interestingness test:\n{content}"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 536 | { |
| 537 | let mut file = File::create(test_path)?; |
| 538 | file.write_all(content.as_bytes())?; |
| 539 | } |
| 540 | |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 541 | let mut perms = std::fs::metadata(test_path)?.permissions(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 542 | perms.set_mode(0o700); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 543 | std::fs::set_permissions(test_path, perms)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 544 | Ok(()) |
| 545 | } |
| 546 | |
| 547 | fn make_compile_step(enabled: bool, file: &str, extra_clang_args: &[&str]) -> String { |
| 548 | if enabled { |
| 549 | format!( |
| 550 | "{} {} -c {}", |
| 551 | get_clang_path(), |
| 552 | make_clang_args(&[PathBuf::from(".")], extra_clang_args).join(" "), |
| 553 | file, |
| 554 | ) |
| 555 | } else { |
| 556 | "echo 'Skipping compilation'".into() |
| 557 | } |
| 558 | } |
| 559 | |
| 560 | fn create_rs_file(rs_path: &Path, directives: &[String]) -> Result<(), std::io::Error> { |
| 561 | announce_progress("Creating Rust input file"); |
| 562 | let mut file = File::create(rs_path)?; |
| 563 | file.write_all("use autocxx::include_cpp;\ninclude_cpp! (\n".as_bytes())?; |
| 564 | for directive in directives { |
| 565 | file.write_all(directive.as_bytes())?; |
| 566 | } |
| 567 | file.write_all(");\n".as_bytes())?; |
| 568 | Ok(()) |
| 569 | } |
| 570 | |
| 571 | fn create_concatenated_header(headers: &[&str], listing_path: &Path) -> Result<(), std::io::Error> { |
| 572 | announce_progress("Creating preprocessed header"); |
| 573 | let mut file = File::create(listing_path)?; |
| 574 | for header in headers { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 575 | file.write_all(format!("#include \"{header}\"\n").as_bytes())?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 576 | } |
| 577 | Ok(()) |
| 578 | } |
| 579 | |
| 580 | fn create_file(path: &Path, content: &str) -> Result<(), std::io::Error> { |
| 581 | let mut file = File::create(path)?; |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 582 | write!(file, "{content}")?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 583 | Ok(()) |
| 584 | } |
| 585 | |
| 586 | fn get_cxx_suppressions() -> Vec<String> { |
| 587 | let defines: IndexSet<_> = Regex::new(r"\bCXXBRIDGE1_\w+\b") |
| 588 | .unwrap() |
| 589 | .find_iter(cxx_gen::HEADER) |
| 590 | .map(|m| m.as_str()) |
| 591 | .collect(); // for uniqueness |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 592 | defines.into_iter().map(|def| format!("-D{def}")).collect() |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 593 | } |
| 594 | |
| 595 | #[test] |
| 596 | fn test_get_cxx_suppressions() { |
| 597 | let defines = get_cxx_suppressions(); |
| 598 | assert!(defines.contains(&"-DCXXBRIDGE1_RUST_BITCOPY_T".to_string())); |
| 599 | assert!(defines.contains(&"-DCXXBRIDGE1_RUST_STR".to_string())); |
| 600 | } |