Re: Changes to --printf-directory handling in Make 4.4

Kaz Kylheku <[email protected]>
Newsgroups gmane.comp.gnu.make.general
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.