From: "Yoann Congal" <yoann.congal@smile.fr>
To: "Mohamad Alsadhan" <mo@sdhn.cc>,
"Nathan Chancellor" <nathan@kernel.org>,
"Nicolas Schier" <nsc@kernel.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>
Cc: <linux-kbuild@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<rust-for-linux@vger.kernel.org>
Subject: Re: [PATCH v7] kbuild: host: use single executable for rustc -C linker
Date: Fri, 17 Apr 2026 09:59:17 +0200 [thread overview]
Message-ID: <DHV9RMX3PLT6.JO8708PRE7S@smile.fr> (raw)
In-Reply-To: <20260416211525.13847-1-mo@sdhn.cc>
On Thu Apr 16, 2026 at 11:15 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
> exec 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>
This does work on my ccache-enabled Yocto build, so:
Tested-by: Yoann Congal <yoann.congal@smile.fr>
Thanks!
> ---
> 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 (Yoann)
> - Include script to `.gitignore` and make clean (Nicolas)
> - 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 (Gary) 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/
> ---
> Makefile | 3 ++-
> rust/Makefile | 8 +++++---
> samples/rust/hostprogs/.gitignore | 1 +
> samples/rust/hostprogs/Makefile | 2 ++
> scripts/.gitignore | 1 +
> scripts/Makefile | 3 +++
> scripts/Makefile.host | 26 +++++++++++++++++++++++---
> 7 files changed, 37 insertions(+), 7 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..6bc761a31 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=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/samples/rust/hostprogs/.gitignore b/samples/rust/hostprogs/.gitignore
> index a6c173da5..d88a75790 100644
> --- a/samples/rust/hostprogs/.gitignore
> +++ b/samples/rust/hostprogs/.gitignore
> @@ -1,3 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0
>
> single
> +rustc-wrapper
> diff --git a/samples/rust/hostprogs/Makefile b/samples/rust/hostprogs/Makefile
> index 8ddcbd741..9c9663a77 100644
> --- a/samples/rust/hostprogs/Makefile
> +++ b/samples/rust/hostprogs/Makefile
> @@ -3,3 +3,5 @@
> hostprogs-always-y := single
>
> single-rust := y
> +
> +clean-files += rustc-wrapper
> 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..84fd87a53 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 wrapper script that forwards arguments to
> +# `HOSTRUSTC_LD` so that 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
> +
> +$(obj)/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=$(obj)/rustc-wrapper \
> -Clink-args='$(call escsq,$(KBUILD_HOSTLDFLAGS))' \
> $(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \
> $(HOSTRUSTFLAGS_$(target-stem))
> @@ -153,10 +172,11 @@ $(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 $(obj)/rustc-wrapper FORCE
> +$(call if_changed_dep,host-rust)
>
> -targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
> +targets += $(obj)/rustc-wrapper \
> + $(host-csingle) $(host-cmulti) $(host-cobjs) \
> $(host-cxxmulti) $(host-cxxobjs) $(host-rust)
>
> # %.lex.o <- %.lex.c <- %.l
--
Yoann Congal
Smile ECS
prev parent reply other threads:[~2026-04-17 7:59 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-16 21:15 [PATCH v7] kbuild: host: use single executable for rustc -C linker Mohamad Alsadhan
2026-04-17 7:59 ` Yoann Congal [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DHV9RMX3PLT6.JO8708PRE7S@smile.fr \
--to=yoann.congal@smile.fr \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=mo@sdhn.cc \
--cc=nathan@kernel.org \
--cc=nsc@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox