[PATCH] make: add valid-for and valid-max target TTL support
Yair Lenga <[email protected]> Mon, 16 Feb 2026 13:24:21 -0500
| Newsgroups | gmane.comp.gnu.make.bugs |
|---|---|
| Message-ID | <CAK3_KpOm2VOu6iNMrAYucXjUTs0rZ4_VyMDWZL6ZJaM-SbqfpA@mail.gmail.com> |
--000000000000fb6399064af51023
Content-Type: multipart/alternative; boundary="000000000000fb6397064af51021"
--000000000000fb6397064af51021
Content-Type: text/plain; charset="UTF-8"
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.
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.
-------
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
--000000000000fb6397064af51021
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">Hello,<br><br>This patch introduces two optional target-sp=
ecific variables,<br>'valid-for' and 'valid-max', that allo=
w controlling the freshness<br>and expiration of existing targets based on =
their modification time.<br><br>Motivation<br>----------<br><br>GNU make tr=
eats an existing target with no prerequisites as<br>permanently up to date.=
=C2=A0 In practice, many build workflows generate<br>cache or artifact file=
s that should be reused while fresh but<br>periodically regenerated even wi=
thout explicit dependencies.=C2=A0 Today<br>this:=C2=A0 requires introducin=
g artificial phony prerequisites or external<br>timestamp logic.<br><br>The=
new variables provide a declarative mechanism for this:<br><br>=C2=A0 vali=
d-for =3D DURATION<br>=C2=A0 =C2=A0 =C2=A0 If the target exists and its age=
is less than or equal to the<br>=C2=A0 =C2=A0 =C2=A0 specified duration, m=
ake treats it as up to date and skips<br>=C2=A0 =C2=A0 =C2=A0 dependency ch=
ecking.<br><br>=C2=A0 valid-max =3D DURATION<br>=C2=A0 =C2=A0 =C2=A0 If the=
target exists and its age exceeds the specified duration,<br>=C2=A0 =C2=A0=
=C2=A0 make forces a rebuild even if the target has no prerequisites.<br><=
br>Durations are specified as either an integer number of seconds or a<br>s=
equence of <number><unit> segments without spaces, where unit i=
s one<br>of 's', 'm', 'h', 'd', or 'w&#=
39; (e.g. "300", "5m", "1d5h").<br><br>When b=
oth variables are present, 'valid-for' takes precedence for<br>fres=
h targets, while 'valid-max' forces rebuild of expired targets.<br>=
<br>Implementation<br>--------------<br><br>The logic is implemented in upd=
ate_file_1() via a small helper that<br>parses and evaluates the TTL variab=
les.=C2=A0 Invalid values are ignored<br>and reported under --debug=3Dv.=C2=
=A0 Successful use of the TTL is reported<br>under --debug=3Db.<br><br>This=
approach preserves existing make semantics while enabling<br>expiration of=
leaf targets and optional dependency skipping for<br>recent artifacts.<br>=
<br>-------<br>Example 1: Periodic reloading of remote resources that chang=
e very frequently.<br><br>=C2=A0 cache.json: private valid-max=3D1d<br>=C2=
=A0 cache.json:<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 curl -o $@ <a href=3D=
"https://example/api">https://example/api</a><br><br>The file will be regen=
erated once per day even without prerequisites.This can be useful when a te=
st job is running every few minutes, but loading of remote resources is tim=
e consuming.<div><br></div><div>Other use cases include periodically refres=
hing generated version<br>stamps, expiring downloaded resources in reproduc=
ible builds, and<br>limiting reuse of expensive intermediate artifacts in C=
I or code<br>generation workflows.</div><div><br></div><div>---</div><div><=
br></div><div>Patch attached. Comments welcome.</div><div><br></div><div>Be=
st regards,</div><div>Yair Lenga</div></div>
--000000000000fb6397064af51021--
--000000000000fb6399064af51023
Content-Type: application/octet-stream;
name="0001-Add-support-valid-for-and-valid-max.patch"
Content-Disposition: attachment;
filename="0001-Add-support-valid-for-and-valid-max.patch"
Content-Transfer-Encoding: base64
Content-ID: <f_mlpi6xmh0>
X-Attachment-Id: f_mlpi6xmh0
RnJvbSAzNGVlOTA0NTE3YjM3Mjg1NjdlNDA1NWM4ZDMxMGZlNmU3ZTJkY2JiIE1vbiBTZXAgMTcg
MDA6MDA6MDAgMjAwMQpGcm9tOiBZYWlyIExlbmdhIDx5YWlyLmxlbmdhQGdtYWlsLmNvbT4KRGF0
ZTogTW9uLCAxNiBGZWIgMjAyNiAxMjo1Nzo1MCAtMDUwMApTdWJqZWN0OiBbUEFUQ0hdIEFkZCBz
dXBwb3J0ICJ2YWxpZC1mb3IiIGFuZCAidmFsaWQtbWF4IgoKLS0tCiBzcmMvcmVtYWtlLmMgICAg
ICB8IDE3OSArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrCiB0
ZXN0LXZhbGlkLWZvci5tayB8ICA1OCArKysrKysrKysrKysrKysKIDIgZmlsZXMgY2hhbmdlZCwg
MjM3IGluc2VydGlvbnMoKykKIGNyZWF0ZSBtb2RlIDEwMDY0NCB0ZXN0LXZhbGlkLWZvci5tawoK
ZGlmZiAtLWdpdCBhL3NyYy9yZW1ha2UuYyBiL3NyYy9yZW1ha2UuYwppbmRleCBhZGYzOWQ0Ni4u
ODUwY2YzZWIgMTAwNjQ0Ci0tLSBhL3NyYy9yZW1ha2UuYworKysgYi9zcmMvcmVtYWtlLmMKQEAg
LTQ3MSw2ICs0NzEsMTY1IEBAIGNvbXBsYWluIChzdHJ1Y3QgZmlsZSAqZmlsZSkKICAgICB9CiB9
CiAKKy8qIE5hbWUgb2YgdGhlIHRhcmdldC1zcGVjaWZpYyB2YXJpYWJsZSB0aGF0IHNwZWNpZmll
cyBhIFRUTAorICAgRFVSSU5HIHdoaWNoIGFuIGV4aXN0aW5nIHRhcmdldCBpcyBjb25zaWRlcmVk
IHVwIHRvIGRhdGUKKyAgIHdpdGhvdXQgY2hlY2tpbmcgaXRzIHByZXJlcXVpc2l0ZXMuCisKKyAg
IFRoZSB2YWx1ZSBtdXN0IGJlIGEgY29uY3JldGUgZHVyYXRpb24gc3RyaW5nIGNvbnNpc3Rpbmcg
b2YKKyAgIGEgcG9zaXRpdmUgaW50ZWdlciBvcHRpb25hbGx5IGZvbGxvd2VkIGJ5IGEgc2luZ2xl
IHVuaXQKKyAgIGNoYXJhY3RlcjogJ3MnIChzZWNvbmRzKSwgJ20nIChtaW51dGVzKSwgJ2gnICho
b3VycyksIG9yCisgICAnZCcgKGRheXMpLiAgSWYgbm8gdW5pdCBpcyBnaXZlbiwgc2Vjb25kcyBh
cmUgYXNzdW1lZC4KKworICAgTGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZSBpcyBpZ25v
cmVkLiAgVmFsdWVzCisgICBjb250YWluaW5nIHVuZXhwYW5kZWQgJyQnIHJlZmVyZW5jZXMgYXJl
IGlnbm9yZWQuICAqLworc3RhdGljIGNvbnN0IGNoYXIgdmFsaWRfZm9yX3ZhcltdID0gInZhbGlk
LWZvciI7CisKKy8qIE5hbWUgb2YgdGhlIHRhcmdldC1zcGVjaWZpYyB2YXJpYWJsZSB0aGF0IHNw
ZWNpZmllcyBhIFRUTAorICAgQUZURVIgd2hpY2ggYW4gZXhpc3RpbmcgdGFyZ2V0IGlzIGNvbnNp
ZGVyZWQgb3V0IG9mIGRhdGUKKyAgIGFuZCBtdXN0IGJlIHJlY3JlYXRlZCwgZXZlbiBpZiBpdCBp
cyBuZXdlciB0aGFuIGl0cyBwcmVyZXF1aXNpdGVzLiAKKyAgIFRoaXMgaXMgdGhlIG9wcG9zaXRl
IG9mIHZhbGlkLWZvciwgYW5kIGlzIHVzZWZ1bCBmb3IgdGFyZ2V0cyB0aGF0CisgICBoYXZlIGV4
cGVuc2l2ZSBkZXBlbmRlbmNpZXMgb3IgY29tcGxleCBkZXBlbmRlY2llcywgd2hpY2ggY2FuCisg
ICBub3QgYmUgY2FwdHVyZWQgaW4gYSBydWxlCisKKyAgIFRoZSB2YWx1ZSBpcyB0aGUgc2FtZSB0
aGUgJ3ZhbGlkX2ZvcicuCisqLworCitzdGF0aWMgY29uc3QgY2hhciB2YWxpZF9tYXhfdmFyW10g
PSAidmFsaWQtbWF4IjsKKworLyogUGFyc2UgVFRMIHN0cmluZ3MgbGlrZTogICIzMCIsICIzMHMi
LCAiNW0iLCAiMmgiLCAiN2QiCisgICBSZXR1cm5zIDEgb24gc3VjY2VzcyBhbmQgc3RvcmVzIHNl
Y29uZHMgaW4gKm91dC4KKyAgIFJldHVybnMgMCBvbiBwYXJzZSBlcnJvciBvciBub24tcG9zaXRp
dmUgVFRMLiAqLworc3RhdGljIGludAorcGFyc2VfdmFsaWRfdGltZSAoY29uc3QgY2hhciAqcywg
dGltZV90ICpvdXQpCit7CisgIGNoYXIgKmVuZDsKKyAgY29uc3QgY2hhciAqdGFpbCA7CisgIGxv
bmcgZHVyYXRpb24gPSAwIDsKKyAgbG9uZyBuOworICBsb25nIG11bHQgOworCisgIGlmIChzID09
IDApCisgICAgcmV0dXJuIDA7CisKKyAgd2hpbGUgKGlzc3BhY2UgKCh1bnNpZ25lZCBjaGFyKSpz
KSkKKyAgICArK3M7CisKKyAgdGFpbCA9IHMgKyBzdHJsZW4ocykgOworICB3aGlsZSAodGFpbCA+
IHMgJiYgaXNzcGFjZSAoKHVuc2lnbmVkIGNoYXIpdGFpbFstMV0pKQorICAgIC0tdGFpbDsKKwor
ICAgIC8qIENoZWNrIGZvciBzaW1wbGUgaW50ZWdlciBjYXNlIChzZWNvbmRzKSAqLworICBuID0g
c3RydG9sIChzLCAmZW5kLCAxMCk7CisgIGlmICggcyAhPSBlbmQgJiYgZW5kID09IHRhaWwgKSB7
CisgICAgKm91dCA9ICh0aW1lX3QpIG47CisgICAgcmV0dXJuIDE7CisgIH0KKworICB3aGlsZSAo
IHMgPCB0YWlsICkgeworICAgIGlmICggcyA9PSBlbmQgKQorICAgICAgcmV0dXJuIDAgOworICAg
IC8qIG5vIHZhbHVlICovCisKKyAgICAvKiBDaGVjayBmb3IgYSB1bml0IGNoYXJhY3Rlci4gICov
CisgICAgc3dpdGNoICgqZW5kICkKKyAgICB7CisgICAgICBjYXNlICdzJzogbXVsdCA9IDE7IGJy
ZWFrOworICAgICAgY2FzZSAnbSc6IG11bHQgPSA2MDsgYnJlYWs7CisgICAgICBjYXNlICdoJzog
bXVsdCA9IDYwICogNjA7IGJyZWFrOworICAgICAgY2FzZSAnZCc6IG11bHQgPSAyNCAqIDYwICog
NjA7IGJyZWFrOworICAgICAgY2FzZSAndyc6IG11bHQgPSA3ICogMjQgKiA2MCAqIDYwOyBicmVh
azsKKyAgICAgIGRlZmF1bHQ6IHJldHVybiAwOworICAgIH0gICAgCisgICAgZHVyYXRpb24gKz0g
biAqIG11bHQ7CisgICAgcyA9IGVuZCArIDE7CisgICAgbiA9IHN0cnRvbCAocywgJmVuZCwgMTAp
OworICB9CisKKyAgKm91dCA9IGR1cmF0aW9uIDsKKyAgcmV0dXJuIDE7Cit9CisKK3N0YXRpYyBj
b25zdCBjaGFyICpwYXJzZV92YWxpZF92YXIoc3RydWN0IGZpbGUgKmZpbGUsIGNvbnN0IGNoYXIg
KnZhcikKK3sKKyAgc3RydWN0IHZhcmlhYmxlICp2OworICBjb25zdCBjaGFyICp0dGxfdGV4dDsK
KyAgdiA9IGxvb2t1cF92YXJpYWJsZV9mb3JfZmlsZSAodmFyLCBzdHJsZW4gKHZhciksIGZpbGUp
OworICBpZiAoIXYgfHwgIXYtPnZhbHVlIHx8ICF2LT52YWx1ZVswXSkKKyAgICByZXR1cm4gMDsK
KworICB0dGxfdGV4dCA9IHYtPnZhbHVlOworCisgIHdoaWxlIChpc3NwYWNlICgodW5zaWduZWQg
Y2hhcikqdHRsX3RleHQpKQorICAgICsrdHRsX3RleHQ7CisKKyAgaWYgKCp0dGxfdGV4dCA9PSAn
XDAnKQorICAgIHJldHVybiAwOworCisgIHJldHVybiB0dGxfdGV4dCA7Cit9CisKKworLyogUmV0
dXJuIDEgaWYgRklMRSBhZ2UgaXMgc3RpbGwgd2l0aGluIGl0cyAidmFsaWQtZm9yIiBUVEwsIGFu
ZCBkb2VzIG5vdAorICBuZWVkIHRvIGJlIHJlbWFkZSwgcmVnYXJkbGVzcyBvZiB0aGUgYWdlIG9m
IGl0cyBwcmVyZXF1aXNpdGVzLiAqLworc3RhdGljIGludAorcGFyc2VfdmFsaWRfZm9yX3AgKHN0
cnVjdCBmaWxlICpmaWxlLCB0aW1lX3QgYWdlLCB1bnNpZ25lZCBpbnQgZGVwdGgpCit7CisgIGNv
bnN0IGNoYXIgKnR0bF90ZXh0OworICB0aW1lX3QgdmFsaWRfdW50aWw7CisKKyAgdHRsX3RleHQg
PSBwYXJzZV92YWxpZF92YXIoZmlsZSwgdmFsaWRfZm9yX3Zhcik7CisKKyAgaWYgKCB0dGxfdGV4
dCA9PSAwICkKKyAgICByZXR1cm4gMDsKKworICBpZiAoIXBhcnNlX3ZhbGlkX3RpbWUgKHR0bF90
ZXh0LCAmdmFsaWRfdW50aWwpKSB7CisgICAgREJTKERCX1ZFUkJPU0UsCisgICAgICAgKCJJZ25v
cmluZyAlcyB2YWx1ZSBvbiB0YXJnZXQgJXM6ICclcycgKGJhZCB2YWx1ZSlcbiIsCisgICAgICAg
IHZhbGlkX2Zvcl92YXIsIGZpbGUtPm5hbWUsIHR0bF90ZXh0KSk7CisgICAgcmV0dXJuIDA7Cisg
IH0KKworICBpZiAoIGFnZSA8IHZhbGlkX3VudGlsKSB7CisgICAgREJTKERCX0JBU0lDLAorICAg
ICAgICgiTWFya2luZyAlczogdXAgdG8gZGF0ZSAoJXM9JyVzJywgcmVtYWluZyB0dGw9JWxkIHNl
Y29uZHMpXG4iLAorICAgICAgICBmaWxlLT5uYW1lLCB2YWxpZF9mb3JfdmFyLCB0dGxfdGV4dCwg
KGxvbmcpICh2YWxpZF91bnRpbCAtIGFnZSkpKTsKKyAgICByZXR1cm4gMTsKKyAgfQorCisgIHJl
dHVybiAwOworfQorCisvKiBSZXR1cm4gMSBpZiB0aGUgZmlsZSBhZ2UgaXMgYWJvdmUgdmFsaWQt
bWF4LCBhbmQgaGFzIHRvIGJlIHJlY3JlYXRlZCByZWdhcmRsZXNzCisgICBvZiB0aGUgYWdlIG9m
IGl0cyBwcmVyZXF1aXNpdGVzLgorICovCitzdGF0aWMgaW50CitjaGVja192YWxpZF9tYXhfcCAo
c3RydWN0IGZpbGUgKmZpbGUsIGxvbmcgYWdlLCB1bnNpZ25lZCBpbnQgZGVwdGgpCit7CisgIGNv
bnN0IGNoYXIgKnR0bF90ZXh0OworICB0aW1lX3QgZXhwaXJlX2F0OworCisgIHR0bF90ZXh0ID0g
cGFyc2VfdmFsaWRfdmFyKGZpbGUsIHZhbGlkX21heF92YXIpOworCisgIGlmICggdHRsX3RleHQg
PT0gMCApCisgICAgcmV0dXJuIDA7CisKKyAgaWYgKCFwYXJzZV92YWxpZF90aW1lICh0dGxfdGV4
dCwgJmV4cGlyZV9hdCkpIHsKKyAgICBEQlMoREJfVkVSQk9TRSwKKyAgICAgICAoIklnbm9yaW5n
ICVzIHZhbHVlIG9uIHRhcmdldCAlczogJyVzJyAoYmFkIHZhbHVlKVxuIiwKKyAgICAgICAgdmFs
aWRfZm9yX3ZhciwgZmlsZS0+bmFtZSwgdHRsX3RleHQpKTsKKyAgICByZXR1cm4gMDsKKyAgfQor
ICBpZiAoYWdlID4gZXhwaXJlX2F0KSB7CisgICAgREJTKERCX0JBU0lDLAorICAgICAgICgiTWFy
a2luZyAlcyBvdXQgb2YgZGF0ZSAoJXM9JXMpLCBleHBpcmVkPSVsZCBzZWNvbmRzIGFnbylcbiIs
CisgICAgICAgIGZpbGUtPm5hbWUsIHZhbGlkX21heF92YXIsIHR0bF90ZXh0LCAobG9uZykgKGFn
ZSAtIGV4cGlyZV9hdCkpKTsKKyAgICByZXR1cm4gMTsKKyAgfQorCisgIHJldHVybiAwOworfQor
CisKIC8qIENvbnNpZGVyIGEgc2luZ2xlICdzdHJ1Y3QgZmlsZScgYW5kIHVwZGF0ZSBpdCBhcyBh
cHByb3ByaWF0ZS4KICAgIFJldHVybiBhbiB1cGRhdGVfc3RhdHVzIHZhbHVlOyB1c2UgdXNfc3Vj
Y2VzcyBpZiB3ZSBhcmVuJ3Qgc3VyZSB5ZXQuICAqLwogCkBAIC01OTEsNiArNzUwLDI2IEBAIHVw
ZGF0ZV9maWxlXzEgKHN0cnVjdCBmaWxlICpmaWxlLCB1bnNpZ25lZCBpbnQgZGVwdGgpCiAKICAg
bXVzdF9tYWtlID0gbm9leGlzdDsKIAorICAvKiB2YWxpZC1mb3IsIHZhbGlkLW1heCBUVEwgY2Fj
aGUgc2hvcnRjdXQgKi8KKyAgaWYgKCFub2V4aXN0ICYmICFmaWxlLT5waG9ueSkgeworICAgIGlu
dCByZXNvbHV0aW9uIDsKKyAgICBGSUxFX1RJTUVTVEFNUCBub3cgPSBmaWxlX3RpbWVzdGFtcF9u
b3coJnJlc29sdXRpb24pIDsKKyAgICBsb25nIGFnZSA9IEZJTEVfVElNRVNUQU1QX1MgKG5vdykg
LSBGSUxFX1RJTUVTVEFNUF9TKHRoaXNfbXRpbWUpIDsKKworICAgIGlmICggcGFyc2VfdmFsaWRf
Zm9yX3AgKGZpbGUsIGFnZSwgZGVwdGgpKQorICAgIHsKKyAgICAgIGZpbGUtPnVwZGF0ZWQgPSAx
OworICAgICAgZmlsZS0+dXBkYXRlX3N0YXR1cyA9IHVzX3N1Y2Nlc3M7CisgICAgICBub3RpY2Vf
ZmluaXNoZWRfZmlsZSAoZmlsZSk7CisgICAgICByZXR1cm4gdXNfc3VjY2VzczsKKyAgICB9Cisg
ICAgZWxzZSBpZiAoIGNoZWNrX3ZhbGlkX21heF9wIChmaWxlLCBhZ2UsIGRlcHRoKSkKKyAgICB7
CisgICAgICBtdXN0X21ha2UgPSAxIDsKKyAgICB9CisKKyAgfQorCiAgIC8qIElmIGZpbGUgd2Fz
IHNwZWNpZmllZCBhcyBhIHRhcmdldCB3aXRoIG5vIGNvbW1hbmRzLCBjb21lIHVwIHdpdGggc29t
ZQogICAgICBkZWZhdWx0IGNvbW1hbmRzLiAgVGhpcyBtYXkgYWxzbyBhZGQgbW9yZSBhbHNvX21h
a2UgZmlsZXMuICAqLwogCmRpZmYgLS1naXQgYS90ZXN0LXZhbGlkLWZvci5tayBiL3Rlc3QtdmFs
aWQtZm9yLm1rCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAwLi4xOTFmMjY3MQot
LS0gL2Rldi9udWxsCisrKyBiL3Rlc3QtdmFsaWQtZm9yLm1rCkBAIC0wLDAgKzEsNTggQEAKKy5Q
SE9OWTogYWxsIGNyZWF0ZSBjbGVhbiBkZWJ1ZworCitNS0ZJTEUgPSAkKGxhc3R3b3JkICQoTUFL
RUZJTEVfTElTVCkpCisKK1RFU1RTID0gdmFsaWQtbWFrZS50IHZhbGlkLWtlZXAudCBleHBpcmUt
bWFrZS50IGV4cGlyZS1rZWVwLnQgdDMta2VlcC50IHQ1LWtlZXAudCB0Ny1tYWtlLnQKKworYWxs
OiBjcmVhdGUKKwkkKE1BS0UpIC1yIC1mICQoTUtGSUxFKSB0ZXN0CisJZWNobyAiUmVzdWx0czoi
CisJZ3JlcCAnJyAkKFRFU1RTKQorCitkZWJ1ZzogY3JlYXRlCisJJChNQUtFKSAtciAtZCAtZiAk
KE1LRklMRSkgdGVzdCA+ICRALmxvZworCWVjaG8gIlJlc3VsdHM6IgorCWdyZXAgJycgJChURVNU
UykKKwllY2hvICJEZWJ1ZyBpbmZvcm1hdGlvbiBpbiAkQC5sb2ciCisKK2NsZWFuOgorCXJtIC1m
IC0tICQoVEVTVFMpCisKK3Rlc3Q6ICQoVEVTVFMpCisKK2NyZWF0ZToKKwlmb3IgZiBpbiAkKFRF
U1RTKSA7IGRvIGVjaG8ga2VlcCA+ICQkZiA7IGRvbmUKKwl0b3VjaCAtZCAiNSBtaW4gYWdvIiAk
KFRFU1RTKQorCXRvdWNoIC1kICIzIG1pbiBhZ28iIHQzLWtlZXAudAorCXRvdWNoIC1kICI3IG1p
biBhZ28iIHQ3LW1ha2UudAorCisuUEhPTlk6IEZPUkNFCisKK3ZhbGlkLW1ha2UudDogcHJpdmF0
ZSB2YWxpZC1mb3I9NG0KK3ZhbGlkLW1ha2UudDogRk9SQ0UKKwllY2hvICJtYWtlIiA+ICRACisK
K3ZhbGlkLWtlZXAudDogcHJpdmF0ZSB2YWxpZC1mb3I9Nm0KK3ZhbGlkLWtlZXAudDogRk9SQ0UK
KwllY2hvICJtYWtlIiA+ICRACisKK2V4cGlyZS1tYWtlLnQ6IHByaXZhdGUgdmFsaWQtbWF4PTRt
CitleHBpcmUtbWFrZS50OgorCWVjaG8gIm1ha2UiID4gJEAKKworZXhwaXJlLWtlZXAudDogcHJp
dmF0ZSB2YWxpZC1tYXg9Nm0KK2V4cGlyZS1rZWVwLnQ6CisJZWNobyAibWFrZSIgPiAkQAorCit0
My1rZWVwLnQgdDUta2VlcC50IHQ3LW1ha2UudDogcHJpdmF0ZSB2YWxpZC1mb3I9NG0KK3QzLWtl
ZXAudCB0NS1rZWVwLnQgdDctbWFrZS50OiBwcml2YXRlIHZhbGlkLW1heD02bQorCit0My1rZWVw
LnQ6CisJZWNobyAibWFrZSIgPiAkQAorCit0NS1rZWVwLnQ6CisJZWNobyAibWFrZSIgPiAkQAor
Cit0Ny1tYWtlLnQ6IAorCWVjaG8gIm1ha2UiID4gJEAKKwotLSAKMi40My4wCgo=
--000000000000fb6399064af51023--