From 043745cb9688fbecd97638b2fa156cb055bbbb14 Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Sun, 26 Dec 2021 15:05:12 +0100
Subject: [PATCH] Avoid the merge derive macro in rustbuild

The task of the macro is simple enough that a decl macro is almost ten
times shorter than the original proc macro. The proc macro is 159 lines
while the decl macro is just 18 lines.

This reduces the amount of dependencies of rustbuild from 45 to 37. It
also slight reduces compilation time from 47s to 44s for debug builds.
---
 Cargo.lock               |  16 --
 src/bootstrap/Cargo.toml |   2 +-
 src/bootstrap/config.rs  | 361 +++++++++++++++++++++------------------
 3 files changed, 197 insertions(+), 182 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index b7148883704..dd4d48c126f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2225,22 +2225,6 @@ name = "merge"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9"
-dependencies = [
- "merge_derive",
- "num-traits",
-]
-
-[[package]]
-name = "merge_derive"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07"
-dependencies = [
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
 
 [[package]]
 name = "minifier"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 9ad453497f6..1f218dd8d67 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -47,7 +47,7 @@ toml = "0.5"
 time = "0.1"
 ignore = "0.4.10"
 opener = "0.5"
-merge = "0.1.0"
+merge = { version = "0.1.0", default-features = false, features = ["std"] }
 once_cell = "1.7.2"
 
 [target.'cfg(windows)'.dependencies.winapi]
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 7a4593a75f2..fcca783fbfe 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -357,105 +357,132 @@ impl Merge for TomlConfig {
     }
 }
 
-/// TOML representation of various global build decisions.
-#[derive(Deserialize, Default, Clone, Merge)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct Build {
-    build: Option<String>,
-    host: Option<Vec<String>>,
-    target: Option<Vec<String>>,
-    // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable
-    build_dir: Option<String>,
-    cargo: Option<String>,
-    rustc: Option<String>,
-    rustfmt: Option<PathBuf>,
-    docs: Option<bool>,
-    compiler_docs: Option<bool>,
-    docs_minification: Option<bool>,
-    submodules: Option<bool>,
-    fast_submodules: Option<bool>,
-    gdb: Option<String>,
-    nodejs: Option<String>,
-    npm: Option<String>,
-    python: Option<String>,
-    locked_deps: Option<bool>,
-    vendor: Option<bool>,
-    full_bootstrap: Option<bool>,
-    extended: Option<bool>,
-    tools: Option<HashSet<String>>,
-    verbose: Option<usize>,
-    sanitizers: Option<bool>,
-    profiler: Option<bool>,
-    cargo_native_static: Option<bool>,
-    low_priority: Option<bool>,
-    configure_args: Option<Vec<String>>,
-    local_rebuild: Option<bool>,
-    print_step_timings: Option<bool>,
-    print_step_rusage: Option<bool>,
-    check_stage: Option<u32>,
-    doc_stage: Option<u32>,
-    build_stage: Option<u32>,
-    test_stage: Option<u32>,
-    install_stage: Option<u32>,
-    dist_stage: Option<u32>,
-    bench_stage: Option<u32>,
-    patch_binaries_for_nix: Option<bool>,
+macro_rules! derive_merge {
+    ($(#[$attr:meta])* struct $name:ident {
+        $($field:ident: $field_ty:ty,)*
+    }) => {
+        $(#[$attr])*
+        struct $name {
+            $($field: $field_ty,)*
+        }
+
+        impl Merge for $name {
+            fn merge(&mut self, other: Self) {
+                $(
+                    Merge::merge(&mut self.$field, other.$field);
+                )*
+            }
+        }
+    }
 }
 
-/// TOML representation of various global install decisions.
-#[derive(Deserialize, Default, Clone, Merge)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct Install {
-    prefix: Option<String>,
-    sysconfdir: Option<String>,
-    docdir: Option<String>,
-    bindir: Option<String>,
-    libdir: Option<String>,
-    mandir: Option<String>,
-    datadir: Option<String>,
+derive_merge! {
+    /// TOML representation of various global build decisions.
+    #[derive(Deserialize, Default, Clone)]
+    #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+    struct Build {
+        build: Option<String>,
+        host: Option<Vec<String>>,
+        target: Option<Vec<String>>,
+        // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable
+        build_dir: Option<String>,
+        cargo: Option<String>,
+        rustc: Option<String>,
+        rustfmt: Option<PathBuf>,
+        docs: Option<bool>,
+        compiler_docs: Option<bool>,
+        docs_minification: Option<bool>,
+        submodules: Option<bool>,
+        fast_submodules: Option<bool>,
+        gdb: Option<String>,
+        nodejs: Option<String>,
+        npm: Option<String>,
+        python: Option<String>,
+        locked_deps: Option<bool>,
+        vendor: Option<bool>,
+        full_bootstrap: Option<bool>,
+        extended: Option<bool>,
+        tools: Option<HashSet<String>>,
+        verbose: Option<usize>,
+        sanitizers: Option<bool>,
+        profiler: Option<bool>,
+        cargo_native_static: Option<bool>,
+        low_priority: Option<bool>,
+        configure_args: Option<Vec<String>>,
+        local_rebuild: Option<bool>,
+        print_step_timings: Option<bool>,
+        print_step_rusage: Option<bool>,
+        check_stage: Option<u32>,
+        doc_stage: Option<u32>,
+        build_stage: Option<u32>,
+        test_stage: Option<u32>,
+        install_stage: Option<u32>,
+        dist_stage: Option<u32>,
+        bench_stage: Option<u32>,
+        patch_binaries_for_nix: Option<bool>,
+    }
 }
 
-/// TOML representation of how the LLVM build is configured.
-#[derive(Deserialize, Default, Merge)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct Llvm {
-    skip_rebuild: Option<bool>,
-    optimize: Option<bool>,
-    thin_lto: Option<bool>,
-    release_debuginfo: Option<bool>,
-    assertions: Option<bool>,
-    tests: Option<bool>,
-    plugins: Option<bool>,
-    ccache: Option<StringOrBool>,
-    version_check: Option<bool>,
-    static_libstdcpp: Option<bool>,
-    ninja: Option<bool>,
-    targets: Option<String>,
-    experimental_targets: Option<String>,
-    link_jobs: Option<u32>,
-    link_shared: Option<bool>,
-    version_suffix: Option<String>,
-    clang_cl: Option<String>,
-    cflags: Option<String>,
-    cxxflags: Option<String>,
-    ldflags: Option<String>,
-    use_libcxx: Option<bool>,
-    use_linker: Option<String>,
-    allow_old_toolchain: Option<bool>,
-    polly: Option<bool>,
-    clang: Option<bool>,
-    download_ci_llvm: Option<StringOrBool>,
+derive_merge! {
+    /// TOML representation of various global install decisions.
+    #[derive(Deserialize, Default, Clone)]
+    #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+    struct Install {
+        prefix: Option<String>,
+        sysconfdir: Option<String>,
+        docdir: Option<String>,
+        bindir: Option<String>,
+        libdir: Option<String>,
+        mandir: Option<String>,
+        datadir: Option<String>,
+    }
 }
 
-#[derive(Deserialize, Default, Clone, Merge)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct Dist {
-    sign_folder: Option<String>,
-    gpg_password_file: Option<String>,
-    upload_addr: Option<String>,
-    src_tarball: Option<bool>,
-    missing_tools: Option<bool>,
-    compression_formats: Option<Vec<String>>,
+derive_merge! {
+    /// TOML representation of how the LLVM build is configured.
+    #[derive(Deserialize, Default)]
+    #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+    struct Llvm {
+        skip_rebuild: Option<bool>,
+        optimize: Option<bool>,
+        thin_lto: Option<bool>,
+        release_debuginfo: Option<bool>,
+        assertions: Option<bool>,
+        tests: Option<bool>,
+        plugins: Option<bool>,
+        ccache: Option<StringOrBool>,
+        version_check: Option<bool>,
+        static_libstdcpp: Option<bool>,
+        ninja: Option<bool>,
+        targets: Option<String>,
+        experimental_targets: Option<String>,
+        link_jobs: Option<u32>,
+        link_shared: Option<bool>,
+        version_suffix: Option<String>,
+        clang_cl: Option<String>,
+        cflags: Option<String>,
+        cxxflags: Option<String>,
+        ldflags: Option<String>,
+        use_libcxx: Option<bool>,
+        use_linker: Option<String>,
+        allow_old_toolchain: Option<bool>,
+        polly: Option<bool>,
+        clang: Option<bool>,
+        download_ci_llvm: Option<StringOrBool>,
+    }
+}
+
+derive_merge! {
+    #[derive(Deserialize, Default, Clone)]
+    #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+    struct Dist {
+        sign_folder: Option<String>,
+        gpg_password_file: Option<String>,
+        upload_addr: Option<String>,
+        src_tarball: Option<bool>,
+        missing_tools: Option<bool>,
+        compression_formats: Option<Vec<String>>,
+    }
 }
 
 #[derive(Deserialize)]
@@ -471,80 +498,84 @@ impl Default for StringOrBool {
     }
 }
 
