On 2023-07-04 10:08, Paul Smith wrote: > On Mon, 2023-07-03 at 16:32 +0100, Jonathan Wakely wrote: >> This left me puzzled for a while, but what's happening is that the output of >> $(shell $(MAKE) bar) >> now includes the --print-directory output, so where the recipe for >> 'foo' used to do simply `make baz` now it is `make make[1] Entering >> directory etc.` >> >> Is this an intended consequence of the changes to --print-directory >> handling in 4.4? > > I don't think it's related to that. > > I think it's related to this change: > >> * WARNING: Backward-incompatibility! >> Previously makefile variables marked as export were not exported to >> commands >> started by the $(shell ...) function. Now, all exported variables are >> exported to $(shell ...). If this leads to recursion during expansion, >> then >> for backward-compatibility the value from the original environment is used. >> To detect this change search for 'shell-export' in the .FEATURES variable. > > Previously the only environment variables passed to $(shell ...) > commands were the ones inherited from the parent environment; changes > made in the makefile were not sent to $(shell ...). Now, exported > variables (such as MAKEFLAGS) set in the makefile itself are also > passed to $(shell ...) commands.
May I suggest an ugly, but potentially general solution sketch? This relies on GNU Coreutils env, GNU grep. The idea is to create a custom shell invocation command which uses an arbitrarily sanitized environment. We can remove unwanted variables from it. The command is invoked as $(call sh,<command>) rather than $(shell <command>). How this works is that we generate a command of the following form which executes in the environment in which there are unwanted variables: env -i -- "var1=$var1" "var2=$var2" ... /bin/sh -c 'target command' Because this shell command has all the environments in its dynamic scope, it is able to interpolate their values into the env command line. But "env -i" clears the environment to empty, and then performs the specified environment bindings, so then the payload /bin/sh -c 'target command' executes in an environment which has only those variables that we choose to mention in the env command line. Implementation follows: # Get list of all environment variable names visible to shell command env_var_names := $(shell env -0 | grep -z -E -o '^[^=]+' | tr '\0' '\n') # Filter out environment names starting with MAKE env_var_names := $(filter-out MAKE,$(env_var_names)) # Shell command which invokes with only the filtered environment: use as $(call sh, ...) # Get list of all environment variable names visible to shell command env_var_names := $(shell env -0 | grep -z -E -o '^[^=]+' | tr '\0' '\n') # Filter out environment names starting with MAKE env_var_names := $(filter-out MAKE,$(env_var_names)) # Shell command which invokes with only the filtered environment: use as $(call sh, ...) sh = $(shell env -i -- $(foreach name,$(env_var_names),"$(name)=$$$(name)") /bin/sh -c '$(1)') # Invoke 'env > out' command via $(call sh, ...) $(call sh, env > out) # Print content of out: no MAKE variables should be shown all: cat out # Invoke 'env > out' command via $(call sh, ...) $(call sh, env > out) # Print content of out: no MAKE variables should be shown all: cat out