public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
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


      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