|
| 1 | +- Feature Name: `metabuild` |
| 2 | +- Start Date: 2017-10-31 |
| 3 | +- RFC PR: [rust-lang/rfcs#2196](https://github.com/rust-lang/rfcs/pull/2196) |
| 4 | +- Rust Issue: [rust-lang/rust#49803](https://github.com/rust-lang/rust/issues/49803) |
| 5 | + |
| 6 | +# Summary |
| 7 | + |
| 8 | +Introduce a mechanism for Cargo crates to make use of declarative build |
| 9 | +scripts, obtained from one or more of their dependencies rather than via a |
| 10 | +`build.rs` file. Support experimentation with declarative build scripts in the |
| 11 | +crates.io ecosystem. |
| 12 | + |
| 13 | +# Motivation |
| 14 | + |
| 15 | +Cargo has many potentially desirable enhancements planned for its build |
| 16 | +process, including integrating a Cargo build process with native dependencies, |
| 17 | +and integrating with broader build systems or projects, such as massive |
| 18 | +mono-repo build systems, or Linux distributions. |
| 19 | + |
| 20 | +Right now, the biggest problem facing such systems involves `build.rs` scripts |
| 21 | +and the arbitrary things those scripts can do. Such build systems typically |
| 22 | +need more information about native dependencies that are embedded in |
| 23 | +`build.rs`, so that they can provide their own versions of those dependencies, |
| 24 | +or encode appropriate dependencies in another metadata format such as the |
| 25 | +dependencies of their packaging system or build system. Right now, such systems |
| 26 | +often have to override the `build.rs` script themselves, and do custom |
| 27 | +per-crate integration work, manually; there’s no way to introspect what |
| 28 | +`build.rs` does, or get a declarative semantic description of the build script. |
| 29 | + |
| 30 | +At the same time, we don't yet have sufficiently precise information about the |
| 31 | +needs of such systems to design an ideal set of Cargo metadata on the first |
| 32 | +try. Rather than attempt to architect the perfect solution from the start, and |
| 33 | +potentially create an intermediate state that will require long-term support, |
| 34 | +we propose to allow experimentation with declarative build systems within the |
| 35 | +crates.io ecosystem, in crates supplying modular components similar to |
| 36 | +`build.rs` scripts. By convention, such scripts should typically read any |
| 37 | +parameters and metadata they need from `Cargo.toml`, in a form that other |
| 38 | +build-related software can read as well. |
| 39 | + |
| 40 | +# Guide-level explanation |
| 41 | + |
| 42 | +In the `[package]` section of `Cargo.toml`, you can specify a field |
| 43 | +`metabuild`, whose value should be a string or list of strings, each one |
| 44 | +exactly matching the name of a dependency specified in the |
| 45 | +`[build-dependencies]` section. If you specify `metabuild`, you must not |
| 46 | +specify `build`, and Cargo will ignore the `build.rs` file if any. |
| 47 | + |
| 48 | +When Cargo builds a crate that specifies a `metabuild` field, at the point when |
| 49 | +it would have built and run `build.rs`, it will instead invoke the |
| 50 | +`metabuild()` function from each of the specified crates in order. |
| 51 | + |
| 52 | +In effect, Cargo will act as though it had a `build.rs` file containing an |
| 53 | +`extern crate` line for each string, in order, as well as a `main` function |
| 54 | +that calls the `metabuild` function in each such crate, in order. For example, |
| 55 | +if the crate contains `metabuild = ["pkgc", "parsegen"]`, then the effective |
| 56 | +`build.rs` will look like this: |
| 57 | + |
| 58 | +```rust |
| 59 | +extern crate pkgc; |
| 60 | +extern crate parsegen; |
| 61 | + |
| 62 | +fn main() { |
| 63 | + pkgc::metabuild(); |
| 64 | + parsegen::metabuild(); |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +Note that the `metabuild` functions intentionally take no parameters; they |
| 69 | +should obtain any parameters they need from `Cargo.toml`. Various crates to |
| 70 | +parse `Cargo.toml` exist in the crates.io ecosystem. |
| 71 | + |
| 72 | +Also note that the `metabuild` functions do not return an error type; if they |
| 73 | +fail, they should panic. |
| 74 | + |
| 75 | +Future versions of this interface with higher integration into Cargo may |
| 76 | +incorporate ways for Cargo to pass pre-parsed data from `Cargo.toml`, or ways |
| 77 | +for the `metabuild` functions to return semantic error information. Metabuild |
| 78 | +interfaces may also wish to run scripts in parallel, provide dependencies |
| 79 | +between them, or orchestrate their execution in many other ways. This minimal |
| 80 | +specification allows for experimentation with such interfaces within the |
| 81 | +crates.io ecosystem, by providing an adapter from the raw metabuild interface. |
| 82 | + |
| 83 | +# Reference-level explanation |
| 84 | + |
| 85 | +Cargo's logic to invoke `build.rs` should check for the `metabuild` key, and if |
| 86 | +present, create and invoke a temporary `build.rs` as described above. For an |
| 87 | +initial implementation, Cargo can generate and cache that `build.rs` in the |
| 88 | +`target` directory when needed, alongside the built version of the script. |
| 89 | + |
| 90 | +For Cargo schema versioning, using the `metabuild` key will result in the crate |
| 91 | +requiring a sufficiently new version of Cargo to understand `metabuild`. This |
| 92 | +should start out as an unstable Cargo feature; in the course of experimentation |
| 93 | +and stabilization, the implementation of this feature may change, requiring |
| 94 | +adaptation of experimental build scripts. |
| 95 | + |
| 96 | +If any of the strings mentioned in `metabuild` do not match one of the |
| 97 | +build-dependencies, Cargo should produce an error (*before* attempting to |
| 98 | +generate and compile a `build.rs` script). However, if a string matches a |
| 99 | +conditional build-dependency, such as one conditional on a feature or target, |
| 100 | +then Cargo should only invoke that build-dependency's `metabuild` function when |
| 101 | +those conditions apply. |
| 102 | + |
| 103 | +Cargo's documentation on `metabuild` should recommend a preferred crate for |
| 104 | +parsing data from `Cargo.toml`, to avoid every provider of a metabuild function |
| 105 | +from reimplementing it themselves. |
| 106 | + |
| 107 | +As we develop other best practices for the development and implementation of |
| 108 | +metabuild crates, we should extract and standardize common code for those |
| 109 | +practices as crates. |
| 110 | + |
| 111 | +# Drawbacks |
| 112 | + |
| 113 | +While Cargo can change this interface arbitrarily while still unstable, one |
| 114 | +stabilized, Cargo will have to support it forever, even if we develop a new |
| 115 | +build/metabuild interface in the future. |
| 116 | + |
| 117 | +# Rationale and Alternatives |
| 118 | + |
| 119 | +`metabuild` could always point to a single crate, and not support a list of |
| 120 | +crate names; a crate in the crates.io ecosystem could easily provide the "list |
| 121 | +of crate names" functionality, along with more advanced flows of information |
| 122 | +from one such crate to another. However, many simple cases will only want to |
| 123 | +invoke a list of crates in order, and handling that one case within Cargo will |
| 124 | +simplify initial experimentation while still allowing implementation of more |
| 125 | +complex logic via other crates in the crates.io ecosystem. |
| 126 | + |
| 127 | +`metabuild()` functions could take parameters, return errors, or make use of |
| 128 | +traits. However, this would require providing appropriate types and traits for |
| 129 | +all of those, as well as a helper crate providing those types and traits, and |
| 130 | +we do not yet know what interfaces we need or want. We propose experimenting |
| 131 | +via the crates.io ecosystem first, before considering such interfaces. |
| 132 | + |
| 133 | +Cargo could compile and run a separate `build.rs`-like script to run each |
| 134 | +metabuild function independently, rather than a single script that invokes all |
| 135 | +of them. |
| 136 | + |
| 137 | +We could avoid introducing an extensible mechanism, and instead introduce |
| 138 | +individual semantic build interfaces one-by-one within Cargo itself. However, |
| 139 | +this would drastically impair experimentation and development, and in |
| 140 | +particular this would make it more difficult to evaluate multiple potential |
| 141 | +approaches to any given piece of build functionality. Such an interface would |
| 142 | +also not provide an obvious path to support code generators. |
0 commit comments