* [PATCH v8] kbuild: host: use single executable for rustc -C linker
@ 2026-05-09 10:19 Mohamad Alsadhan
2026-05-11 6:59 ` Nathan Chancellor
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Mohamad Alsadhan @ 2026-05-09 10:19 UTC (permalink / raw)
To: Nathan Chancellor, Nicolas Schier, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich
Cc: Mohamad Alsadhan, Yoann Congal, linux-kbuild, linux-kernel,
rust-for-linux
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:
error: multiple input filenames provided ...
Generate a small wrapper script and pass it to -Clinker e.g.
```
#!/bin/sh
ccache gcc "$@"
```
Note that the wrapper intentionally avoids a leading `exec` to
naturally support leading environment assignments such as:
`VAR=VAL ccache gcc`.
This fix should be general enough to address most if not all cases
(incl. wrappers or subcommands) and avoids surprises of simpler fixes
like just defaulting to gcc.
This avoids passing the user command as an environment variable as
that would be more challenging to trace and debug shell expansions.
Link: https://github.com/Rust-for-Linux/linux/issues/1224
Suggested-by: Yoann Congal <yoann.congal@smile.fr>
Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
---
v7 -> v8:
- Unify into a single global `scripts/rustc-wrapper`, instead of
generating per-directory wrappers.
- Use `$(objtree)/scripts/rustc-wrapper` when passing the wrapper to
`rustc` for clarity.
- Keep make dependencies on `scripts/rustc-wrapper`, since the rule
is defined for that target name.
- Drop the sample-local clean/ignore handling that was only needed
for per-directory wrappers.
v6 -> v7:
- Always generate `scripts/rustc-wrapper` when `CONFIG_RUST=y`,
including cases with no Rust host programs in `scripts/`.
- Use `$(obj)/rustc-wrapper` for host Rust programs, fixing builds
outside `scripts/`, e.g. `samples/rust/hostprogs/`.
- Remove leading exec (again) to allow leading env vars e.g.
HOSTRUSTC_LD="VAR=VAL ccache gcc".
- Track, clean and ignore generated wrapper as needed.
v5 -> v6:
- Add fix to `rust/Makefile` as well
- Include script to `.gitignore` and make clean
- Add back the outer `exec` to the command
v4 -> v5:
- Fix word splitting issues
- Remove unnecessary `exec sh -c` and simplify generated script
v3 -> v4:
- Use filechk instead of if_changed macro to regenerate script
- Remove trailing space at EOL
v2 -> v3:
- Scrap previous hacky approaches (e.g. using lastword) and go with
a proper fix which turned out not that complex to implement.
v1 -> v2:
- Rename HOSTRUSTC_LINKER to HOSTRUSTC_LD for consistency
- Introduce explicit HOSTRUSTC_LD override
- Warn when falling back due to multi-argument HOSTCC
- Error out if a user-specified HOSTRUSTC_LD is not an executable
v1: https://lore.kernel.org/all/20260225102819.16553-1-mo@sdhn.cc/
v2: https://lore.kernel.org/all/20260227132713.23106-1-mo@sdhn.cc/
v3: https://lore.kernel.org/all/20260312002852.11292-1-mo@sdhn.cc/
v4: https://lore.kernel.org/all/20260317112021.14353-1-mo@sdhn.cc/
v5: https://lore.kernel.org/all/20260321150034.9915-1-mo@sdhn.cc/
v6: https://lore.kernel.org/all/20260331000802.380-1-mo@sdhn.cc/
v7: https://lore.kernel.org/all/20260416211525.13847-1-mo@sdhn.cc/
---
Makefile | 3 ++-
rust/Makefile | 8 +++++---
scripts/.gitignore | 1 +
scripts/Makefile | 3 +++
scripts/Makefile.host | 23 +++++++++++++++++++++--
5 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/Makefile b/Makefile
index 1a219bf1c..5ebeef67f 100644
--- a/Makefile
+++ b/Makefile
@@ -1651,7 +1651,8 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \
modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \
compile_commands.json rust/test \
rust-project.json .vmlinux.objs .vmlinux.export.c \
- .builtin-dtbs-list .builtin-dtb.S
+ .builtin-dtbs-list .builtin-dtb.S \
+ scripts/rustc-wrapper
# Directories & files removed with 'make mrproper'
MRPROPER_FILES += include/config include/generated \
diff --git a/rust/Makefile b/rust/Makefile
index 5eca6a817..620d397db 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -565,7 +565,7 @@ $(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE
quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
cmd_rustc_procmacro = \
$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
- -Clinker-flavor=gcc -Clinker=$(HOSTCC) \
+ -Clinker-flavor=gcc -Clinker=$(objtree)/scripts/rustc-wrapper \
-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
--crate-type proc-macro -L$(objtree)/$(obj) \
@@ -576,12 +576,14 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
$(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/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/rustc-wrapper FORCE
+$(call if_changed_dep,rustc_procmacro)
# `rustc` requires `-Zunstable-options` to use custom target specifications
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208..b3948b148 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -6,6 +6,7 @@
/kallsyms
/module.lds
/recordmcount
+/rustc-wrapper
/rustdoc_test_builder
/rustdoc_test_gen
/sign-file
diff --git a/scripts/Makefile b/scripts/Makefile
index 0941e5ce7..287421322 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -29,6 +29,9 @@ generate_rust_target-rust := y
rustdoc_test_builder-rust := y
rustdoc_test_gen-rust := y
+always-$(CONFIG_RUST) += rustc-wrapper
+clean-files += rustc-wrapper
+
HOSTCFLAGS_tracepoint-update.o = -I$(srctree)/tools/include
HOSTCFLAGS_elf-parse.o = -I$(srctree)/tools/include
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
diff --git a/scripts/Makefile.host b/scripts/Makefile.host
index c1dedf646..ab810e42b 100644
--- a/scripts/Makefile.host
+++ b/scripts/Makefile.host
@@ -87,11 +87,30 @@ hostcxx_flags = -Wp,-MMD,$(depfile) \
$(KBUILD_HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \
$(HOSTCXXFLAGS_$(target-stem).o)
+# 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)
+
+define filechk_rustc-wrapper
+ printf "%s\n" \
+ '#!/bin/sh' \
+ '$(call escsq,$(HOSTRUSTC_LD)) "$$@"'
+endef
+
+scripts/rustc-wrapper: FORCE
+ $(call filechk,rustc-wrapper)
+ $(Q)chmod +x $@
+
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
# 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/rustc-wrapper \
-Clink-args='$(call escsq,$(KBUILD_HOSTLDFLAGS))' \
$(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \
$(HOSTRUSTFLAGS_$(target-stem))
@@ -153,7 +172,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/rustc-wrapper FORCE
+$(call if_changed_dep,host-rust)
targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
--
2.54.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH v8] kbuild: host: use single executable for rustc -C linker 2026-05-09 10:19 [PATCH v8] kbuild: host: use single executable for rustc -C linker Mohamad Alsadhan @ 2026-05-11 6:59 ` Nathan Chancellor 2026-05-11 11:18 ` Nicolas Schier 2026-05-12 17:32 ` Yoann Congal 2026-06-10 13:18 ` Miguel Ojeda 2 siblings, 1 reply; 6+ messages in thread From: Nathan Chancellor @ 2026-05-11 6:59 UTC (permalink / raw) To: Mohamad Alsadhan Cc: Nicolas Schier, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, Yoann Congal, linux-kbuild, linux-kernel, rust-for-linux Hi Mohamad, On Sat, May 09, 2026 at 01:19:38PM +0300, Mohamad Alsadhan 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: > > error: multiple input filenames provided ... > > Generate a small wrapper script and pass it to -Clinker e.g. > > ``` > #!/bin/sh > ccache gcc "$@" > ``` > > Note that the wrapper intentionally avoids a leading `exec` to > naturally support leading environment assignments such as: > `VAR=VAL ccache gcc`. > > This fix should be general enough to address most if not all cases > (incl. wrappers or subcommands) and avoids surprises of simpler fixes > like just defaulting to gcc. > > This avoids passing the user command as an environment variable as > that would be more challenging to trace and debug shell expansions. > > Link: https://github.com/Rust-for-Linux/linux/issues/1224 > Suggested-by: Yoann Congal <yoann.congal@smile.fr> > Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc> Thanks, this seems reasonable from a Kbuild perspective (although Nicolas may have other comments) and it appears to work for me. I expect this to go via the Rust tree. Acked-by: Nathan Chancellor <nathan@kernel.org> Tested-by: Nathan Chancellor <nathan@kernel.org> > diff --git a/Makefile b/Makefile > index 1a219bf1c..5ebeef67f 100644 > --- a/Makefile > +++ b/Makefile > @@ -1651,7 +1651,8 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ > modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \ > compile_commands.json rust/test \ > rust-project.json .vmlinux.objs .vmlinux.export.c \ > - .builtin-dtbs-list .builtin-dtb.S > + .builtin-dtbs-list .builtin-dtb.S \ > + scripts/rustc-wrapper What tree was this generated against? It conflicts with Linus's tree due to commit a76e30c2479c ("kbuild: Delete .builtin-dtbs.S when running make clean") that landed in 7.0-rc6. > > # Directories & files removed with 'make mrproper' > MRPROPER_FILES += include/config include/generated \ -- Cheers, Nathan ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v8] kbuild: host: use single executable for rustc -C linker 2026-05-11 6:59 ` Nathan Chancellor @ 2026-05-11 11:18 ` Nicolas Schier 0 siblings, 0 replies; 6+ messages in thread From: Nicolas Schier @ 2026-05-11 11:18 UTC (permalink / raw) To: Nathan Chancellor Cc: Mohamad Alsadhan, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, Yoann Congal, linux-kbuild, linux-kernel, rust-for-linux On Mon, May 11, 2026 at 03:59:15PM +0900, Nathan Chancellor wrote: > Hi Mohamad, > > On Sat, May 09, 2026 at 01:19:38PM +0300, Mohamad Alsadhan 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: > > > > error: multiple input filenames provided ... > > > > Generate a small wrapper script and pass it to -Clinker e.g. > > > > ``` > > #!/bin/sh > > ccache gcc "$@" > > ``` > > > > Note that the wrapper intentionally avoids a leading `exec` to > > naturally support leading environment assignments such as: > > `VAR=VAL ccache gcc`. > > > > This fix should be general enough to address most if not all cases > > (incl. wrappers or subcommands) and avoids surprises of simpler fixes > > like just defaulting to gcc. > > > > This avoids passing the user command as an environment variable as > > that would be more challenging to trace and debug shell expansions. > > > > Link: https://github.com/Rust-for-Linux/linux/issues/1224 > > Suggested-by: Yoann Congal <yoann.congal@smile.fr> > > Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc> > > Thanks, this seems reasonable from a Kbuild perspective (although > Nicolas may have other comments) and it appears to work for me. I expect > this to go via the Rust tree. > > Acked-by: Nathan Chancellor <nathan@kernel.org> > Tested-by: Nathan Chancellor <nathan@kernel.org> > > > diff --git a/Makefile b/Makefile > > index 1a219bf1c..5ebeef67f 100644 > > --- a/Makefile > > +++ b/Makefile > > @@ -1651,7 +1651,8 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ > > modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \ > > compile_commands.json rust/test \ > > rust-project.json .vmlinux.objs .vmlinux.export.c \ > > - .builtin-dtbs-list .builtin-dtb.S > > + .builtin-dtbs-list .builtin-dtb.S \ (If it'd go through kbuild, I'd fix the spaces-only here during patch application.) Thanks, no further objections. Reviewed-by: Nicolas Schier <nsc@kernel.org> -- Nicolas ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v8] kbuild: host: use single executable for rustc -C linker 2026-05-09 10:19 [PATCH v8] kbuild: host: use single executable for rustc -C linker Mohamad Alsadhan 2026-05-11 6:59 ` Nathan Chancellor @ 2026-05-12 17:32 ` Yoann Congal 2026-06-10 13:18 ` Miguel Ojeda 2 siblings, 0 replies; 6+ messages in thread From: Yoann Congal @ 2026-05-12 17:32 UTC (permalink / raw) To: Mohamad Alsadhan, Nathan Chancellor, Nicolas Schier, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich Cc: linux-kbuild, linux-kernel, rust-for-linux On Sat May 9, 2026 at 12:19 PM CEST, Mohamad Alsadhan 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: > > error: multiple input filenames provided ... > > Generate a small wrapper script and pass it to -Clinker e.g. > > ``` > #!/bin/sh > ccache gcc "$@" > ``` > > Note that the wrapper intentionally avoids a leading `exec` to > naturally support leading environment assignments such as: > `VAR=VAL ccache gcc`. > > This fix should be general enough to address most if not all cases > (incl. wrappers or subcommands) and avoids surprises of simpler fixes > like just defaulting to gcc. > > This avoids passing the user command as an environment variable as > that would be more challenging to trace and debug shell expansions. > > Link: https://github.com/Rust-for-Linux/linux/issues/1224 > Suggested-by: Yoann Congal <yoann.congal@smile.fr> > Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc> Hello, I also tested this one: Tested-by: Yoann Congal <yoann.congal@smile.fr> Thanks! > --- > v7 -> v8: > - Unify into a single global `scripts/rustc-wrapper`, instead of > generating per-directory wrappers. > - Use `$(objtree)/scripts/rustc-wrapper` when passing the wrapper to > `rustc` for clarity. > - Keep make dependencies on `scripts/rustc-wrapper`, since the rule > is defined for that target name. > - Drop the sample-local clean/ignore handling that was only needed > for per-directory wrappers. > > v6 -> v7: > - Always generate `scripts/rustc-wrapper` when `CONFIG_RUST=y`, > including cases with no Rust host programs in `scripts/`. > - Use `$(obj)/rustc-wrapper` for host Rust programs, fixing builds > outside `scripts/`, e.g. `samples/rust/hostprogs/`. > - Remove leading exec (again) to allow leading env vars e.g. > HOSTRUSTC_LD="VAR=VAL ccache gcc". > - Track, clean and ignore generated wrapper as needed. > > v5 -> v6: > - Add fix to `rust/Makefile` as well > - Include script to `.gitignore` and make clean > - Add back the outer `exec` to the command > > v4 -> v5: > - Fix word splitting issues > - Remove unnecessary `exec sh -c` and simplify generated script > > v3 -> v4: > - Use filechk instead of if_changed macro to regenerate script > - Remove trailing space at EOL > > v2 -> v3: > - Scrap previous hacky approaches (e.g. using lastword) and go with > a proper fix which turned out not that complex to implement. > > v1 -> v2: > - Rename HOSTRUSTC_LINKER to HOSTRUSTC_LD for consistency > - Introduce explicit HOSTRUSTC_LD override > - Warn when falling back due to multi-argument HOSTCC > - Error out if a user-specified HOSTRUSTC_LD is not an executable > > v1: https://lore.kernel.org/all/20260225102819.16553-1-mo@sdhn.cc/ > v2: https://lore.kernel.org/all/20260227132713.23106-1-mo@sdhn.cc/ > v3: https://lore.kernel.org/all/20260312002852.11292-1-mo@sdhn.cc/ > v4: https://lore.kernel.org/all/20260317112021.14353-1-mo@sdhn.cc/ > v5: https://lore.kernel.org/all/20260321150034.9915-1-mo@sdhn.cc/ > v6: https://lore.kernel.org/all/20260331000802.380-1-mo@sdhn.cc/ > v7: https://lore.kernel.org/all/20260416211525.13847-1-mo@sdhn.cc/ > --- > Makefile | 3 ++- > rust/Makefile | 8 +++++--- > scripts/.gitignore | 1 + > scripts/Makefile | 3 +++ > scripts/Makefile.host | 23 +++++++++++++++++++++-- > 5 files changed, 32 insertions(+), 6 deletions(-) > > diff --git a/Makefile b/Makefile > index 1a219bf1c..5ebeef67f 100644 > --- a/Makefile > +++ b/Makefile > @@ -1651,7 +1651,8 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ > modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \ > compile_commands.json rust/test \ > rust-project.json .vmlinux.objs .vmlinux.export.c \ > - .builtin-dtbs-list .builtin-dtb.S > + .builtin-dtbs-list .builtin-dtb.S \ > + scripts/rustc-wrapper > > # Directories & files removed with 'make mrproper' > MRPROPER_FILES += include/config include/generated \ > diff --git a/rust/Makefile b/rust/Makefile > index 5eca6a817..620d397db 100644 > --- a/rust/Makefile > +++ b/rust/Makefile > @@ -565,7 +565,7 @@ $(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE > quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@ > cmd_rustc_procmacro = \ > $(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \ > - -Clinker-flavor=gcc -Clinker=$(HOSTCC) \ > + -Clinker-flavor=gcc -Clinker=$(objtree)/scripts/rustc-wrapper \ > -Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \ > --emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \ > --crate-type proc-macro -L$(objtree)/$(obj) \ > @@ -576,12 +576,14 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@ > $(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/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/rustc-wrapper FORCE > +$(call if_changed_dep,rustc_procmacro) > > # `rustc` requires `-Zunstable-options` to use custom target specifications > diff --git a/scripts/.gitignore b/scripts/.gitignore > index 4215c2208..b3948b148 100644 > --- a/scripts/.gitignore > +++ b/scripts/.gitignore > @@ -6,6 +6,7 @@ > /kallsyms > /module.lds > /recordmcount > +/rustc-wrapper > /rustdoc_test_builder > /rustdoc_test_gen > /sign-file > diff --git a/scripts/Makefile b/scripts/Makefile > index 0941e5ce7..287421322 100644 > --- a/scripts/Makefile > +++ b/scripts/Makefile > @@ -29,6 +29,9 @@ generate_rust_target-rust := y > rustdoc_test_builder-rust := y > rustdoc_test_gen-rust := y > > +always-$(CONFIG_RUST) += rustc-wrapper > +clean-files += rustc-wrapper > + > HOSTCFLAGS_tracepoint-update.o = -I$(srctree)/tools/include > HOSTCFLAGS_elf-parse.o = -I$(srctree)/tools/include > HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include > diff --git a/scripts/Makefile.host b/scripts/Makefile.host > index c1dedf646..ab810e42b 100644 > --- a/scripts/Makefile.host > +++ b/scripts/Makefile.host > @@ -87,11 +87,30 @@ hostcxx_flags = -Wp,-MMD,$(depfile) \ > $(KBUILD_HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \ > $(HOSTCXXFLAGS_$(target-stem).o) > > +# 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) > + > +define filechk_rustc-wrapper > + printf "%s\n" \ > + '#!/bin/sh' \ > + '$(call escsq,$(HOSTRUSTC_LD)) "$$@"' > +endef > + > +scripts/rustc-wrapper: FORCE > + $(call filechk,rustc-wrapper) > + $(Q)chmod +x $@ > + > # `--out-dir` is required to avoid temporaries being created by `rustc` in the > # 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/rustc-wrapper \ > -Clink-args='$(call escsq,$(KBUILD_HOSTLDFLAGS))' \ > $(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \ > $(HOSTRUSTFLAGS_$(target-stem)) > @@ -153,7 +172,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/rustc-wrapper FORCE > +$(call if_changed_dep,host-rust) > > targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \ -- Yoann Congal Smile ECS ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v8] kbuild: host: use single executable for rustc -C linker 2026-05-09 10:19 [PATCH v8] kbuild: host: use single executable for rustc -C linker Mohamad Alsadhan 2026-05-11 6:59 ` Nathan Chancellor 2026-05-12 17:32 ` Yoann Congal @ 2026-06-10 13:18 ` Miguel Ojeda 2026-06-17 19:40 ` Nicolas Schier 2 siblings, 1 reply; 6+ messages in thread From: Miguel Ojeda @ 2026-06-10 13:18 UTC (permalink / raw) To: Mohamad Alsadhan Cc: Nathan Chancellor, Nicolas Schier, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, Yoann Congal, linux-kbuild, linux-kernel, rust-for-linux On Sat, May 9, 2026 at 12:20 PM Mohamad Alsadhan <mo@sdhn.cc> 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`? - 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. - Since we have now `zerocopy-derive`, which is a proc macro, I had to add a dependency for that one too like the other two. Some good news, though, is that the whole `clean` thing made me realize I missed the `mrproper` for `zerocopy-derive`, so I am sending a quick patch for that. Cheers, Miguel ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v8] kbuild: host: use single executable for rustc -C linker 2026-06-10 13:18 ` Miguel Ojeda @ 2026-06-17 19:40 ` Nicolas Schier 0 siblings, 0 replies; 6+ messages in thread From: Nicolas Schier @ 2026-06-17 19:40 UTC (permalink / raw) To: Miguel Ojeda Cc: Mohamad Alsadhan, Nathan Chancellor, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, Yoann Congal, linux-kbuild, linux-kernel, rust-for-linux On Wed, Jun 10, 2026 at 03:18:54PM +0200, Miguel Ojeda wrote: > On Sat, May 9, 2026 at 12:20 PM Mohamad Alsadhan <mo@sdhn.cc> 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 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-17 19:41 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-09 10:19 [PATCH v8] kbuild: host: use single executable for rustc -C linker Mohamad Alsadhan 2026-05-11 6:59 ` Nathan Chancellor 2026-05-11 11:18 ` Nicolas Schier 2026-05-12 17:32 ` Yoann Congal 2026-06-10 13:18 ` Miguel Ojeda 2026-06-17 19:40 ` Nicolas Schier
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox