Re: [PATCH v8] kbuild: host: use single executable for rustc -C linker
Nicolas Schier <[email protected]>
| Newsgroups | org.kernel.vger.linux-kbuild,org.kernel.vger.linux-kernel,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <ajL4JBIUVHdYISsX@levanger> |
On Wed, Jun 10, 2026 at 03:18:54PM +0200, Miguel Ojeda wrote: > On Sat, May 9, 2026 at 12:20 PM Mohamad Alsadhan <[email protected]> wrote: > > > > rustc's -C linker= option expects a single executable path. When > > HOSTCC contains a wrapper (e.g. "ccache gcc"), passing > > `-Clinker=$(HOSTCC)` results in the shell splitting the value into > > multiple words, and rustc interprets the additional word as an > > input filename: > > I have been taking a look at this, and considered applying it since > Kbuild is OK with it (thanks a lot for all the work during the > different versions), but I am not sure if the following bits are all > intended: > > - Shouldn't `HOSTRUSTC_LD` be documented in `Documentation/kbuild/kbuild.rst`? Good catch, thanks. Yes. > - Why do we do both `clean-files` and `CLEAN_FILES`? > > + In fact, should we do it on `clean` or `mrproper`? Nicolas > originally suggested `MRPROPER_FILES`, but this is on `CLEAN_FILES`. > But more on that below, since I guess it depends on how we treat > out-of-tree modules... > > - Was this tested with an out-of-tree module? I am asking because: > > + It does create an unused wrapper in a `scripts/` folder in the > out-of-tree module directory (i.e. the one used is the in-tree one) -- > is that intended? > > + If we remove the wrapper during `clean` as the patch currently > does, then it means we cannot build Rust host programs in an > out-of-tree module (because it uses the in-tree one). Should it be in > `mrproper` instead, or should we generate a per-out-of-tree-module > one? > > While I think the kernel generally expects that the same > toolchain is used for both the main build and out-of-tree modules (it > may happen to work otherwise, but as a policy it is not supposed to be > supported, or at least that is what I recall I was told), I am not > sure if it applies to host programs. I guess someone may want to use a > different host toolchain vs. the one used to build the main kernel, > and I guess things would generally work. > > - The `filechk` could fail if we use Rust host programs in more > folders later on, i.e. if two submakes run it at the same time, and > one at the end deletes the (shared) temporary, then the other will > fail if it was in the middle of updating it. Uh, thanks for all those good questions. I didn't even think about rust host progs being built in out-of-tree module trees. And the concurrent filechk calls are really no good. Iff HOSTRUSTC_LD shall be kept stable for external kernel modules, a semi-simple solution might be to move rustc-wrapper rules to scripts/basic/ and use cmd_* instead of filechk. Regarding the queations above, this would mean: * ${KBUILD_OUTPUT}/scripts/basic/rustc-wrapper will be purged during 'mrproper' * It will be generated only once, no matter who many in-tree folder use it. Only roughly tested: diff --git a/Makefile b/Makefile index 8235b6a9b3cc..e3a82c344e69 100644 --- a/Makefile +++ b/Makefile @@ -661,6 +661,7 @@ export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \ PHONY += scripts_basic scripts_basic: KBUILD_HOSTCFLAGS := $(KBUILD_HOSTCFLAGS) scripts_basic: KBUILD_HOSTLDFLAGS := $(KBUILD_HOSTLDFLAGS) +scripts_basic: HOSTRUSTC_LD := $(HOSTRUSTC_LD) scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic diff --git a/rust/Makefile b/rust/Makefile index 2fbdebb93bf2..d02002c50432 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -593,7 +593,7 @@ quiet_cmd_rustc_procmacro = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) cmd_rustc_procmacro = \ $(rustc_target_envs) \ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) $(rust_common_flags) $(rustc_target_flags) \ - -Clinker-flavor=gcc -Clinker=$(HOSTCC) \ + -Clinker-flavor=gcc -Clinker=$(objtree)/scripts/basic/rustc-wrapper \ -Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \ --emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \ --crate-type proc-macro -L$(objtree)/$(obj) \ @@ -610,12 +610,14 @@ $(obj)/$(libzerocopy_derive_name): $(src)/zerocopy-derive/lib.rs $(obj)/libproc_ $(obj)/$(libmacros_name): private rustc_target_flags = \ --extern proc_macro2 --extern quote --extern syn $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \ - $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE + $(obj)/libquote.rlib $(obj)/libsyn.rlib \ + scripts/basic/rustc-wrapper FORCE +$(call if_changed_dep,rustc_procmacro) $(obj)/$(libpin_init_internal_name): private rustc_target_flags = $(pin_init_internal-flags) $(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs \ - $(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE + $(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib \ + scripts/basic/rustc-wrapper FORCE +$(call if_changed_dep,rustc_procmacro) # `rustc` requires `-Zunstable-options` to use custom target specifications diff --git a/scripts/Makefile.host b/scripts/Makefile.host index c1dedf646a39..0be405efa38a 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -91,7 +91,8 @@ hostcxx_flags = -Wp,-MMD,$(depfile) \ # current working directory, which may be not accessible in the out-of-tree # modules case. hostrust_flags = --out-dir $(dir $@) --emit=dep-info=$(depfile) \ - -Clinker-flavor=gcc -Clinker=$(HOSTCC) \ + -Clinker-flavor=gcc \ + -Clinker=$(objtree)/scripts/basic/rustc-wrapper \ -Clink-args='$(call escsq,$(KBUILD_HOSTLDFLAGS))' \ $(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \ $(HOSTRUSTFLAGS_$(target-stem)) @@ -153,7 +154,7 @@ $(host-cxxobjs): $(obj)/%.o: $(obj)/%.cc FORCE quiet_cmd_host-rust = HOSTRUSTC $@ cmd_host-rust = \ $(HOSTRUSTC) $(hostrust_flags) --emit=link=$@ $< -$(host-rust): $(obj)/%: $(src)/%.rs FORCE +$(host-rust): $(obj)/%: $(src)/%.rs scripts/basic/rustc-wrapper FORCE +$(call if_changed_dep,host-rust) targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \ diff --git a/scripts/basic/.gitignore b/scripts/basic/.gitignore index 07c195f605a1..d314c04fe131 100644 --- a/scripts/basic/.gitignore +++ b/scripts/basic/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only /fixdep /randstruct.seed +/rustc-wrapper diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile index fb8e2c38fbc7..0aec1adc199b 100644 --- a/scripts/basic/Makefile +++ b/scripts/basic/Makefile @@ -19,3 +19,25 @@ always-$(CONFIG_RANDSTRUCT) += randstruct.seed $(obj)/../../include/generated/integer-wrap.h: $(srctree)/scripts/integer-wrap-ignore.scl FORCE $(call if_changed,touch) always-$(CONFIG_UBSAN_INTEGER_WRAP) += ../../include/generated/integer-wrap.h + +# rustc-wrapper: rustc's `-Clinker=` expects a single executable path, not a +# command line. `HOSTCC` may be a multi-word command when wrapped (e.g. +# "ccache gcc"), which would otherwise be split by the shell and mis-parsed by +# rustc. To work around this, we generate a script that invokes `HOSTRUSTC_LD` +# with the linker arguments appended so such commands can be used safely. +# +# Set `HOSTRUSTC_LD` for a different rustc linker command than `HOSTCC` +HOSTRUSTC_LD ?= $(HOSTCC) + +quiet_cmd_rustc-wrapper = GEN $@ + cmd_rustc-wrapper = \ + printf '%s\n' '\#!/bin/sh' '$(addsuffix $(space),$(HOSTRUSTC_LD))"$$@"' >$@; \ + chmod a+x $@ + +$(obj)/rustc-wrapper: FORCE + $(call if_changed,rustc-wrapper) + +always-$(CONFIG_RUST) += rustc-wrapper +ifneq ($(CONFIG_RUST),) +targets += rustc-wrapper +endif -- Nicolas