Squashed 'third_party/autocxx/' content from commit 629e8fa53

git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/engine/src/builder.rs b/engine/src/builder.rs
new file mode 100644
index 0000000..d0b5199
--- /dev/null
+++ b/engine/src/builder.rs
@@ -0,0 +1,301 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx_parser::file_locations::FileLocationStrategy;
+use miette::Diagnostic;
+use thiserror::Error;
+
+use crate::generate_rs_single;
+use crate::{strip_system_headers, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
+use std::ffi::OsStr;
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::Write;
+use std::marker::PhantomData;
+use std::path::{Path, PathBuf};
+
+/// Errors returned during creation of a [`cc::Build`] from an include_cxx
+/// macro.
+#[derive(Error, Diagnostic, Debug)]
+#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
+pub enum BuilderError {
+    #[error("cxx couldn't handle our generated bindings - could be a bug in autocxx: {0}")]
+    InvalidCxx(cxx_gen::Error),
+    #[error(transparent)]
+    #[diagnostic(transparent)]
+    ParseError(ParseError),
+    #[error("we couldn't write the generated code to disk at {1}: {0}")]
+    FileWriteFail(std::io::Error, PathBuf),
+    #[error("no include_cpp! macro was found")]
+    NoIncludeCxxMacrosFound,
+    #[error("could not create a directory {1}: {0}")]
+    UnableToCreateDirectory(std::io::Error, PathBuf),
+}
+
+#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
+pub type BuilderBuild = cc::Build;
+
+/// For test purposes only, a [`cc::Build`] and lists of Rust and C++
+/// files generated.
+#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
+pub struct BuilderSuccess(pub BuilderBuild, pub Vec<PathBuf>, pub Vec<PathBuf>);
+
+/// Results of a build.
+#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
+pub type BuilderResult = Result<BuilderSuccess, BuilderError>;
+
+/// The context in which a builder object lives. Callbacks for various
+/// purposes.
+#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
+pub trait BuilderContext {
+    /// Perform any initialization specific to the context in which this
+    /// builder lives.
+    fn setup() {}
+
+    /// Create a dependency recorder, if any.
+    fn get_dependency_recorder() -> Option<Box<dyn RebuildDependencyRecorder>>;
+}
+
+/// An object to allow building of bindings from a `build.rs` file.
+///
+/// It would be unusual to use this directly - see the `autocxx_build` or
+/// `autocxx_gen` crates.
+#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
+pub struct Builder<'a, BuilderContext> {
+    rs_file: PathBuf,
+    autocxx_incs: Vec<OsString>,
+    extra_clang_args: Vec<String>,
+    dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
+    custom_gendir: Option<PathBuf>,
+    auto_allowlist: bool,
+    cpp_codegen_options: CppCodegenOptions<'a>,
+    // This member is to ensure that this type is parameterized
+    // by a BuilderContext. The goal is to balance three needs:
+    // (1) have most of the functionality over in autocxx_engine,
+    // (2) expose this type to users of autocxx_build and to
+    //     make it easy for callers simply to call Builder::new,
+    // (3) ensure that such a Builder does a few tasks specific to its use
+    // in a cargo environment.
+    ctx: PhantomData<BuilderContext>,
+}
+
+impl<CTX: BuilderContext> Builder<'_, CTX> {
+    /// Create a new Builder object. You'll need to pass in the Rust file
+    /// which contains the bindings (typically an `include_cpp!` macro
+    /// though `autocxx` can also handle manually-crafted `cxx::bridge`
+    /// bindings), and a list of include directories which should be searched
+    /// by autocxx as it tries to hunt for the include files specified
+    /// within the `include_cpp!` macro.
+    ///
+    /// Usually after this you'd call [`build`].
+    pub fn new(
+        rs_file: impl AsRef<Path>,
+        autocxx_incs: impl IntoIterator<Item = impl AsRef<OsStr>>,
+    ) -> Self {
+        CTX::setup();
+        Self {
+            rs_file: rs_file.as_ref().to_path_buf(),
+            autocxx_incs: autocxx_incs
+                .into_iter()
+                .map(|s| s.as_ref().to_os_string())
+                .collect(),
+            extra_clang_args: Vec::new(),
+            dependency_recorder: CTX::get_dependency_recorder(),
+            custom_gendir: None,
+            auto_allowlist: false,
+            cpp_codegen_options: CppCodegenOptions::default(),
+            ctx: PhantomData,
+        }
+    }
+
+    /// Specify extra arguments for clang.
+    pub fn extra_clang_args(mut self, extra_clang_args: &[&str]) -> Self {
+        self.extra_clang_args = extra_clang_args.iter().map(|s| s.to_string()).collect();
+        self
+    }
+
+    /// Where to generate the code.
+    pub fn custom_gendir(mut self, custom_gendir: PathBuf) -> Self {
+        self.custom_gendir = Some(custom_gendir);
+        self
+    }
+
+    /// Update C++ code generation options. See [`CppCodegenOptions`] for details.
+    pub fn cpp_codegen_options<F>(mut self, modifier: F) -> Self
+    where
+        F: FnOnce(&mut CppCodegenOptions),
+    {
+        modifier(&mut self.cpp_codegen_options);
+        self
+    }
+
+    /// Automatically discover uses of the C++ `ffi` mod and generate the allowlist
+    /// from that.
+    /// This is a highly experimental option, not currently recommended.
+    /// It doesn't work in the following cases:
+    /// * Static function calls on types within the FFI mod.
+    /// * Anything inside a macro invocation.
+    /// * You're using a different name for your `ffi` mod
+    /// * You're using multiple FFI mods
+    /// * You've got usages scattered across files beyond that with the
+    ///   `include_cpp` invocation
+    /// * You're using `use` statements to rename mods or items. If this
+    /// proves to be a promising or helpful direction, autocxx would be happy
+    /// to accept pull requests to remove some of these limitations.
+    pub fn auto_allowlist(mut self, do_it: bool) -> Self {
+        self.auto_allowlist = do_it;
+        self
+    }
+
+    /// Whether to suppress inclusion of system headers (`memory`, `string` etc.)
+    /// from generated C++ bindings code. This should not normally be used,
+    /// but can occasionally be useful if you're reducing a test case and you
+    /// have a preprocessed header file which already contains absolutely everything
+    /// that the bindings could ever need.
+    pub fn suppress_system_headers(mut self, do_it: bool) -> Self {
+        self.cpp_codegen_options.suppress_system_headers = do_it;
+        self
+    }
+
+    /// An annotation optionally to include on each C++ function.
+    /// For example to export the symbol from a library.
+    pub fn cxx_impl_annotations(mut self, cxx_impl_annotations: Option<String>) -> Self {
+        self.cpp_codegen_options.cxx_impl_annotations = cxx_impl_annotations;
+        self
+    }
+
+    /// Build autocxx C++ files and return a [`cc::Build`] you can use to build
+    /// more from a build.rs file.
+    ///
+    /// The error type returned by this function supports [`miette::Diagnostic`],
+    /// so if you use the `miette` crate and its `fancy` feature, then simply
+    /// return a `miette::Result` from your main function, you should get nicely
+    /// printed diagnostics.
+    pub fn build(self) -> Result<BuilderBuild, BuilderError> {
+        self.build_listing_files().map(|r| r.0)
+    }
+
+    /// For use in tests only, this does the build and returns additional information
+    /// about the files generated which can subsequently be examined for correctness.
+    /// In production, please use simply [`build`].
+    pub fn build_listing_files(self) -> Result<BuilderSuccess, BuilderError> {
+        let clang_args = &self
+            .extra_clang_args
+            .iter()
+            .map(|s| &s[..])
+            .collect::<Vec<_>>();
+        rust_version_check();
+        let gen_location_strategy = match self.custom_gendir {
+            None => FileLocationStrategy::new(),
+            Some(custom_dir) => FileLocationStrategy::Custom(custom_dir),
+        };
+        let incdir = gen_location_strategy.get_include_dir();
+        ensure_created(&incdir)?;
+        let cxxdir = gen_location_strategy.get_cxx_dir();
+        ensure_created(&cxxdir)?;
+        let rsdir = gen_location_strategy.get_rs_dir();
+        ensure_created(&rsdir)?;
+        // We are incredibly unsophisticated in our directory arrangement here
+        // compared to cxx. I have no doubt that we will need to replicate just
+        // about everything cxx does, in due course...
+        // Write cxx.h to that location, as it may be needed by
+        // some of our generated code.
+        write_to_file(
+            &incdir,
+            "cxx.h",
+            &Self::get_cxx_header_bytes(self.cpp_codegen_options.suppress_system_headers),
+        )?;
+
+        let autocxx_inc = build_autocxx_inc(self.autocxx_incs, &incdir);
+        gen_location_strategy.set_cargo_env_vars_for_build();
+
+        let mut parsed_file = crate::parse_file(self.rs_file, self.auto_allowlist)
+            .map_err(BuilderError::ParseError)?;
+        parsed_file
+            .resolve_all(
+                autocxx_inc,
+                clang_args,
+                self.dependency_recorder,
+                &self.cpp_codegen_options,
+            )
+            .map_err(BuilderError::ParseError)?;
+        let mut counter = 0;
+        let mut builder = cc::Build::new();
+        builder.cpp(true);
+        if std::env::var_os("AUTOCXX_ASAN").is_some() {
+            builder.flag_if_supported("-fsanitize=address");
+        }
+        let mut generated_rs = Vec::new();
+        let mut generated_cpp = Vec::new();
+        builder.includes(parsed_file.include_dirs());
+        for include_cpp in parsed_file.get_cpp_buildables() {
+            let generated_code = include_cpp
+                .generate_h_and_cxx(&self.cpp_codegen_options)
+                .map_err(BuilderError::InvalidCxx)?;
+            for filepair in generated_code.0 {
+                let fname = format!("gen{}.cxx", counter);
+                counter += 1;
+                if let Some(implementation) = &filepair.implementation {
+                    let gen_cxx_path = write_to_file(&cxxdir, &fname, implementation)?;
+                    builder.file(&gen_cxx_path);
+                    generated_cpp.push(gen_cxx_path);
+                }
+                write_to_file(&incdir, &filepair.header_name, &filepair.header)?;
+                generated_cpp.push(incdir.join(filepair.header_name));
+            }
+        }
+
+        for rs_output in parsed_file.get_rs_outputs() {
+            let rs = generate_rs_single(rs_output);
+            generated_rs.push(write_to_file(&rsdir, &rs.filename, rs.code.as_bytes())?);
+        }
+        if counter == 0 {
+            Err(BuilderError::NoIncludeCxxMacrosFound)
+        } else {
+            Ok(BuilderSuccess(builder, generated_rs, generated_cpp))
+        }
+    }
+
+    fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
+        strip_system_headers(crate::HEADER.as_bytes().to_vec(), suppress_system_headers)
+    }
+}
+
+fn ensure_created(dir: &Path) -> Result<(), BuilderError> {
+    std::fs::create_dir_all(dir)
+        .map_err(|e| BuilderError::UnableToCreateDirectory(e, dir.to_path_buf()))
+}
+
+fn build_autocxx_inc<I, T>(paths: I, extra_path: &Path) -> Vec<PathBuf>
+where
+    I: IntoIterator<Item = T>,
+    T: AsRef<OsStr>,
+{
+    paths
+        .into_iter()
+        .map(|p| PathBuf::from(p.as_ref()))
+        .chain(std::iter::once(extra_path.to_path_buf()))
+        .collect()
+}
+
+fn write_to_file(dir: &Path, filename: &str, content: &[u8]) -> Result<PathBuf, BuilderError> {
+    let path = dir.join(filename);
+    try_write_to_file(&path, content).map_err(|e| BuilderError::FileWriteFail(e, path.clone()))?;
+    Ok(path)
+}
+
+fn try_write_to_file(path: &Path, content: &[u8]) -> std::io::Result<()> {
+    let mut f = File::create(path)?;
+    f.write_all(content)
+}
+
+fn rust_version_check() {
+    if !version_check::is_min_version("1.54.0").unwrap_or(false) {
+        panic!("Rust 1.54 or later is required.")
+    }
+}