Hi Britton,
Thanks for taking the time to read my proposal. Good point about
documentation, should have included more use case. See below for some
additional example. If the proposal will be accepted - I look into creating
a documentation pages for "info make" for various use cases:
Is it usable only for leaves ?
- Short answer: It can be used also on non-leaves to limit rebuild, or
limit reuse of targets. In it's simplest form - it can be used on leaves
that (including leaf that depend on "PHONY" targets).
Below are the main use cases where new features will help, all
involve speeding up process when there is "expensive" operation, which can
be skipped MOST of the time, and it's OK to be slightly behind. I'm
implementing all of those cases with combination of macros/shell commands.
1. Cache (original example):
Assuming the build need a remote resource that take lot of time to
download/process, and does not change frequently, Adding "valid-max" make
it possible to declare - It's OK with this target if it was created in the
last day - no need to reload.
cache.json: private valid-max=1d
cache.json:
curl -o $@ https://example/api
2. Reduce excessive updates.
In some cases, the build process depend on files can be updated multiple
time. This could be a CI process that produces new artifacts on every
commit. when running heavy tests, it might not be practical to execute the
full test every time the remote artifact is recreated. Adding "valid-max="
on non-leaf node, limit the execution of the tests to no more than once
every 24 hours (1d).
tests.txt: private valid-for=1d
tests.txt: /path/to/remote/artifact.tar
tar xvf $<
run_tests
touch $@
3. Expiration of targets, when dependencies are incomplete.
In some cases, it's not possible to describe the full set of dependencies
(e.g., when it's dynamic), or when dependencies are "noisy". For example -
I had a case where a job had a dependency on remote artifacts and local
artifacts, and we wanted to force a policy that require the job to run (and
re-fetch) the remote artifacts, even if none of the local artifacts got
updated. Adding valid-max indicate that the job should be re-make if it's
older than 1 day, even of leave dependency was updated.
job.txt: private valid-max=1d
job.txt: /path/to/other/artifact.tgz
tar xvf $<
curl .... | tar xvf -
run_job.sh
touch job.txt
I hope that those examples show how the new functionality make it easier to
address common use cases when using make for CI/data integration processes
- where the simple DAG rule may result in non-practical results. I'm sure
that there are other potential usages for building practical processes for
testing, deployment, database integration - and other systems that do not
provide exact modification timestamp, or when it's not practical to use
simple DAG rules.
Regards,
Yair
On Tue, Feb 17, 2026 at 11:17 AM Britton Kerin <[email protected]>
wrote:
>
>
> On Mon, Feb 16, 2026, 1:46 PM Yair Lenga <[email protected]> wrote:
>
>> Hello,
>>
>> This patch introduces two optional target-specific variables,
>> 'valid-for' and 'valid-max', that allow controlling the freshness
>> and expiration of existing targets based on their modification time.
>>
>> Motivation
>> ----------
>>
>> GNU make treats an existing target with no prerequisites as
>> permanently up to date. In practice, many build workflows generate
>> cache or artifact files that should be reused while fresh but
>> periodically regenerated even without explicit dependencies. Today
>> this: requires introducing artificial phony prerequisites or external
>> timestamp logic.
>>
>> The new variables provide a declarative mechanism for this:
>>
>> valid-for = DURATION
>> If the target exists and its age is less than or equal to the
>> specified duration, make treats it as up to date and skips
>> dependency checking.
>>
>> valid-max = DURATION
>> If the target exists and its age exceeds the specified duration,
>> make forces a rebuild even if the target has no prerequisites.
>>
>> Durations are specified as either an integer number of seconds or a
>> sequence of <number><unit> segments without spaces, where unit is one
>> of 's', 'm', 'h', 'd', or 'w' (e.g. "300", "5m", "1d5h").
>>
>> When both variables are present, 'valid-for' takes precedence for
>> fresh targets, while 'valid-max' forces rebuild of expired targets.
>>
>
> I'm not sure I entirely understand this and think it could probably be
> better expressed for documentation at least. By fresh I assume you mean
> otherwise up-to-date?
>
>
>> Implementation
>> --------------
>>
>> The logic is implemented in update_file_1() via a small helper that
>> parses and evaluates the TTL variables. Invalid values are ignored
>> and reported under --debug=v. Successful use of the TTL is reported
>> under --debug=b.
>>
>> This approach preserves existing make semantics while enabling
>> expiration of leaf targets and optional dependency skipping for
>> recent artifacts.
>>
>
> Is it usable only for leaves? I guess these variables are not inherited
> by dependent targets (as other target-local variables are) since that would
> presumably add a bunch of undesired edges to the DAG?
>
>
>> -------
>> Example 1: Periodic reloading of remote resources that change very
>> frequently.
>>
>> cache.json: private valid-max=1d
>> cache.json:
>> curl -o $@ https://example/api
>>
>> The file will be regenerated once per day even without prerequisites.This
>> can be useful when a test job is running every few minutes, but loading of
>> remote resources is time consuming.
>>
>> Other use cases include periodically refreshing generated version
>> stamps, expiring downloaded resources in reproducible builds, and
>> limiting reuse of expensive intermediate artifacts in CI or code
>> generation workflows.
>>
>> ---
>>
>> Patch attached. Comments welcome.
>>
>> Best regards,
>> Yair Lenga
>>
>