mirror of
https://github.com/Relintai/scons_gd.git
synced 2025-02-06 16:25:59 +01:00
1715 lines
50 KiB
XML
1715 lines
50 KiB
XML
|
<?xml version='1.0'?>
|
||
|
<!DOCTYPE sconsdoc [
|
||
|
<!ENTITY % scons SYSTEM "../scons.mod">
|
||
|
%scons;
|
||
|
|
||
|
<!ENTITY % builders-mod SYSTEM "../generated/builders.mod">
|
||
|
%builders-mod;
|
||
|
<!ENTITY % functions-mod SYSTEM "../generated/functions.mod">
|
||
|
%functions-mod;
|
||
|
<!ENTITY % tools-mod SYSTEM "../generated/tools.mod">
|
||
|
%tools-mod;
|
||
|
<!ENTITY % variables-mod SYSTEM "../generated/variables.mod">
|
||
|
%variables-mod;
|
||
|
|
||
|
]>
|
||
|
|
||
|
<chapter id="chap-depends"
|
||
|
xmlns="http://www.scons.org/dbxsd/v1.0"
|
||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">
|
||
|
<title>Dependencies</title>
|
||
|
|
||
|
<!--
|
||
|
|
||
|
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.
|
||
|
|
||
|
-->
|
||
|
|
||
|
<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="depends_ex1">
|
||
|
<file name="SConstruct">
|
||
|
Program('hello.c')
|
||
|
</file>
|
||
|
<file name="hello.c">
|
||
|
int main() { printf("Hello, world!\n"); }
|
||
|
</file>
|
||
|
</scons_example>
|
||
|
|
||
|
<scons_output example="depends_ex1" os="posix" suffix="1">
|
||
|
<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="depends_ex1" os="posix" suffix="2">
|
||
|
<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 a
|
||
|
<firstterm>&contentsig;</firstterm>,
|
||
|
or hash, of the contents of each file,
|
||
|
although you can easily configure
|
||
|
&SCons; to use the
|
||
|
modification times (or time stamps) instead.
|
||
|
You can even write your own Python function
|
||
|
for deciding if an input file should trigger a rebuild.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<section>
|
||
|
<title>Using Content Signatures to Decide if a File Has Changed</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
By default, &SCons;
|
||
|
uses a cryptographic hash of the file's contents,
|
||
|
not the file's modification time,
|
||
|
to decide whether a file has changed.
|
||
|
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="depends_ex1" os="posix" suffix="3">
|
||
|
<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="depends_ex1" os="posix" suffix="4">
|
||
|
<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 the default behavior of using
|
||
|
&contentsigs; explicitly,
|
||
|
using the &f-link-Decider; function as follows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
Program('hello.c')
|
||
|
Decider('content')
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
You can also use the string <literal>'MD5'</literal>
|
||
|
as a synonym for <literal>'content'</literal>
|
||
|
when calling the &f-Decider; function - this older
|
||
|
name is deprecated since &SCons; now supports a
|
||
|
choice of hash functions, not just the MD5 function.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<section>
|
||
|
<title>Ramifications of Using Content Signatures</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Using &contentsigs; 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="depends_ex1" os="posix" suffix="5">
|
||
|
<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 &f-link-Decider;
|
||
|
function as follows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_example name="depends_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="depends_newer" os="posix" suffix="1">
|
||
|
<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 &f-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 &f-Decider; function:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_example name="depends_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="depends_match" os="posix" suffix="1">
|
||
|
<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
|
||
|
a file's &contentsig;,
|
||
|
but to read those contents
|
||
|
only when the file's timestamp has changed.
|
||
|
To do this, call the &f-link-Decider;
|
||
|
function with <literal>'content-timestamp'</literal>
|
||
|
argument as follows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_example name="depends_MD5-timestamp">
|
||
|
<file name="SConstruct" printme="1">
|
||
|
Program('hello.c')
|
||
|
Decider('content-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('content')</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="depends_MD5-timestamp" os="posix" suffix="1">
|
||
|
<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
|
||
|
a signature calcuation on its contents.
|
||
|
This can significantly speed up many up-to-date builds.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The only drawback to using
|
||
|
<literal>Decider('content-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('content-timestamp')</literal>
|
||
|
may not be appropriate.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Extending &SCons;: Writing Your Own Custom &Decider; Function</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The different string values that we've passed to
|
||
|
the &f-link-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="depends_function">
|
||
|
<file name="SConstruct" printme="1">
|
||
|
Program('hello.c')
|
||
|
def decide_if_changed(dependency, target, prev_ni, repo_node=None):
|
||
|
if dependency.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
|
||
|
&contentsig; and/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><literal>csig</literal></term>
|
||
|
|
||
|
<listitem>
|
||
|
<para>
|
||
|
The &contentsig;:
|
||
|
a cryptgraphic hash, or checksum, of the file contents
|
||
|
of the <varname>dependency</varname>
|
||
|
file the last time the ⌖ was built.
|
||
|
</para>
|
||
|
</listitem>
|
||
|
|
||
|
</varlistentry>
|
||
|
|
||
|
<varlistentry>
|
||
|
<term><literal>size</literal></term>
|
||
|
|
||
|
<listitem>
|
||
|
<para>
|
||
|
The size in bytes of the <varname>dependency</varname>
|
||
|
file the last time the target was built.
|
||
|
</para>
|
||
|
</listitem>
|
||
|
|
||
|
</varlistentry>
|
||
|
|
||
|
<varlistentry>
|
||
|
<term><literal>timestamp</literal></term>
|
||
|
|
||
|
<listitem>
|
||
|
<para>
|
||
|
The modification time of the <varname>dependency</varname>
|
||
|
file the last time the ⌖ was built.
|
||
|
</para>
|
||
|
</listitem>
|
||
|
|
||
|
</varlistentry>
|
||
|
|
||
|
</variablelist>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
These attributes 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
|
||
|
(use the Python <function>hasattr</function> method or a
|
||
|
<literal>try</literal>-<literal>except</literal> block).
|
||
|
|
||
|
</para>
|
||
|
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The fourth argument <varname>repo_node</varname>
|
||
|
is the &Node; to use if it is not None when comparing &BuildInfo;.
|
||
|
This is typically only set when the target node only exists in a
|
||
|
&Repository;
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<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>
|
||
|
|
||
|
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, repo_node=None):
|
||
|
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 not prev_ni.hasattr("csig"):
|
||
|
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():
|
||
|
with open("test.txt", "a") as f:
|
||
|
f.write("some line\n")
|
||
|
|
||
|
|
||
|
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 &f-link-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 &f-env-Decider;
|
||
|
method to affect only the configuration
|
||
|
decisions for targets built with a
|
||
|
specific &consenv;.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
For example, if we arbitrarily want to build
|
||
|
one program using &contentsigs;
|
||
|
and another using file modification times
|
||
|
from the same source
|
||
|
we might configure it this way:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_example name="depends_mixing">
|
||
|
<file name="SConstruct" printme="1">
|
||
|
env1 = Environment(CPPPATH = ['.'])
|
||
|
env2 = env1.Clone()
|
||
|
env2.Decider('timestamp-match')
|
||
|
env1.Program('prog-content', '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="depends_mixing" os="posix" suffix="1">
|
||
|
<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>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="depends_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="depends_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="depends_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="depends_include" os="posix" suffix="1">
|
||
|
<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;
|
||
|
constructed the <literal>-I.</literal> argument
|
||
|
from the <literal>'.'</literal> in 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="depends_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="depends_ex5" os="posix" suffix="1">
|
||
|
<scons_output_command>scons -Q hello</scons_output_command>
|
||
|
</scons_output>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
And like this on Windows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_output example="depends_ex5" os="win32" suffix="2">
|
||
|
<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="depends_ex1" suffix="6">
|
||
|
<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="depends_ex1" suffix="7">
|
||
|
<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="depends_ex1" suffix="8">
|
||
|
<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 &f-link-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 &f-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="depends_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="depends_macroinc" os="posix" suffix="1">
|
||
|
<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.
|
||
|
Not being 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. &f-link-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 &f-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="depends_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="depends_parsedep" os="posix" suffix="1">
|
||
|
<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="depends_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="depends_parsedeprebuild" os="posix" suffix="1">
|
||
|
<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>
|
||
|
|
||
|
&f-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 &f-ParseDepends; leads to unnecessary recompilations.
|
||
|
Therefore, &f-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 using the
|
||
|
&f-link-Ignore; function as follows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_example name="depends_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="depends_ignore" suffix="1">
|
||
|
<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>
|
||
|
&f-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="depends_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="depends_ignore_explicit" os="posix" suffix="1">
|
||
|
<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="depends_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="depends_no-Requires" suffix="1">
|
||
|
<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 &f-link-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="depends_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; builder 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="depends_Requires" suffix="1">
|
||
|
<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 &f-link-AlwaysBuild; method.
|
||
|
When a file is passed to the &f-AlwaysBuild; method,
|
||
|
like so:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_example name="depends_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="depends_AlwaysBuild" suffix="1">
|
||
|
<scons_output_command>scons -Q</scons_output_command>
|
||
|
<scons_output_command>scons -Q</scons_output_command>
|
||
|
</scons_output>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The &f-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 &f-AlwaysBuild; target,
|
||
|
will still be rebuilt only if it's out-of-date
|
||
|
with respect to its dependencies:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<scons_output example="depends_AlwaysBuild" suffix="2">
|
||
|
<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>
|
||
|
|
||
|
-->
|
||
|
|
||
|
</chapter>
|