-/// TOML representation of how the Rust build is configured.
-#[derive(Deserialize, Default, Merge)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct Rust {
-    optimize: Option<bool>,
-    debug: Option<bool>,
-    codegen_units: Option<u32>,
-    codegen_units_std: Option<u32>,
-    debug_assertions: Option<bool>,
-    debug_assertions_std: Option<bool>,
-    overflow_checks: Option<bool>,
-    overflow_checks_std: Option<bool>,
-    debug_logging: Option<bool>,
-    debuginfo_level: Option<u32>,
-    debuginfo_level_rustc: Option<u32>,
-    debuginfo_level_std: Option<u32>,
-    debuginfo_level_tools: Option<u32>,
-    debuginfo_level_tests: Option<u32>,
-    run_dsymutil: Option<bool>,
-    backtrace: Option<bool>,
-    incremental: Option<bool>,
-    parallel_compiler: Option<bool>,
-    default_linker: Option<String>,
-    channel: Option<String>,
-    description: Option<String>,
-    musl_root: Option<String>,
-    rpath: Option<bool>,
-    verbose_tests: Option<bool>,
-    optimize_tests: Option<bool>,
-    codegen_tests: Option<bool>,
-    ignore_git: Option<bool>,
-    dist_src: Option<bool>,
-    save_toolstates: Option<String>,
-    codegen_backends: Option<Vec<String>>,
-    lld: Option<bool>,
-    use_lld: Option<bool>,
-    llvm_tools: Option<bool>,
-    deny_warnings: Option<bool>,
-    backtrace_on_ice: Option<bool>,
-    verify_llvm_ir: Option<bool>,
-    thin_lto_import_instr_limit: Option<u32>,
-    remap_debuginfo: Option<bool>,
-    jemalloc: Option<bool>,
-    test_compare_mode: Option<bool>,
-    llvm_libunwind: Option<String>,
-    control_flow_guard: Option<bool>,
-    new_symbol_mangling: Option<bool>,
-    profile_generate: Option<String>,
-    profile_use: Option<String>,
-    // ignored; this is set from an env var set by bootstrap.py
-    download_rustc: Option<StringOrBool>,
+derive_merge! {
+    /// TOML representation of how the Rust build is configured.
+    #[derive(Deserialize, Default)]
+    #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+    struct Rust {
+        optimize: Option<bool>,
+        debug: Option<bool>,
+        codegen_units: Option<u32>,
+        codegen_units_std: Option<u32>,
+        debug_assertions: Option<bool>,
+        debug_assertions_std: Option<bool>,
+        overflow_checks: Option<bool>,
+        overflow_checks_std: Option<bool>,
+        debug_logging: Option<bool>,
+        debuginfo_level: Option<u32>,
+        debuginfo_level_rustc: Option<u32>,
+        debuginfo_level_std: Option<u32>,
+        debuginfo_level_tools: Option<u32>,
+        debuginfo_level_tests: Option<u32>,
+        run_dsymutil: Option<bool>,
+        backtrace: Option<bool>,
+        incremental: Option<bool>,
+        parallel_compiler: Option<bool>,
+        default_linker: Option<String>,
+        channel: Option<String>,
+        description: Option<String>,
+        musl_root: Option<String>,
+        rpath: Option<bool>,
+        verbose_tests: Option<bool>,
+        optimize_tests: Option<bool>,
+        codegen_tests: Option<bool>,
+        ignore_git: Option<bool>,
+        dist_src: Option<bool>,
+        save_toolstates: Option<String>,
+        codegen_backends: Option<Vec<String>>,
+        lld: Option<bool>,
+        use_lld: Option<bool>,
+        llvm_tools: Option<bool>,
+        deny_warnings: Option<bool>,
+        backtrace_on_ice: Option<bool>,
+        verify_llvm_ir: Option<bool>,
+        thin_lto_import_instr_limit: Option<u32>,
+        remap_debuginfo: Option<bool>,
+        jemalloc: Option<bool>,
+        test_compare_mode: Option<bool>,
+        llvm_libunwind: Option<String>,
+        control_flow_guard: Option<bool>,
+        new_symbol_mangling: Option<bool>,
+        profile_generate: Option<String>,
+        profile_use: Option<String>,
+        // ignored; this is set from an env var set by bootstrap.py
+        download_rustc: Option<StringOrBool>,
+    }
 }
 
-/// TOML representation of how each build target is configured.
-#[derive(Deserialize, Default, Merge)]
-#[serde(deny_unknown_fields, rename_all = "kebab-case")]
-struct TomlTarget {
-    cc: Option<String>,
-    cxx: Option<String>,
-    ar: Option<String>,
-    ranlib: Option<String>,
-    default_linker: Option<PathBuf>,
-    linker: Option<String>,
-    llvm_config: Option<String>,
-    llvm_filecheck: Option<String>,
-    android_ndk: Option<String>,
-    sanitizers: Option<bool>,
-    profiler: Option<bool>,
-    crt_static: Option<bool>,
-    musl_root: Option<String>,
-    musl_libdir: Option<String>,
-    wasi_root: Option<String>,
-    qemu_rootfs: Option<String>,
-    no_std: Option<bool>,
+derive_merge! {
+    /// TOML representation of how each build target is configured.
+    #[derive(Deserialize, Default)]
+    #[serde(deny_unknown_fields, rename_all = "kebab-case")]
+    struct TomlTarget {
+        cc: Option<String>,
+        cxx: Option<String>,
+        ar: Option<String>,
+        ranlib: Option<String>,
+        default_linker: Option<PathBuf>,
+        linker: Option<String>,
+        llvm_config: Option<String>,
+        llvm_filecheck: Option<String>,
+        android_ndk: Option<String>,
+        sanitizers: Option<bool>,
+        profiler: Option<bool>,
+        crt_static: Option<bool>,
+        musl_root: Option<String>,
+        musl_libdir: Option<String>,
+        wasi_root: Option<String>,
+        qemu_rootfs: Option<String>,
+        no_std: Option<bool>,
+    }
 }
 
 impl Config {