diff options
Diffstat (limited to 'doc/user/depends.in')
-rw-r--r-- | doc/user/depends.in | 1872 |
1 files changed, 0 insertions, 1872 deletions
diff --git a/doc/user/depends.in b/doc/user/depends.in deleted file mode 100644 index de2e11c..0000000 --- a/doc/user/depends.in +++ /dev/null @@ -1,1872 +0,0 @@ -<!-- - - Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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. - ---> - - <para> - - So far we've seen how &SCons; handles one-time builds. - But one of the main functions of a build tool like &SCons; - is to rebuild only what is necessary - when source files change--or, put another way, - &SCons; should <emphasis>not</emphasis> - waste time rebuilding things that don't need to be rebuilt. - You can see this at work simply by re-invoking &SCons; - after building our simple &hello; example: - - </para> - - <scons_example name="ex1"> - <file name="SConstruct"> - Program('hello.c') - </file> - <file name="hello.c"> - int main() { printf("Hello, world!\n"); } - </file> - </scons_example> - - <scons_output example="ex1" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - <para> - - The second time it is executed, - &SCons; realizes that the &hello; program - is up-to-date with respect to the current &hello_c; source file, - and avoids rebuilding it. - You can see this more clearly by naming - the &hello; program explicitly on the command line: - - </para> - - <scons_output example="ex1" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - Note that &SCons; reports <literal>"...is up to date"</literal> - only for target files named explicitly on the command line, - to avoid cluttering the output. - - </para> - - <section> - <title>Deciding When an Input File Has Changed: the &Decider; Function</title> - - <para> - - Another aspect of avoiding unnecessary rebuilds - is the fundamental build tool behavior - of <emphasis>rebuilding</emphasis> - things when an input file changes, - so that the built software is up to date. - By default, - &SCons; keeps track of this through an - MD5 &signature;, or checksum, of the contents of each file, - although you can easily configure - &SCons; to use the - modification times (or time stamps) - instead. - You can even specify your own Python function - for deciding if an input file has changed. - - </para> - - <section> - <title>Using MD5 Signatures to Decide if a File Has Changed</title> - - <para> - - By default, - &SCons; keeps track of whether a file has changed - based on an MD5 checksum of the file's contents, - not the file's modification time. - This means that you may be surprised by the - default &SCons; behavior if you are used to the - &Make; convention of forcing - a rebuild by updating the file's modification time - (using the &touch; command, for example): - - </para> - - <scons_output example="ex1" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>touch hello.c</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - Even though the file's modification time has changed, - &SCons; realizes that the contents of the - &hello_c; file have <emphasis>not</emphasis> changed, - and therefore that the &hello; program - need not be rebuilt. - This avoids unnecessary rebuilds when, - for example, someone rewrites the - contents of a file without making a change. - But if the contents of the file really do change, - then &SCons; detects the change - and rebuilds the program as required: - - </para> - - <scons_output example="ex1" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - Note that you can, if you wish, - specify this default behavior - (MD5 signatures) explicitly - using the &Decider; function as follows: - - </para> - - <sconstruct> - Program('hello.c') - Decider('MD5') - </sconstruct> - - <para> - - You can also use the string <literal>'content'</literal> - as a synonym for <literal>'MD5'</literal> - when calling the &Decider; function. - - </para> - - <section> - <title>Ramifications of Using MD5 Signatures</title> - - <para> - - Using MD5 signatures to decide if an input file has changed - has one surprising benefit: - if a source file has been changed - in such a way that the contents of the - rebuilt target file(s) - will be exactly the same as the last time - the file was built, - then any "downstream" target files - that depend on the rebuilt-but-not-changed target - file actually need not be rebuilt. - - </para> - - <para> - - So if, for example, - a user were to only change a comment in a &hello_c; file, - then the rebuilt &hello_o; file - would be exactly the same as the one previously built - (assuming the compiler doesn't put any build-specific - information in the object file). - &SCons; would then realize that it would not - need to rebuild the &hello; program as follows: - - </para> - - <scons_output example="ex1" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command output=" [CHANGE A COMMENT IN hello.c]" edit="STRIP CCCOM line">edit hello.c</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - In essence, &SCons; - "short-circuits" any dependent builds - when it realizes that a target file - has been rebuilt to exactly the same file as the last build. - This does take some extra processing time - to read the contents of the target (&hello_o;) file, - but often saves time when the rebuild that was avoided - would have been time-consuming and expensive. - - </para> - - </section> - - </section> - - <section> - <title>Using Time Stamps to Decide If a File Has Changed</title> - - <para> - - If you prefer, you can - configure &SCons; to use the modification time - of a file, not the file contents, - when deciding if a target needs to be rebuilt. - &SCons; gives you two ways to use time stamps - to decide if an input file has changed - since the last time a target has been built. - - </para> - - <para> - - The most familiar way to use time stamps - is the way &Make; does: - that is, have &SCons; decide - that a target must be rebuilt - if a source file's modification time is - <emphasis>newer</emphasis> - than the target file. - To do this, call the &Decider; - function as follows: - - </para> - - <scons_example name="newer"> - <file name="SConstruct" printme="1"> - Object('hello.c') - Decider('timestamp-newer') - </file> - <file name="hello.c"> - int main() { printf("Hello, world!\n"); } - </file> - </scons_example> - - <para> - - This makes &SCons; act like &Make; - when a file's modification time is updated - (using the &touch; command, for example): - - </para> - - <scons_output example="newer" os="posix"> - <scons_output_command>scons -Q hello.o</scons_output_command> - <scons_output_command>touch hello.c</scons_output_command> - <scons_output_command>scons -Q hello.o</scons_output_command> - </scons_output> - - <para> - - And, in fact, because this behavior is the same - as the behavior of &Make;, - you can also use the string <literal>'make'</literal> - as a synonym for <literal>'timestamp-newer'</literal> - when calling the &Decider; function: - - </para> - - <sconstruct> - Object('hello.c') - Decider('make') - </sconstruct> - - <para> - - One drawback to using times stamps exactly like &Make; - is that if an input file's modification time suddenly - becomes <emphasis>older</emphasis> than a target file, - the target file will not be rebuilt. - This can happen if an old copy of a source file is restored - from a backup archive, for example. - The contents of the restored file will likely be different - than they were the last time a dependent target was built, - but the target won't be rebuilt - because the modification time of the source file - is not newer than the target. - - </para> - - <para> - - Because &SCons; actually stores information - about the source files' time stamps whenever a target is built, - it can handle this situation by checking for - an exact match of the source file time stamp, - instead of just whether or not the source file - is newer than the target file. - To do this, specify the argument - <literal>'timestamp-match'</literal> - when calling the &Decider; function: - - </para> - - <scons_example name="match"> - <file name="SConstruct" printme="1"> - Object('hello.c') - Decider('timestamp-match') - </file> - <file name="hello.c"> - int main() { printf("Hello, world!\n"); } - </file> - </scons_example> - - <para> - - When configured this way, - &SCons; will rebuild a target whenever - a source file's modification time has changed. - So if we use the <literal>touch -t</literal> - option to change the modification time of - &hello_c; to an old date (January 1, 1989), - &SCons; will still rebuild the target file: - - </para> - - <scons_output example="match" os="posix"> - <scons_output_command>scons -Q hello.o</scons_output_command> - <scons_output_command>touch -t 198901010000 hello.c</scons_output_command> - <scons_output_command>scons -Q hello.o</scons_output_command> - </scons_output> - - <para> - - In general, the only reason to prefer - <literal>timestamp-newer</literal> - instead of - <literal>timestamp-match</literal>, - would be if you have some specific reason - to require this &Make;-like behavior of - not rebuilding a target when an otherwise-modified - source file is older. - - </para> - - </section> - - <section> - <title>Deciding If a File Has Changed Using Both MD Signatures and Time Stamps</title> - - <para> - - As a performance enhancement, - &SCons; provides a way to use - MD5 checksums of file contents - but to read those contents - only when the file's timestamp has changed. - To do this, call the &Decider; - function with <literal>'MD5-timestamp'</literal> - argument as follows: - - </para> - - <scons_example name="MD5-timestamp"> - <file name="SConstruct" printme="1"> - Program('hello.c') - Decider('MD5-timestamp') - </file> - <file name="hello.c"> - int main() { printf("Hello, world!\n"); } - </file> - </scons_example> - - <para> - - So configured, &SCons; will still behave like - it does when using <literal>Decider('MD5')</literal>: - - </para> - - <!-- - - We want to generate the output as follows, - but our "surrogate" system for generating the - output seems to get this wrong. - Just in-line the output for now. - - <scons_output example="MD5-timestamp" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>touch hello.c</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - --> - - <screen> - % <userinput>scons -Q hello</userinput> - cc -o hello.o -c hello.c - cc -o hello hello.o - % <userinput>touch hello.c</userinput> - % <userinput>scons -Q hello</userinput> - scons: `hello' is up to date. - % <userinput>edit hello.c</userinput> - [CHANGE THE CONTENTS OF hello.c] - % <userinput>scons -Q hello</userinput> - cc -o hello.o -c hello.c - cc -o hello hello.o - </screen> - - <para> - - However, the second call to &SCons; in the above output, - when the build is up-to-date, - will have been performed by simply looking at the - modification time of the &hello_c; file, - not by opening it and performing - an MD5 checksum calcuation on its contents. - This can significantly speed up many up-to-date builds. - - </para> - - <para> - - The only drawback to using - <literal>Decider('MD5-timestamp')</literal> - is that &SCons; will <emphasis>not</emphasis> - rebuild a target file if a source file was modified - within one second of the last time &SCons; built the file. - While most developers are programming, - this isn't a problem in practice, - since it's unlikely that someone will have built - and then thought quickly enough to make a substantive - change to a source file within one second. - Certain build scripts or - continuous integration tools may, however, - rely on the ability to apply changes to files - automatically and then rebuild as quickly as possible, - in which case use of - <literal>Decider('MD5-timestamp')</literal> - may not be appropriate. - - </para> - - </section> - - <section> - <title>Writing Your Own Custom &Decider; Function</title> - - <para> - - The different string values that we've passed to - the &Decider; function are essentially used by &SCons; - to pick one of several specific internal functions - that implement various ways of deciding if a dependency - (usually a source file) - has changed since a target file has been built. - As it turns out, - you can also supply your own function - to decide if a dependency has changed. - - </para> - - <para> - - For example, suppose we have an input file - that contains a lot of data, - in some specific regular format, - that is used to rebuild a lot of different target files, - but each target file really only depends on - one particular section of the input file. - We'd like to have each target file depend on - only its section of the input file. - However, since the input file may contain a lot of data, - we want to open the input file only if its timestamp has changed. - This could be done with a custom - &Decider; function that might look something like this: - - </para> - - <scons_example name="function"> - <file name="SConstruct" printme="1"> - Program('hello.c') - def decide_if_changed(dependency, target, prev_ni): - if self.get_timestamp() != prev_ni.timestamp: - dep = str(dependency) - tgt = str(target) - if specific_part_of_file_has_changed(dep, tgt): - return True - return False - Decider(decide_if_changed) - </file> - <file name="hello.c"> - int main() { printf("Hello, world!\n"); } - </file> - </scons_example> - - <para> - - Note that in the function definition, - the <varname>dependency</varname> - (input file) is the first argument, - and then the ⌖. - Both of these are passed to the functions as - SCons &Node; objects, - which we convert to strings using the Python - <function>str()</function>. - - </para> - - <para> - - The third argument, <varname>prev_ni</varname>, - is an object that holds the - signature or timestamp information - that was recorded about the dependency - the last time the target was built. - A <varname>prev_ni</varname> object can hold - different information, - depending on the type of thing that the - <varname>dependency</varname> argument represents. - For normal files, - the <varname>prev_ni</varname> object - has the following attributes: - - </para> - - <variablelist> - - <varlistentry> - <term>.csig</term> - - <listitem> - <para> - The <emphasis>content signature</emphasis>, - or MD5 checksum, of the contents of the - <varname>dependency</varname> - file the list time the ⌖ was built. - </para> - </listitem> - - </varlistentry> - - <varlistentry> - <term>.size</term> - - <listitem> - <para> - The size in bytes of the <varname>dependency</varname> - file the list time the target was built. - </para> - </listitem> - - </varlistentry> - - <varlistentry> - <term>.timestamp</term> - - <listitem> - <para> - The modification time of the <varname>dependency</varname> - file the list time the ⌖ was built. - </para> - </listitem> - - </varlistentry> - - </variablelist> - - <para> - - Note that ignoring some of the arguments - in your custom &Decider; function - is a perfectly normal thing to do, - if they don't impact the way you want to - decide if the dependency file has changed. - - </para> - - <para> - - Another thing to look out for is the fact that the three - attributes above may not be present at the time of the first run. - Without any prior build, no targets have been created and no - <filename>.sconsign</filename> DB file exists yet. - So, you should always check whether the - <varname>prev_ni</varname> attribute in question is available. - - </para> - - <para> - - We finally present a small example for a - <varname>csig</varname>-based decider function. Note how the - signature information for the <varname>dependency</varname> file - has to get initialized via <function>get_csig</function> - during each function call (this is mandatory!). - - </para> - - <sconstruct> - env = Environment() - - def config_file_decider(dependency, target, prev_ni): - import os.path - - # We always have to init the .csig value... - dep_csig = dependency.get_csig() - # .csig may not exist, because no target was built yet... - if 'csig' not in dir(prev_ni): - return True - # Target file may not exist yet - if not os.path.exists(str(target.abspath)): - return True - if dep_csig != prev_ni.csig: - # Some change on source file => update installed one - return True - return False - - def update_file(): - f = open("test.txt","a") - f.write("some line\n") - f.close() - - update_file() - - # Activate our own decider function - env.Decider(config_file_decider) - - env.Install("install","test.txt") - </sconstruct> - - </section> - - <section> - <title>Mixing Different Ways of Deciding If a File Has Changed</title> - - <para> - - The previous examples have all demonstrated calling - the global &Decider; function - to configure all dependency decisions that &SCons; makes. - Sometimes, however, you want to be able to configure - different decision-making for different targets. - When that's necessary, you can use the - <function>env.Decider</function> - method to affect only the configuration - decisions for targets built with a - specific construction environment. - - </para> - - <para> - - For example, if we arbitrarily want to build - one program using MD5 checkums - and another using file modification times - from the same source - we might configure it this way: - - </para> - - <scons_example name="mixing"> - <file name="SConstruct" printme="1"> - env1 = Environment(CPPPATH = ['.']) - env2 = env1.Clone() - env2.Decider('timestamp-match') - env1.Program('prog-MD5', 'program1.c') - env2.Program('prog-timestamp', 'program2.c') - </file> - <file name="program1.c"> - #include "inc.h" - int main() { printf("Hello, world!\n"); } - </file> - <file name="program2.c"> - #include "inc.h" - int main() { printf("Hello, world!\n"); } - </file> - <file name="inc.h"> - #define INC 1 - </file> - </scons_example> - - <para> - - If both of the programs include the same - <filename>inc.h</filename> file, - then updating the modification time of - <filename>inc.h</filename> - (using the &touch; command) - will cause only <filename>prog-timestamp</filename> - to be rebuilt: - - </para> - - <scons_output example="mixing" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>touch inc.h</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - </section> - - </section> - - <section> - <title>Older Functions for Deciding When an Input File Has Changed</title> - - <para> - - &SCons; still supports two functions that used to be the - primary methods for configuring the - decision about whether or not an input file has changed. - These functions have been officially deprecated - as &SCons; version 2.0, - and their use is discouraged, - mainly because they rely on a somewhat - confusing distinction between how - source files and target files are handled. - These functions are documented here mainly in case you - encounter them in older &SConscript; files. - - </para> - - <section> - <title>The &SourceSignatures; Function</title> - - <para> - - The &SourceSignatures; function is fairly straightforward, - and supports two different argument values - to configure whether source file changes should be decided - using MD5 signatures: - - </para> - - <sconstruct> - Program('hello.c') - SourceSignatures('MD5') - </sconstruct> - - <para> - - Or using time stamps: - - </para> - - <sconstruct> - Program('hello.c') - SourceSignatures('timestamp') - </sconstruct> - - <para> - - These are roughly equivalent to specifying - <function>Decider('MD5')</function> - or - <function>Decider('timestamp-match')</function>, - respectively, - although it only affects how SCons makes - decisions about dependencies on - <emphasis>source</emphasis> files--that is, - files that are not built from any other files. - - </para> - - </section> - - <section> - <title>The &TargetSignatures; Function</title> - - <para> - - The &TargetSignatures; function - specifies how &SCons; decides - when a target file has changed - <emphasis>when it is used as a - dependency of (input to) another target</emphasis>--that is, - the &TargetSignatures; function configures - how the signatures of "intermediate" target files - are used when deciding if a "downstream" target file - must be rebuilt. - <footnote><para> - This easily-overlooked distinction between - how &SCons; decides if the target itself must be rebuilt - and how the target is then used to decide if a different - target must be rebuilt is one of the confusing - things that has led to the &TargetSignatures; - and &SourceSignatures; functions being - replaced by the simpler &Decider; function. - </para></footnote> - - </para> - - <para> - - The &TargetSignatures; function supports the same - <literal>'MD5'</literal> and <literal>'timestamp'</literal> - argument values that are supported by the &SourceSignatures;, - with the same meanings, but applied to target files. - That is, in the example: - - </para> - - <sconstruct> - Program('hello.c') - TargetSignatures('MD5') - </sconstruct> - - <para> - - The MD5 checksum of the &hello_o; target file - will be used to decide if it has changed since the last - time the "downstream" &hello; target file was built. - And in the example: - - </para> - - <sconstruct> - Program('hello.c') - TargetSignatures('timestamp') - </sconstruct> - - <para> - - The modification time of the &hello_o; target file - will be used to decide if it has changed since the last - time the "downstream" &hello; target file was built. - - </para> - - <para> - - The &TargetSignatures; function supports - two additional argument values: - <literal>'source'</literal> and <literal>'build'</literal>. - The <literal>'source'</literal> argument - specifies that decisions involving - whether target files have changed - since a previous build - should use the same behavior - for the decisions configured for source files - (using the &SourceSignatures; function). - So in the example: - - </para> - - <sconstruct> - Program('hello.c') - TargetSignatures('source') - SourceSignatures('timestamp') - </sconstruct> - - <para> - - All files, both targets and sources, - will use modification times - when deciding if an input file - has changed since the last - time a target was built. - - </para> - - <para> - - Lastly, the <literal>'build'</literal> argument - specifies that &SCons; should examine - the build status of a target file - and always rebuild a "downstream" target - if the target file was itself rebuilt, - without re-examining the contents or timestamp - of the newly-built target file. - If the target file was not rebuilt during - this &scons; invocation, - then the target file will be examined - the same way as configured by - the &SourceSignature; call - to decide if it has changed. - - </para> - - <para> - - This mimics the behavior of - <literal>build signatures</literal> - in earlier versions of &SCons;. - A &buildsignature; re-combined - signatures of all the input files - that went into making the target file, - so that the target file itself - did not need to have its contents read - to compute an MD5 signature. - This can improve performance for some configurations, - but is generally not as effective as using - <literal>Decider('MD5-timestamp')</literal>. - - </para> - - </section> - - </section> - - <section> - <title>Implicit Dependencies: The &cv-CPPPATH; Construction Variable</title> - - <para> - - Now suppose that our "Hello, World!" program - actually has an <literal>#include</literal> line - to include the &hello_h; file in the compilation: - - </para> - - <scons_example name="include"> - <file name="SConstruct"> - Program('hello.c', CPPPATH = '.') - </file> - <file name="hello.c" printme="1"> - #include <hello.h> - int - main() - { - printf("Hello, %s!\n", string); - } - </file> - <file name="hello.h"> - #define string "world" - </file> - </scons_example> - - <para> - - And, for completeness, the &hello_h; file looks like this: - - </para> - - <scons_example_file example="include" name="hello.h"> - </scons_example_file> - - <para> - - In this case, we want &SCons; to recognize that, - if the contents of the &hello_h; file change, - the &hello; program must be recompiled. - To do this, we need to modify the - &SConstruct; file like so: - - </para> - - <scons_example_file example="include" name="SConstruct"> - </scons_example_file> - - <para> - - The &cv-link-CPPPATH; value - tells &SCons; to look in the current directory - (<literal>'.'</literal>) - for any files included by C source files - (<filename>.c</filename> or <filename>.h</filename> files). - With this assignment in the &SConstruct; file: - - </para> - - <scons_output example="include" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command output=" [CHANGE THE CONTENTS OF hello.h]">edit hello.h</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - First, notice that &SCons; - added the <literal>-I.</literal> argument - from the &cv-CPPPATH; variable - so that the compilation would find the - &hello_h; file in the local directory. - - </para> - - <para> - - Second, realize that &SCons; knows that the &hello; - program must be rebuilt - because it scans the contents of - the &hello_c; file - for the <literal>#include</literal> lines that indicate - another file is being included in the compilation. - &SCons; records these as - <emphasis>implicit dependencies</emphasis> - of the target file, - Consequently, - when the &hello_h; file changes, - &SCons; realizes that the &hello_c; file includes it, - and rebuilds the resulting &hello; program - that depends on both the &hello_c; and &hello_h; files. - - </para> - - <para> - - Like the &cv-link-LIBPATH; variable, - the &cv-CPPPATH; variable - may be a list of directories, - or a string separated by - the system-specific path separation character - (':' on POSIX/Linux, ';' on Windows). - Either way, &SCons; creates the - right command-line options - so that the following example: - - </para> - - <scons_example name="ex5"> - <file name="SConstruct" printme="1"> - Program('hello.c', CPPPATH = ['include', '/home/project/inc']) - </file> - <file name="hello.c"> - int main() { printf("Hello, world!\n"); } - </file> - </scons_example> - - <para> - - Will look like this on POSIX or Linux: - - </para> - - <scons_output example="ex5" os="posix"> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - And like this on Windows: - - </para> - - <scons_output example="ex5" os="win32"> - <scons_output_command>scons -Q hello.exe</scons_output_command> - </scons_output> - - </section> - - <section> - <title>Caching Implicit Dependencies</title> - - <para> - - Scanning each file for <literal>#include</literal> lines - does take some extra processing time. - When you're doing a full build of a large system, - the scanning time is usually a very small percentage - of the overall time spent on the build. - You're most likely to notice the scanning time, - however, when you <emphasis>rebuild</emphasis> - all or part of a large system: - &SCons; will likely take some extra time to "think about" - what must be built before it issues the - first build command - (or decides that everything is up to date - and nothing must be rebuilt). - - <!-- - Isn't this expensive? The answer is, it depends. If you do a full build of a - large system, the scanning time is insignificant. If you do a rebuild of a - large system, then Cons will spend a fair amount of time thinking about it - before it decides that nothing has to be done (although not necessarily more - time than make!). The good news is that Cons makes it very easy to - intelligently subset your build, when you are working on localized changes. - --> - - </para> - - <para> - - In practice, having &SCons; scan files saves time - relative to the amount of potential time - lost to tracking down subtle problems - introduced by incorrect dependencies. - Nevertheless, the "waiting time" - while &SCons; scans files can annoy - individual developers waiting for their builds to finish. - Consequently, &SCons; lets you cache - the implicit dependencies - that its scanners find, - for use by later builds. - You can do this by specifying the - &implicit-cache; option on the command line: - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -Q --implicit-cache hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - If you don't want to specify &implicit-cache; - on the command line each time, - you can make it the default behavior for your build - by setting the &implicit_cache; option - in an &SConscript; file: - - </para> - - <sconstruct> - SetOption('implicit_cache', 1) - </sconstruct> - - <para> - - &SCons; does not cache implicit dependencies like this by default - because the &implicit-cache; causes &SCons; to simply use the implicit - dependencies stored during the last run, without any checking - for whether or not those dependencies are still correct. - Specifically, this means &implicit-cache; instructs &SCons; - to <emphasis>not</emphasis> rebuild "correctly" in the - following cases: - - - </para> - - <itemizedlist> - - <listitem> - <para> - - When &implicit-cache; is used, &SCons; will ignore any changes that - may have been made to search paths - (like &cv-CPPPATH; or &cv-LIBPATH;,). - This can lead to &SCons; not rebuilding a file if a change to - &cv-CPPPATH; would normally cause a different, same-named file from - a different directory to be used. - - </para> - </listitem> - - <listitem> - <para> - - When &implicit-cache; is used, &SCons; will not detect if a - same-named file has been added to a directory that is earlier in - the search path than the directory in which the file was found - last time. - - </para> - </listitem> - - </itemizedlist> - - <section> - <title>The &implicit-deps-changed; Option</title> - - <para> - - When using cached implicit dependencies, - sometimes you want to "start fresh" - and have &SCons; re-scan the files - for which it previously cached the dependencies. - For example, - if you have recently installed a new version of - external code that you use for compilation, - the external header files will have changed - and the previously-cached implicit dependencies - will be out of date. - You can update them by - running &SCons; with the &implicit-deps-changed; option: - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -Q --implicit-deps-changed hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - In this case, &SCons; will re-scan all of the implicit dependencies - and cache updated copies of the information. - - </para> - - </section> - - <section> - <title>The &implicit-deps-unchanged; Option</title> - - <para> - - By default when caching dependencies, - &SCons; notices when a file has been modified - and re-scans the file for any updated - implicit dependency information. - Sometimes, however, you may want - to force &SCons; to use the cached implicit dependencies, - even if the source files changed. - This can speed up a build for example, - when you have changed your source files - but know that you haven't changed - any <literal>#include</literal> lines. - In this case, - you can use the &implicit-deps-unchanged; option: - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -Q --implicit-deps-unchanged hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - In this case, - &SCons; will assume that the cached implicit - dependencies are correct and - will not bother to re-scan changed files. - For typical builds after small, - incremental changes to source files, - the savings may not be very big, - but sometimes every bit of - improved performance counts. - - </para> - - </section> - - <!-- - - <section> - <title>XXX max drift</title> - - XXX SetOption('max_drift') - - </section> - - --> - - </section> - - <section> - <title>Explicit Dependencies: the &Depends; Function</title> - - <para> - - Sometimes a file depends on another file - that is not detected by an &SCons; scanner. - For this situation, - &SCons; allows you to specific explicitly that one file - depends on another file, - and must be rebuilt whenever that file changes. - This is specified using the &Depends; method: - - </para> - - <programlisting> - hello = Program('hello.c') - Depends(hello, 'other_file') - </programlisting> - - <!-- XXX mention that you can use arrays for target and source? --> - - <screen> - % <userinput>scons -Q hello</userinput> - cc -c hello.c -o hello.o - cc -o hello hello.o - % <userinput>scons -Q hello</userinput> - scons: `hello' is up to date. - % <userinput>edit other_file</userinput> - [CHANGE THE CONTENTS OF other_file] - % <userinput>scons -Q hello</userinput> - cc -c hello.c -o hello.o - cc -o hello hello.o - </screen> - - <para> - - Note that the dependency - (the second argument to &Depends;) - may also be a list of Node objects - (for example, as returned by a call to a Builder): - - </para> - - <programlisting> - hello = Program('hello.c') - goodbye = Program('goodbye.c') - Depends(hello, goodbye) - </programlisting> - - <para> - - in which case the dependency or dependencies - will be built before the target(s): - - </para> - - <screen> - % <userinput>scons -Q hello</userinput> - cc -c goodbye.c -o goodbye.o - cc -o goodbye goodbye.o - cc -c hello.c -o hello.o - cc -o hello hello.o - </screen> - - </section> - - <section> - <title>Dependencies From External Files: the &ParseDepends; - Function</title> - - <para> - - &SCons; has built-in scanners for a number of languages. Sometimes - these scanners fail to extract certain implicit dependencies due - to limitations of the scanner implementation. - - </para> - - <para> - - The following example illustrates a case where the built-in C - scanner is unable to extract the implicit dependency on a header - file. - - </para> - - <scons_example name="macroinc"> - <file name="hello.c" printme="1"> - #define FOO_HEADER <foo.h> - #include FOO_HEADER - - int main() { - return FOO; - } - </file> - <file name="SConstruct"> - Program('hello', 'hello.c', CPPPATH='.') - </file> - <file name="foo.h"> - #define FOO 42 - </file> - </scons_example> - - <scons_output example="macroinc" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command output=" [CHANGE CONTENTS OF foo.h]" - >edit foo.h</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - <para> - - Apparently, the scanner does not know about the header dependency. - Being not a full-fledged C preprocessor, the scanner does not - expand the macro. - - </para> - - <para> - - In these cases, you may also use the compiler to extract the - implicit dependencies. &ParseDepends; can parse the contents of - the compiler output in the style of &Make;, and explicitly - establish all of the listed dependencies. - - </para> - - <para> - - The following example uses &ParseDepends; to process a compiler - generated dependency file which is generated as a side effect - during compilation of the object file: - - </para> - - <!-- XXX The ParseDepends example below fakes proper working by a - priori specification of the dependency file. The produced hello.d - file is not found (or used) for unknown reasons. --> - - <scons_example name="parsedep"> - <file name="hello.c"> - #define FOO_HEADER <foo.h> - #include FOO_HEADER - - int main() { - return FOO; - } - </file> - <file name="SConstruct" printme="1"> - obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') - SideEffect('hello.d', obj) - ParseDepends('hello.d') - Program('hello', obj) - </file> - <file name="foo.h"> - #define FOO 42 - </file> - <file name="hello.d"> - hello.o: hello.c foo.h - </file> - </scons_example> - - <scons_output example="parsedep" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command output=" [CHANGE CONTENTS OF foo.h]" - >edit foo.h</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - <para> - - Parsing dependencies from a compiler-generated - <filename>.d</filename> file has a chicken-and-egg problem, that - causes unnecessary rebuilds: - - </para> - - <scons_example name="parsedeprebuild"> - <file name="hello.c"> - #define FOO_HEADER <foo.h> - #include FOO_HEADER - - int main() { - return FOO; - } - </file> - <file name="SConstruct"> - obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') - SideEffect('hello.d', obj) - ParseDepends('hello.d') - Program('hello', obj) - </file> - <file name="foo.h"> - #define FOO 42 - </file> - </scons_example> - - <!-- - <scons_output example="parsedeprebuild" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - --> - - <screen> - % <userinput>scons -Q</userinput> - cc -o hello.o -c -MD -MF hello.d -I. hello.c - cc -o hello hello.o - % <userinput>scons -Q --debug=explain</userinput> - scons: rebuilding `hello.o' because `foo.h' is a new dependency - cc -o hello.o -c -MD -MF hello.d -I. hello.c - % <userinput>scons -Q</userinput> - scons: `.' is up to date. - </screen> - - <para> - - In the first pass, the dependency file is generated while the - object file is compiled. At that time, &SCons; does not know about - the dependency on <filename>foo.h</filename>. In the second pass, - the object file is regenerated because <filename>foo.h</filename> - is detected as a new dependency. - - </para> - - <para> - - &ParseDepends; immediately reads the specified file at invocation - time and just returns if the file does not exist. A dependency - file generated during the build process is not automatically - parsed again. Hence, the compiler-extracted dependencies are not - stored in the signature database during the same build pass. This - limitation of &ParseDepends; leads to unnecessary recompilations. - Therefore, &ParseDepends; should only be used if scanners are not - available for the employed language or not powerful enough for the - specific task. - - </para> - - </section> - - <section> - <title>Ignoring Dependencies: the &Ignore; Function</title> - - <para> - - Sometimes it makes sense - to not rebuild a program, - even if a dependency file changes. - In this case, - you would tell &SCons; specifically - to ignore a dependency as follows: - - </para> - - <scons_example name="ignore"> - <file name="SConstruct" printme="1"> - hello_obj=Object('hello.c') - hello = Program(hello_obj) - Ignore(hello_obj, 'hello.h') - </file> - <file name="hello.c"> - #include "hello.h" - int main() { printf("Hello, %s!\n", string); } - </file> - <file name="hello.h"> - #define string "world" - </file> - </scons_example> - - <!-- XXX mention that you can use lists for target and source? --> - - <!-- - <scons_output example="ignore"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command output=" [CHANGE THE CONTENTS OF hello.h]">edit hello.h</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - XXX THIS EXAMPLE SHOULD BE UP-TO-DATE! XXX - </scons_output> - --> - - <screen> - % <userinput>scons -Q hello</userinput> - cc -c -o hello.o hello.c - cc -o hello hello.o - % <userinput>scons -Q hello</userinput> - scons: `hello' is up to date. - % <userinput>edit hello.h</userinput> - [CHANGE THE CONTENTS OF hello.h] - % <userinput>scons -Q hello</userinput> - scons: `hello' is up to date. - </screen> - - <para> - - Now, the above example is a little contrived, - because it's hard to imagine a real-world situation - where you wouldn't want to rebuild &hello; - if the &hello_h; file changed. - A more realistic example - might be if the &hello; - program is being built in a - directory that is shared between multiple systems - that have different copies of the - &stdio_h; include file. - In that case, - &SCons; would notice the differences between - the different systems' copies of &stdio_h; - and would rebuild &hello; - each time you change systems. - You could avoid these rebuilds as follows: - - </para> - - <programlisting> - hello = Program('hello.c', CPPPATH=['/usr/include']) - Ignore(hello, '/usr/include/stdio.h') - </programlisting> - - <para> - &Ignore; can also be used to prevent a generated file from being built - by default. This is due to the fact that directories depend on - their contents. So to ignore a generated file from the default build, - you specify that the directory should ignore the generated file. - Note that the file will still be built if the user specifically - requests the target on scons command line, or if the file is - a dependency of another file which is requested and/or is built - by default. - </para> - - <scons_example name="ignore_explicit"> - <file name="SConstruct" printme="1"> - hello_obj=Object('hello.c') - hello = Program(hello_obj) - Ignore('.',[hello,hello_obj]) - </file> - <file name="hello.c"> - #include "stdio.h" - int main() { printf("Hello!\n"); } - </file> - </scons_example> - - <scons_output example="ignore_explicit" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - </section> - - <section> - <title>Order-Only Dependencies: the &Requires; Function</title> - - <para> - - Occasionally, - it may be useful to specify that a certain - file or directory must, if necessary, - be built or created before some other target is built, - but that changes to that file or directory - do <emphasis>not</emphasis> - require that the target itself be rebuilt. - Such a relationship is called an - <emphasis>order-only dependency</emphasis> - because it only affects the order in which - things must be built--the dependency before the target--but - it is not a strict dependency relationship - because the target should not - change in response to changes in the dependent file. - - </para> - - <para> - - For example, suppose that you want to create a file - every time you run a build - that identifies the time the build was performed, - the version number, etc., - and which is included in every program that you build. - The version file's contents will change every build. - If you specify a normal dependency relationship, - then every program that depends on - that file would be rebuilt every time you ran &SCons;. - For example, we could use some Python code in - a &SConstruct; file to create a new <filename>version.c</filename> file - with a string containing the current date every time - we run &SCons;, - and then link a program with the resulting object file - by listing <filename>version.c</filename> in the sources: - - </para> - - <scons_example name="no-Requires"> - <file name="SConstruct" printme="1"> - import time - - version_c_text = """ - char *date = "%s"; - """ % time.ctime(time.time()) - open('version.c', 'w').write(version_c_text) - - hello = Program(['hello.c', 'version.c']) - </file> - <file name="hello.c"> - extern char *date; - int main() { printf("Hello, %s! I was built: %s\n", date); } - </file> - </scons_example> - - <para> - - If we list <filename>version.c</filename> as an actual source file, - though, then the <filename>version.o</filename> file - will get rebuilt every time we run &SCons; - (because the &SConstruct; file itself changes - the contents of <filename>version.c</filename>) - and the <filename>hello</filename> executable - will get re-linked every time - (because the <filename>version.o</filename> file changes): - - </para> - - <scons_output example="no-Requires"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>sleep 1</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>sleep 1</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - <para> - - (Note that for the above example to work, - we &sleep; for one second in between each run, - so that the &SConstruct; file will create a - <filename>version.c</filename> file with a time string - that's one second later than the previous run.) - - </para> - - <para> - - One solution is to use the &Requires; function - to specify that the <filename>version.o</filename> - must be rebuilt before it is used by the link step, - but that changes to <filename>version.o</filename> - should not actually cause the <filename>hello</filename> - executable to be re-linked: - - </para> - - <scons_example name="Requires"> - <file name="SConstruct" printme="1"> - import time - - version_c_text = """ - char *date = "%s"; - """ % time.ctime(time.time()) - open('version.c', 'w').write(version_c_text) - - version_obj = Object('version.c') - - hello = Program('hello.c', - LINKFLAGS = str(version_obj[0])) - - Requires(hello, version_obj) - </file> - <file name="hello.c"> - extern char *date; - int main() { printf("Hello, %s! I was built: %s\n", date); } - </file> - </scons_example> - - <para> - - Notice that because we can no longer list <filename>version.c</filename> - as one of the sources for the <filename>hello</filename> program, - we have to find some other way to get it into the link command line. - For this example, we're cheating a bit and stuffing the - object file name (extracted from <literal>version_obj</literal> - list returned by the &b-Object; call) - into the &cv-link-LINKFLAGS; variable, - because &cv-LINKFLAGS; is already included - in the &cv-link-LINKCOM; command line. - - </para> - - <para> - - With these changes, - we get the desired behavior of only - re-linking the <filename>hello</filename> executable - when the <filename>hello.c</filename> has changed, - even though the <filename>version.o</filename> is rebuilt - (because the &SConstruct; file still changes the - <filename>version.c</filename> contents directly each run): - - </para> - - <scons_output example="Requires"> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>sleep 1</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>sleep 1</scons_output_command> - <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - <scons_output_command>sleep 1</scons_output_command> - <scons_output_command>scons -Q hello</scons_output_command> - </scons_output> - - </section> - - <section> - <title>The &AlwaysBuild; Function</title> - - <para> - - How &SCons; handles dependencies can also be affected - by the &AlwaysBuild; method. - When a file is passed to the &AlwaysBuild; method, - like so: - - </para> - - <scons_example name="AlwaysBuild"> - <file name="SConstruct" printme="1"> - hello = Program('hello.c') - AlwaysBuild(hello) - </file> - <file name="hello.c"> - int main() { printf("Hello, %s!\n", string); } - </file> - </scons_example> - - <para> - - Then the specified target file (&hello; in our example) - will always be considered out-of-date and - rebuilt whenever that target file is evaluated - while walking the dependency graph: - - </para> - - <scons_output example="AlwaysBuild"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - <para> - - The &AlwaysBuild; function has a somewhat misleading name, - because it does not actually mean the target file will - be rebuilt every single time &SCons; is invoked. - Instead, it means that the target will, in fact, - be rebuilt whenever the target file is encountered - while evaluating the targets specified on - the command line (and their dependencies). - So specifying some other target on the command line, - a target that does <emphasis>not</emphasis> - itself depend on the &AlwaysBuild; target, - will still be rebuilt only if it's out-of-date - with respect to its dependencies: - - </para> - - <scons_output example="AlwaysBuild"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q hello.o</scons_output_command> - </scons_output> - - <!-- - - XXX AlwaysBuild() and Alias Nodes - - XXX AlwaysBuild() and Dir Nodes - - XXX AlwaysBuild() with no sources - - --> - - </section> - - <!-- - - <section> - <title>The &Salt; Method</title> - - <para> - - XXX Salt() (are we going to implement this ?) - - original Cons classic POD documentation: - -=head2 The C<Salt> method - -The C<Salt> method adds a constant value to the signature calculation -for every derived file. It is invoked as follows: - - Salt $string; - -Changing the Salt value will force a complete rebuild of every derived -file. This can be used to force rebuilds in certain desired -circumstances. For example, - - Salt `uname -s`; - -Would force a complete rebuild of every derived file whenever the -operating system on which the build is performed (as reported by C<uname --s>) changes. - - </para> - - </section> - - --> |