diff --git a/CHANGES.txt b/CHANGES.txt index f05cf7cad..fe14e03a8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,6 +19,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Switch test framework from using profile to cProfile. profile generates deprecation warnings starting with Python 3.15. - Improve documentation of builder methods and builder objects. + - Make links clickable in the SetOption and GetOption manpage entries. RELEASE 4.10.0 - Thu, 02 Oct 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index e29657c13..0e4eb1f79 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -58,6 +58,8 @@ DOCUMENTATION - Improve documentation of builder methods and builder objects. +- Make links clickable in the SetOption and GetOption manpage entries. + DEVELOPMENT ----------- diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index a5184b6e9..afc037019 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -44,12 +44,15 @@ b"# SCons cache directory - see https://bford.info/cachedir/\n" ) +# Defaults for the cache subsystem's globals. Most of these are filled in by +# SCons.Script.Main._build_targets, once the command line has been parsed. cache_enabled = True cache_debug = False cache_force = False cache_show = False cache_readonly = False cache_tmp_uuid = uuid.uuid4().hex +cli_cache_dir = "" def CacheRetrieveFunc(target, source, env) -> int: t = target[0] @@ -210,9 +213,11 @@ def _mkdir_atomic(self, path: str) -> bool: return False try: + parent_dir = os.path.dirname(directory) + os.makedirs(parent_dir, exist_ok=True) # TODO: Python 3.7. See comment below. - # tempdir = tempfile.TemporaryDirectory(dir=os.path.dirname(directory)) - tempdir = tempfile.mkdtemp(dir=os.path.dirname(directory)) + # tempdir = tempfile.TemporaryDirectory(dir=os.path.dirname(parent_dir)) + tempdir = tempfile.mkdtemp(dir=os.path.dirname(parent_dir)) except OSError as e: msg = "Failed to create cache directory " + path raise SCons.Errors.SConsEnvironmentError(msg) from e @@ -273,18 +278,33 @@ def CacheDebug(self, fmt, target, cachefile) -> None: if cache_debug == '-': self.debugFP = sys.stdout elif cache_debug: + # TODO: this seems fragile. There can be only one debug output + # (terminal, file or none) per run, so it should not + # be reopened. Testing multiple caches showed a problem + # where reopening with 'w' mode meant some of the output + # was lost, so for the moment switched to append mode. + # . Keeping better track of the output file, or switching to + # using the logging module should help. The "persistence" + # of using append mode breaks test/CacheDir/debug.py def debug_cleanup(debugFP) -> None: debugFP.close() - self.debugFP = open(cache_debug, 'w') + self.debugFP = open(cache_debug, 'a') atexit.register(debug_cleanup, self.debugFP) else: self.debugFP = None self.current_cache_debug = cache_debug if self.debugFP: + # TODO: consider emitting more than the base filename to help + # distinguish retrievals across variantdirs (target.relpath?). + # Separately, showing more of the cache entry path would be + # useful for testing, though possibly not otherwise. How else + # can you tell which target went to which cache if there are >1? self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) - self.debugFP.write("requests: %d, hits: %d, misses: %d, hit rate: %.2f%%\n" % - (self.requests, self.hits, self.misses, self.hit_ratio)) + self.debugFP.write( + "requests: %d, hits: %d, misses: %d, hit rate: %.2f%%\n" % + (self.requests, self.hits, self.misses, self.hit_ratio) + ) @classmethod def copy_from_cache(cls, env, src, dst) -> str: @@ -336,7 +356,7 @@ def cachepath(self, node) -> tuple: Given a Node, obtain the configured cache directory and the path to the cached file, which is generated from the node's build signature. If caching is not enabled for the - None, return a tuple of None. + node, return a tuple of ``None``. """ if not self.is_enabled(): return None, None @@ -349,11 +369,11 @@ def cachepath(self, node) -> tuple: def retrieve(self, node) -> bool: """Retrieve a node from cache. - Returns True if a successful retrieval resulted. + Returns ``True`` if a successful retrieval resulted. This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in - built(). + :meth:`built`. Note that there's a special trick here with the execute flag (one that's not normally done for other actions). Basically diff --git a/SCons/Environment.py b/SCons/Environment.py index c42189624..6cdf0ec9b 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -43,6 +43,7 @@ import SCons.Action import SCons.Builder +import SCons.CacheDir import SCons.Debug from SCons.Debug import logInstanceCreation import SCons.Defaults @@ -1222,6 +1223,12 @@ def __init__( self._init_special() self.added_methods = [] + # If user specifies a --cache-dir on the command line, then + # use that for all created Environments, user can alter this + # by specifying CacheDir() per environment. + if SCons.CacheDir.cli_cache_dir: + self.CacheDir(SCons.CacheDir.cli_cache_dir) + # We don't use AddMethod, or define these as methods in this # class, because we *don't* want these functions to be bound # methods. They need to operate independently so that the diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 98f1f345a..591533014 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -1042,6 +1042,9 @@ def _main(parser): SCons.Node.implicit_deps_changed = options.implicit_deps_changed SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged + if options.cache_dir: + SCons.CacheDir.cli_cache_dir = options.cache_dir + if options.no_exec: SCons.SConf.dryrun = 1 SCons.Action.execute_actions = None diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 810e8aae5..747b81c60 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -345,9 +345,9 @@ atexit.register(print_build_failures) -Query the value of settable options which may have been set -on the command line, via option defaults, -or by using the &f-link-SetOption; function. +Query the value of settable option name, +which may have been set on the command line, via option defaults, +or using the &f-link-SetOption; function. The value of the option is returned in a type matching how the option was declared - see the documentation of the corresponding command line option for information about each specific @@ -369,7 +369,7 @@ processed prior to the &f-GetOption; call in the &SConscript; files. Query name - Command-line options + Command-line argument Notes @@ -377,46 +377,61 @@ processed prior to the &f-GetOption; call in the &SConscript; files. cache_debug - + + + + cache_disable + , + cache_force + , + cache_readonly - + + + cache_show - + + + clean + , , + climb_up - - - - + + , + , + , + , + @@ -425,162 +440,244 @@ processed prior to the &f-GetOption; call in the &SConscript; files. debug - + directory - , + + + , + + + diskcheck - + duplicate - + enable_virtualenv - + + + experimental - - since 4.2 + + + + Since 4.2. file + , , , + + + + + hash_chunksize + + + + + Replaces . Since 4.2 hash_format - - since 4.2 + + Since 4.2 help - , + + + , + + ignore_errors - , + + + , + + + ignore_virtualenv - + + + implicit_cache - + + + implicit_deps_changed - + + + implicit_deps_unchanged - + + + + + include_dir - , + + + , + + + install_sandbox - + + + Available only if the &t-link-install; tool has been called keep_going - , + + + , + + + max_drift - + md5_chunksize - , - + + + + Replaced by . + Deprecated since 4.2 - since 4.2 no_exec + , , , , + no_progress - + num_jobs - , + + , + package_type - + + + Available only if the &t-link-packaging; tool has been called profile_file - + question - , + + + , + + + random - + + + repository + , , + silent + , , + site_dir - , + + , + + stack_size - + + + taskmastertrace_file - + + + tree_printers - + warn - , + + + , + + + @@ -782,24 +879,22 @@ in many situations when defining target names that are not directly built. -Sets &scons; option variable name +Set option variable name to value. -These options are all also settable via -command-line options but the variable name -may differ from the command-line option name - -see the table for correspondences. -A value set via command-line option will take -precedence over one set with &f-SetOption;, which -allows setting a project default in the scripts and -temporarily overriding it via command line. -&f-SetOption; calls can also be placed in the +Settable options have corresponding command-line +arguments, which can be used for one-time overrides, +as a value set via command-line option will take +precedence over one set with &f-SetOption;. +The table shows the correspondence between the +option name and the command-line argument(s). +&f-SetOption; calls can also be placed in a site_init.py file. -See the documentation in the manpage for the -corresponding command line option for information about each specific option. -The value parameter is mandatory, +The behavior of the options is described in the +manpage entry for the command-line version. +The value parameter is mandatory; for option values which are boolean in nature (that is, the command line option does not take an argument) use a value @@ -821,7 +916,7 @@ added via an &f-link-AddOption; call, &f-SetOption; is available only after the &f-AddOption; call has completed successfully, and only if that call included the -settable=True argument. +settable=True keyword argument. @@ -836,7 +931,7 @@ The settable variables with their associated command-line options are: Settable name - Command-line options + Command-line argument Notes @@ -845,95 +940,134 @@ The settable variables with their associated command-line options are: clean - , - , - + + , + , + + diskcheck - + + + duplicate - + + + experimental - - since 4.2 + + + + Since 4.2. hash_chunksize - - Actually sets md5_chunksize. - since 4.2 + + + + Replaces . Since 4.2 hash_format - - since 4.2 + + + + Since 4.2 help - , + + , + + + implicit_cache - + + + implicit_deps_changed - + + + + + Also sets implicit_cache. - (settable since 4.2) + Settable since 4.2 implicit_deps_unchanged - + + + + + Also sets implicit_cache. - (settable since 4.2) + Settable since 4.2 + + max_drift - + + + md5_chunksize - + + + + + Synonym for . + Deprecated since 4.2 + no_exec - , - , - , - , - + + , + , + , + , + + no_progress - + + + See If no_progress is set via &f-SetOption; @@ -950,31 +1084,37 @@ The settable variables with their associated command-line options are: num_jobs - , + + , + random - + silent - , - , - + + , + , + + stack_size - + + + warn - + @@ -990,7 +1130,6 @@ SetOption('max_drift', 0) - diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index 2690a086c..7274e5f51 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -793,6 +793,13 @@ def opt_ignore(option, opt, value, parser) -> None: help="Print CacheDir debug info to FILE", metavar="FILE") + op.add_option('--cache-dir', + nargs=1, + dest='cache_dir', + metavar='CACHEDIR', + help='Enable the derived‑file cache and set its directory to CACHEDIR', + default="") + op.add_option('--cache-disable', '--no-cache', dest='cache_disable', default=False, action="store_true", diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py index f98cf3b21..ef3a8e692 100644 --- a/SCons/Script/SConscript.py +++ b/SCons/Script/SConscript.py @@ -47,6 +47,7 @@ import sys import traceback import time +import types class SConscriptReturn(Exception): pass @@ -667,42 +668,43 @@ def get_DefaultEnvironmentProxy(): return _DefaultEnvironmentProxy class DefaultEnvironmentCall: - """A class that implements "global function" calls of - Environment methods by fetching the specified method from the - DefaultEnvironment's class. Note that this uses an intermediate - proxy class instead of calling the DefaultEnvironment method - directly so that the proxy can override the subst() method and + """Create a "global function" from an Environment method. + + Fetches the *method_name* from the Environment instance created to hold + the Default Environment. Uses an intermediate proxy class instead of + calling the :meth:`~SCons.Defaults.DefaultEnvironment` function directly, + so that the proxy can override the ``subst()`` method and thereby prevent expansion of construction variables (since from the user's point of view this was called as a global function, - with no associated construction environment).""" - def __init__(self, method_name, subst: int=0) -> None: + with no associated construction environment). + """ + + def __init__(self, method_name, subst: bool = False) -> None: self.method_name = method_name if subst: self.factory = SCons.Defaults.DefaultEnvironment else: self.factory = get_DefaultEnvironmentProxy + def __call__(self, *args, **kw): env = self.factory() method = getattr(env, self.method_name) return method(*args, **kw) - -def BuildDefaultGlobals(): - """ - Create a dictionary containing all the default globals for - SConstruct and SConscript files. - """ - +def BuildDefaultGlobals() -> dict: + """Create a dict containing all the default globals for SConscript files.""" global GlobalDict if GlobalDict is None: - GlobalDict = {} - import SCons.Script + + GlobalDict = {} d = SCons.Script.__dict__ - def not_a_module(m, d=d, mtype=type(SCons.Script)) -> bool: - return not isinstance(d[m], mtype) + + def not_a_module(m, d=d) -> bool: + return not isinstance(d[m], types.ModuleType) + for m in filter(not_a_module, dir(SCons.Script)): - GlobalDict[m] = d[m] + GlobalDict[m] = d[m] return GlobalDict.copy() diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 98725d5ed..b20ffb883 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -708,6 +708,25 @@ derived-file cache specified by &f-link-CacheDir;. + + - + + + + Enable derived-file caching globally, using + cachedir + as the cache directory. + An individual &consenv; may still specify a different + cache directory by calling &f-link-env-CacheDir;. + + + Added in version NEXT_RELEASE. + + + + , @@ -2815,18 +2834,19 @@ method (see below). Builder Methods -You tell &SCons; what to build -by calling Builders, -functions which take particular action(s) -to produce target(s) of a particular type -(conventionally hinted at by the builder name, e.g. &Program;) -from the specified source files. -A builder call is a declaration: &SCons; enters the -specified relationship into its internal dependency node graph, -and only later makes the decision on whether anything is actually built, -since this depends on command-line options, -target selection rules, and whether the target(s) are -out-of-date with respect to the sources. + +Builder methods are special functions +used to declare relationships in the build dependency graph. +Calling a Builder does not build anything directly, +but records the target or targets (what you want built), +sources (what it’s built from), +and the &consenv; to use for build settings. +The builder itself will typically have information +about producing targets which is also used, +except &b-link-Command;, where the caller is +responsible for specifying the action. +The &SCons; job runner later decides if and when +to initiate a build using this information. @@ -2845,28 +2865,15 @@ as described in the text of the respective entries. The target and source arguments -can be specified either as positional arguments, -in which case target comes first, or as -keyword arguments, using target= -and source=. -Although both arguments are nominally required, -if there is a single source and the target can be inferred -the target argument can be omitted (see below). -Builder methods also take a variety of -keyword arguments, described below. - - -Because long lists of file names -can lead to a lot of quoting in a builder call, -&SCons; -supplies a &f-link-Split; -global function -and a same-named environment method -that splits a single string -into a list, using -strings of white-space characters as the delimiter -(similar to the &Python; string split -method, but succeeds even if the input isn't a string). +can be specified either as positional or keyword arguments. +Some additional keyword arguments are recognized for all builders, +and any unknown keyword arguments are treated as temporary +&consvar; assignments. +You can specify sources and targets as a scalar or a list, +composed of either strings or nodes. +For convenience, the &f-link-Split; method +can be used to split a single whitespace-separated string into a list. + &SCons; can resolve paths to sources and targets @@ -3030,36 +3037,28 @@ if os.path.exists('#inc/foo.h'): env.Append(CPPPATH='#inc') -When the target shares the same base name -as the source and only the suffix varies, -and if the builder method has a suffix defined for the target file type, -then the target argument may be omitted completely, -and -&scons; -will deduce the target file name from -the source file name. -The following examples all build the -executable program -bar -(on POSIX systems) -or -bar.exe -(on Windows systems) -from the bar.c source file: - - -env.Program(target='bar', source='bar.c') -env.Program('bar', source='bar.c') -env.Program(source='bar.c') -env.Program('bar.c') - + +If &SCons; can deduce the target name from the source(s), +the target argument may be omitted. +If the Builder supports it, +&SCons; will take the base name of the first source file +and use it as the base name of the target, +adding the appropriate prefix/suffix. +For Builders designated as single-source, +a separate target is built for each source file. +If multiple sources are passed to a single-source builder, +the target argument must be omitted, +and each target will be given a name deduced from +the corresponding source file. + -The optional + +The optional srcdir keyword argument specifies that -all source file strings that are not absolute paths +source file strings that are not absolute or top-relative paths -shall be interpreted relative to the specified +are to be interpreted relative to the value of srcdir. The following example will build the build/prog @@ -3069,33 +3068,35 @@ on Windows) program from the files src/f1.c and -src/f2.c: +src/f2.c, +rather than looking for them in the directory of +of the &SConscript; file being evaluated. env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src') -The optional + +The optional parse_flags keyword argument causes behavior similar to the &f-link-env-MergeFlags; method, where the argument value is broken into individual settings and merged into the appropriate &consvars;. +The following example adds 'include' to +the &cv-link-CPPPATH; &consvar;, +'EBUG' to +&cv-link-CPPDEFINES;, +and 'm' to +&cv-link-LIBS;: env.Program('hello', 'hello.c', parse_flags='-Iinclude -DEBUG -lm') -This example adds 'include' to -the &cv-link-CPPPATH; &consvar;, -'EBUG' to -&cv-link-CPPDEFINES;, -and 'm' to -&cv-link-LIBS;. - - -The optional + +The optional chdir keyword argument specifies that the Builder's action(s) @@ -3117,19 +3118,23 @@ after the action is complete. -# scons will change to the "sub" subdirectory -# before executing the "cp" command. +# scons will change to the "sub/dir" subdirectory +# before executing the Copy action function env.Command( target='sub/dir/foo.out', source='sub/dir/foo.in', - action="cp dir/foo.in dir/foo.out", - chdir='sub', + action=Copy('${TARGET.file}', '${SOURCE.file}'), + chdir='sub/dir', ) -# Because chdir is not a string, scons will change to the -# target's directory ("sub/dir") before executing the -# "cp" command. -env.Command('sub/dir/foo.out', 'sub/dir/foo.in', "cp foo.in foo.out", chdir=True) +# Here, chdir is not a string, so scons will change to the +# target's directory ("sub/dir") to execute the action. +env.Command(' + target='sub/dir/foo.out', + source='sub/dir/foo.in', + action=Copy('${TARGET.file}', '${SOURCE.file}'), + chdir=True +) @@ -3147,18 +3152,13 @@ by &SCons; interfere with each other when they start changing directory. -Note that &SCons; will -not -automatically modify -its expansion of -&consvars; like &cv-link-TARGET; -and &cv-link-SOURCE; -when using the chdir -keyword argument--that is, -the expanded file names -will still be relative to -the project top directory, -and consequently incorrect + + +&SCons; does not account for +chdir +when it expands &consvars; +like &cv-link-TARGET; and &cv-link-SOURCE;, +so they will be incorrect relative to the chdir directory. If you use the chdir keyword argument, you will typically need to supply a different @@ -3166,9 +3166,11 @@ command line using expansions like ${TARGET.file} and -${SOURCE.file} -to use just the filename portion of the -target and source. +${SOURCE.file}, +which expand to the filename portion of the +target and source. + + Keyword arguments that are not specifically recognized are treated as &consvar; @@ -9073,7 +9075,7 @@ However, the following variables are imported by - + SCONS_LIB_DIR Specifies the directory that contains the &scons; @@ -9099,7 +9101,7 @@ so the command line can be used to override - + SCONS_CACHE_MSVC_CONFIG (Windows only). If set, save the shell environment variables diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 044422686..af7e00dfe 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -72,13 +72,22 @@ CacheDir('/usr/local/build_cache') + + A cache directory can also be specified on the command line + (--cache-dir=CACHEDIR) - useful for the case + where you have a permanent &f-link-CacheDir; call + but want to override it temporarily for a given build. + You can also create a cache directory specific to a &consenv; + by using the env.CacheDir() form. + + The cache directory you specify must have read and write access for all developers - who will be accessing the cached files - (if is used, - only read access is required). + who will be accessing the cached files. + If is used, + only read access is required. It should also be in some central location that all builds will be able to access. In environments where developers are using separate systems @@ -86,9 +95,11 @@ CacheDir('/usr/local/build_cache') this directory would typically be on a shared or NFS-mounted file system. While &SCons; will create the specified cache directory as needed, - in this multiuser scenario it is usually best - to create it ahead of time, so the access rights - can be set up correctly. + the underlying &Python; library function used for this will + create it accessbile only for the creating user ID, + so for a shared cache, it is usually best + to create it ahead of time, and manually set up + the access rights as needed. diff --git a/test/CacheDir/CacheDir_cli.py b/test/CacheDir/CacheDir_cli.py new file mode 100644 index 000000000..7849f0cbc --- /dev/null +++ b/test/CacheDir/CacheDir_cli.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# MIT License +# +# Copyright The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +""" +Test retrieving derived files from a CacheDir. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +cache = test.workpath('cache') + +src_aaa_out = test.workpath('src', 'aaa.out') +src_bbb_out = test.workpath('src', 'bbb.out') +src_ccc_out = test.workpath('src', 'ccc.out') +src_cat_out = test.workpath('src', 'cat.out') +src_all = test.workpath('src', 'all') + +test.subdir('src') + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(src, "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that building with -n and an empty cache reports that proper +# build operations would be taken, but that nothing is actually built +# and that the cache is still empty. +test.run(chdir='src', arguments=f'--cache-dir={cache} -n .', stdout=test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) +# Even if you do -n, the cache will be configured. +expect = ['CACHEDIR.TAG', 'config'] +found = sorted(os.listdir(cache)) +test.fail_test( + expect != found, + message=f"expected cachedir contents {expect}, found {found}", +) +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir='src', arguments=f'--cache-dir={cache} .') +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') +test.up_to_date(chdir='src', arguments='.') +test.run(chdir='src', arguments='-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir='src', arguments=f'--cache-dir={cache} .', stdout=test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) +test.must_not_exist(src_cat_out) +test.up_to_date(chdir='src', arguments='.') +test.run(chdir='src', arguments='-c .') + +# Verify that rebuilding with -n reports that everything was retrieved +# from the cache, but that nothing really was. +test.run(chdir='src', arguments=f'--cache-dir={cache} -n .', stdout=test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(chdir='src', arguments=f'--cache-dir={cache} -s .', stdout="") +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_not_exist(src_cat_out) +test.up_to_date(chdir='src', arguments='.') + +test.run(chdir='src', arguments='-c .') +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(chdir='src', arguments=f'--cache-dir={cache} .', stdout=test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match(['src', 'cat.out'], "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir='src', arguments='.') + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: