blob: d585deec26602a5540afe398145e5c59a2f7cd18 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// 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
11use std::{
12 borrow::Cow,
13 fs::File,
14 io::Write,
15 os::unix::prelude::PermissionsExt,
16 path::{Path, PathBuf},
17};
18
19use autocxx_engine::{get_clang_path, make_clang_args, preprocess};
20use autocxx_parser::IncludeCppConfig;
21use clap::{crate_authors, crate_version, Arg, ArgMatches, Command};
22use indexmap::IndexSet;
23use indoc::indoc;
24use itertools::Itertools;
25use quote::ToTokens;
26use regex::Regex;
27use tempfile::TempDir;
28
29static LONG_HELP: &str = indoc! {"
30Command line utility to minimize autocxx bug cases.
31
32This is a wrapper for creduce.
33
34Example command-line:
35autocxx-reduce file -I my-inc-dir -h my-header -d 'generate!(\"MyClass\")' -k -- --n 64
36"};
37
38fn main() {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070039 // 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 Silverman4e662aa2022-05-11 23:10:19 -070046 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700148 .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 Silverman4e662aa2022-05-11 23:10:19 -0700166 .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 Schuh6ea9bfa2023-08-06 19:05:10 -0700200 Arg::new("no-rustc")
201 .long("no-rustc")
202 .help("Do not compile the rust generated by autocxxgen"),
203 )
204 .arg(
Brian Silverman4e662aa2022-05-11 23:10:19 -0700205 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
219fn 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)]
233struct ReproCase {
234 config: String,
235 header: String,
236}
237
238fn 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700255 "Preprocessing {listing_path:?} to {concat_path:?}"
Brian Silverman4e662aa2022-05-11 23:10:19 -0700256 ));
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 Schuh6ea9bfa2023-08-06 19:05:10 -0700263 .map(|s| format!("{s}\n")),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700264 )
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 Schuh6ea9bfa2023-08-06 19:05:10 -0700309 let gen_cmd = matches.value_of("gen-cmd").unwrap();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700310 if !Path::new(gen_cmd).exists() {
311 panic!(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700312 "autocxx-gen not found in {gen_cmd}. hint: autocxx-reduce --gen-cmd /path/to/autocxx-gen"
Brian Silverman4e662aa2022-05-11 23:10:19 -0700313 );
314 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700315
Brian Silverman4e662aa2022-05-11 23:10:19 -0700316 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700322 &matches,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700323 gen_cmd,
324 &interestingness_test,
325 None,
326 &rs_path,
327 &extra_clang_args,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700328 )?;
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 Schuh6ea9bfa2023-08-06 19:05:10 -0700336 &matches,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700337 gen_cmd,
338 &interestingness_test,
339 Some(matches.value_of("problem").unwrap()),
340 &rs_path,
341 &extra_clang_args,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700342 )?;
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 Schuh6ea9bfa2023-08-06 19:05:10 -0700354 std::fs::copy(&concat_path, PathBuf::from(output_path))?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700355 }
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.
364fn 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
371fn announce_progress(msg: &str) {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700372 println!("=== {msg} ===");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700373}
374
375fn 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700378 println!("{contents}");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700379 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.
385const REMOVE_PASS_LINE_MARKERS: &[&str] = &["--remove-pass", "pass_line_markers", "*"];
386const SKIP_INITIAL_PASSES: &[&str] = &["--skip-initial-passes"];
387
388fn 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700393 Err(error) => panic!("failed to run creduce. creduce_cmd = {creduce_cmd}. hint: autocxx-reduce --creduce /path/to/creduce. error = {error}"),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700394 Ok(result) => result.stdout
395 };
396 let msg = std::str::from_utf8(&msg).unwrap();
397 msg.contains("--remove-pass")
398}
399
400fn 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
427fn 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700436 announce_progress(&format!("Running sample gen cmd: {gen_cmd} {args_str}"));
Brian Silverman4e662aa2022-05-11 23:10:19 -0700437 std::process::Command::new(gen_cmd).args(args).status()?;
438 Ok(())
439}
440
441fn 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
452fn 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
473fn create_interestingness_test(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700474 matches: &ArgMatches,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700475 gen_cmd: &str,
476 test_path: &Path,
477 problem: Option<&str>,
478 rs_file: &Path,
479 extra_clang_args: &[&str],
Brian Silverman4e662aa2022-05-11 23:10:19 -0700480) -> Result<(), std::io::Error> {
481 announce_progress("Creating interestingness test");
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700482 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 Silverman4e662aa2022-05-11 23:10:19 -0700494 // 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700503 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 Silverman4e662aa2022-05-11 23:10:19 -0700511 let problem_grep = problem
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700512 .map(|problem| format!("| grep -q \"{problem}\" >/dev/null 2>&1"))
Brian Silverman4e662aa2022-05-11 23:10:19 -0700513 .unwrap_or_default();
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700514 // 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 Silverman4e662aa2022-05-11 23:10:19 -0700516 let content = format!(
517 indoc! {"
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700518 #!/bin/bash
Brian Silverman4e662aa2022-05-11 23:10:19 -0700519 set -e
520 echo Precompile
521 {}
522 echo Move
523 mv concat.h concat-body.h
Brian Silverman4e662aa2022-05-11 23:10:19 -0700524 (echo \"#ifndef __CONCAT_H__\"; echo \"#define __CONCAT_H__\"; echo '#include \"concat-body.h\"'; echo \"#endif\") > concat.h
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700525 echo Codegen
526 ({} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1) {}
Brian Silverman4e662aa2022-05-11 23:10:19 -0700527 echo Remove
528 rm concat.h
529 echo Swap back
530 mv concat-body.h concat.h
531 echo Done
532 "},
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700533 precompile_step, gen_cmd, args, rustc_step, postcompile_step, problem_grep
Brian Silverman4e662aa2022-05-11 23:10:19 -0700534 );
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700535 println!("Interestingness test:\n{content}");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700536 {
537 let mut file = File::create(test_path)?;
538 file.write_all(content.as_bytes())?;
539 }
540
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700541 let mut perms = std::fs::metadata(test_path)?.permissions();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700542 perms.set_mode(0o700);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700543 std::fs::set_permissions(test_path, perms)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700544 Ok(())
545}
546
547fn 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
560fn 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
571fn 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700575 file.write_all(format!("#include \"{header}\"\n").as_bytes())?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700576 }
577 Ok(())
578}
579
580fn create_file(path: &Path, content: &str) -> Result<(), std::io::Error> {
581 let mut file = File::create(path)?;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700582 write!(file, "{content}")?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700583 Ok(())
584}
585
586fn 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 Schuh6ea9bfa2023-08-06 19:05:10 -0700592 defines.into_iter().map(|def| format!("-D{def}")).collect()
Brian Silverman4e662aa2022-05-11 23:10:19 -0700593}
594
595#[test]
596fn 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}