unlock unlimited code runs for $79, limited time →
NOSSLETTER.techLIVE
back to OSSpedia
rust-lang/rust
senior7/10
api-designconfiguration-managementbackwards-compatibility

Partially support building and locating wasm proc-macros

Project: rust-lang/rust | Language: Rust | Concepts: api-design, configuration-management, backwards-compatibility | Difficulty: senior
Link: https://github.com/rust-lang/rust/pull/160981

The problem

Rust's procedural macros (proc-macros) are typically compiled for the host platform. There is ongoing work to support compiling and loading proc-macros as WebAssembly (wasm), which is needed for environments like WASI (WebAssembly System Interface). Before this PR, rustc did not have the ability to build or locate proc-macros for wasm targets, nor did it expose the relevant target tuple for tooling like Cargo.

Why it's non-trivial

Supporting wasm proc-macros requires more than just allowing a different target:

  • The build system and loader must distinguish between host and wasm proc-macros.
  • The correct target triple and file search paths must be wired through the compiler.
  • The system must be opt-in and backwards-compatible, as the feature is unstable and not all metadata/ABI support is present.
  • Tooling (like Cargo) needs a way to query the correct wasm proc-macro target.

The approach

Exposing the wasm proc-macro target tuple

A new --print option is added to expose the wasm proc-macro target tuple:

+    WasmProcMacroTuple,

This adds a new variant to the PrintKind enum.

+            WasmProcMacroTuple => println_info!("{}", sess.wasm_proc_macro_tuple),

This enables rustc --print=wasm-proc-macro-tuple to print the tuple, which is needed for future Cargo integration.

Wiring up the wasm proc-macro target and filesearch

New fields are added to the Session struct to store the wasm proc-macro target and its tuple:

+    pub wasm_proc_macro_tuple: TargetTuple,
+    pub wasm_proc_macro_target: Target,
...
+    wasm_proc_macro_filesearch: Option<Arc<FileSearch>>,

These are initialized in build_session:

+    let wasm_proc_macro_tuple = TargetTuple::from_tuple("wasm32-wasip2");
+    let (wasm_proc_macro_target, target_warnings) = Target::search(
+        &wasm_proc_macro_tuple,
+        sopts.sysroot.path(),
+        sopts.unstable_opts.unstable_options,
+    )
...
+    let wasm_proc_macro_filesearch = if sopts.unstable_opts.wasm_proc_macros {
+        Some(Arc::new(FileSearch::new(
+            &sopts.search_paths,
+            &wasm_proc_macro_tlib_path,
+            &wasm_proc_macro_target,
+            sopts.unstable_opts.implicit_sysroot_deps,
+        )))
+    } else {
+        None
+    };

This ensures that, when the unstable option is enabled, the session has the correct target and file search paths for wasm proc-macros.

Allowing proc-macros to be built for the wasm target

The default restriction that prevents building proc-macros for non-host targets is relaxed for wasm when the unstable option is enabled:

+    if crate_type == CrateType::ProcMacro {
+        if sess.opts.target_triple == sess.wasm_proc_macro_tuple
+            && sess.opts.unstable_opts.wasm_proc_macros
+        {
+            return false;
+        }
+    }

This means rustc will not reject building a proc-macro for the wasm target if the option is set.

Locating wasm proc-macros

The crate locator is updated to support searching for wasm proc-macros:

+    pub(crate) fn for_wasm_proc_macro(&mut self, sess: &'a Session, path_kind: PathKind) {
+        self.is_proc_macro = true;
+        self.target = &sess.wasm_proc_macro_target;
+        self.tuple = sess.wasm_proc_macro_tuple.clone();
+        self.filesearch = sess.wasm_proc_macro_filesearch();
+        self.path_kind = path_kind;
+    }

This function configures the locator to search for wasm proc-macros using the correct target and search paths.

+            } else if sess.opts.unstable_opts.wasm_proc_macros {
+                // Load the proc macro crate for wasm
+                proc_macro_locator.for_wasm_proc_macro(sess, path_kind);
+
+                match self.load(&mut proc_macro_locator, &mut CrateRejections::default())? {
+                    Some(host_result) => Ok(Some((host_result, None))),
+                    None => Ok(None),
+                }
+            } else {
+                Ok(None)
+            }

If the unstable option is set and loading the host proc-macro fails, rustc will attempt to locate a wasm proc-macro instead.

Updating compiletest for wasm proc-macro file naming

Test infrastructure is updated to expect .wasm files for wasm proc-macros:

+        AuxType::ProcMacro if wasm_proc_macros => Some(format!("{name}.wasm")),

This ensures tests for wasm proc-macros look for the correct file extension.

Documentation and help output

A new unstable book page documents the new print option, and help output is updated to mention it.

+                        <all-target-specs-json|...|wasm-proc-macro-tuple>.

From the review thread

bjorn3: I've added --print wasm-proc-macro-tuple as preparation for future cargo support for -Zwasm-proc-macros.

bjorn3: These changes are based on my wasip1 PR and I have rebased that PR on top of this. It should work just fine for wasip2 too though.

Takeaways

  • When introducing new target-specific features, expose configuration and query points (like --print) for toolchain integration and future-proofing.
  • Backwards compatibility and opt-in gating are essential when adding unstable or experimental features to a compiler.
  • Updating test infrastructure and documentation is necessary to support new targets and file formats, ensuring smooth developer experience and CI coverage.