From 72c578fd4b0b4a5a43e18594339ac4ff26c376dc Mon Sep 17 00:00:00 2001 From: Luca Falavigna Date: Sat, 2 Jan 2010 20:56:27 +0100 Subject: Imported Upstream version 1.2.0.d20091224 --- doc/user/MANIFEST | 50 + doc/user/README | 21 + doc/user/SCons-win32-install-1.jpg | Bin 0 -> 28857 bytes doc/user/SCons-win32-install-2.jpg | Bin 0 -> 27131 bytes doc/user/SCons-win32-install-3.jpg | Bin 0 -> 25137 bytes doc/user/SCons-win32-install-4.jpg | Bin 0 -> 19549 bytes doc/user/actions.in | 404 +++++++ doc/user/actions.xml | 404 +++++++ doc/user/add-method.in | 127 ++ doc/user/add-method.xml | 135 +++ doc/user/alias.in | 102 ++ doc/user/alias.xml | 112 ++ doc/user/ant.in | 52 + doc/user/ant.xml | 52 + doc/user/build-install.in | 719 +++++++++++ doc/user/build-install.xml | 719 +++++++++++ doc/user/builders-built-in.in | 963 +++++++++++++++ doc/user/builders-built-in.xml | 949 +++++++++++++++ doc/user/builders-commands.in | 156 +++ doc/user/builders-commands.xml | 146 +++ doc/user/builders-writing.in | 1087 +++++++++++++++++ doc/user/builders-writing.xml | 953 +++++++++++++++ doc/user/builders.in | 57 + doc/user/builders.xml | 57 + doc/user/caching.in | 502 ++++++++ doc/user/caching.xml | 506 ++++++++ doc/user/command-line.in | 2327 ++++++++++++++++++++++++++++++++++++ doc/user/command-line.xml | 2243 ++++++++++++++++++++++++++++++++++ doc/user/cons.pl | 720 +++++++++++ doc/user/copyright.in | 32 + doc/user/copyright.xml | 32 + doc/user/depends.in | 1816 ++++++++++++++++++++++++++++ doc/user/depends.xml | 1776 +++++++++++++++++++++++++++ doc/user/environments.in | 1678 ++++++++++++++++++++++++++ doc/user/environments.xml | 1684 ++++++++++++++++++++++++++ doc/user/errors.in | 41 + doc/user/errors.xml | 41 + doc/user/example.in | 41 + doc/user/example.xml | 41 + doc/user/factories.in | 507 ++++++++ doc/user/factories.xml | 466 ++++++++ doc/user/file-removal.in | 223 ++++ doc/user/file-removal.xml | 202 ++++ doc/user/hierarchy.in | 794 ++++++++++++ doc/user/hierarchy.xml | 746 ++++++++++++ doc/user/install.in | 247 ++++ doc/user/install.xml | 237 ++++ doc/user/java.in | 657 ++++++++++ doc/user/java.xml | 433 +++++++ doc/user/less-simple.in | 623 ++++++++++ doc/user/less-simple.xml | 608 ++++++++++ doc/user/libraries.in | 445 +++++++ doc/user/libraries.xml | 451 +++++++ doc/user/main.in | 385 ++++++ doc/user/main.xml | 385 ++++++ doc/user/make.in | 121 ++ doc/user/make.xml | 121 ++ doc/user/mergeflags.in | 137 +++ doc/user/mergeflags.xml | 138 +++ doc/user/misc.in | 606 ++++++++++ doc/user/misc.xml | 565 +++++++++ doc/user/nodes.in | 386 ++++++ doc/user/nodes.xml | 401 +++++++ doc/user/output.in | 681 +++++++++++ doc/user/output.xml | 703 +++++++++++ doc/user/parseconfig.in | 114 ++ doc/user/parseconfig.xml | 132 ++ doc/user/parseflags.in | 184 +++ doc/user/parseflags.xml | 199 +++ doc/user/preface.in | 426 +++++++ doc/user/preface.xml | 426 +++++++ doc/user/python.in | 154 +++ doc/user/python.xml | 154 +++ doc/user/repositories.in | 641 ++++++++++ doc/user/repositories.xml | 595 +++++++++ doc/user/run.in | 375 ++++++ doc/user/run.xml | 375 ++++++ doc/user/scanners.in | 331 +++++ doc/user/scanners.xml | 317 +++++ doc/user/sconf.in | 486 ++++++++ doc/user/sconf.xml | 486 ++++++++ doc/user/separate.in | 540 +++++++++ doc/user/separate.xml | 520 ++++++++ doc/user/sideeffect.in | 216 ++++ doc/user/sideeffect.xml | 211 ++++ doc/user/simple.in | 517 ++++++++ doc/user/simple.xml | 587 +++++++++ doc/user/sourcecode.in | 162 +++ doc/user/sourcecode.xml | 157 +++ doc/user/tasks.in | 108 ++ doc/user/tasks.xml | 108 ++ doc/user/tools.in | 38 + doc/user/tools.xml | 38 + doc/user/troubleshoot.in | 875 ++++++++++++++ doc/user/troubleshoot.xml | 1313 ++++++++++++++++++++ doc/user/variables.in | 56 + doc/user/variables.xml | 56 + doc/user/variants.in | 151 +++ doc/user/variants.xml | 146 +++ 99 files changed, 43207 insertions(+) create mode 100644 doc/user/MANIFEST create mode 100644 doc/user/README create mode 100644 doc/user/SCons-win32-install-1.jpg create mode 100644 doc/user/SCons-win32-install-2.jpg create mode 100644 doc/user/SCons-win32-install-3.jpg create mode 100644 doc/user/SCons-win32-install-4.jpg create mode 100644 doc/user/actions.in create mode 100644 doc/user/actions.xml create mode 100644 doc/user/add-method.in create mode 100644 doc/user/add-method.xml create mode 100644 doc/user/alias.in create mode 100644 doc/user/alias.xml create mode 100644 doc/user/ant.in create mode 100644 doc/user/ant.xml create mode 100644 doc/user/build-install.in create mode 100644 doc/user/build-install.xml create mode 100644 doc/user/builders-built-in.in create mode 100644 doc/user/builders-built-in.xml create mode 100644 doc/user/builders-commands.in create mode 100644 doc/user/builders-commands.xml create mode 100644 doc/user/builders-writing.in create mode 100644 doc/user/builders-writing.xml create mode 100644 doc/user/builders.in create mode 100644 doc/user/builders.xml create mode 100644 doc/user/caching.in create mode 100644 doc/user/caching.xml create mode 100644 doc/user/command-line.in create mode 100644 doc/user/command-line.xml create mode 100644 doc/user/cons.pl create mode 100644 doc/user/copyright.in create mode 100644 doc/user/copyright.xml create mode 100644 doc/user/depends.in create mode 100644 doc/user/depends.xml create mode 100644 doc/user/environments.in create mode 100644 doc/user/environments.xml create mode 100644 doc/user/errors.in create mode 100644 doc/user/errors.xml create mode 100644 doc/user/example.in create mode 100644 doc/user/example.xml create mode 100644 doc/user/factories.in create mode 100644 doc/user/factories.xml create mode 100644 doc/user/file-removal.in create mode 100644 doc/user/file-removal.xml create mode 100644 doc/user/hierarchy.in create mode 100644 doc/user/hierarchy.xml create mode 100644 doc/user/install.in create mode 100644 doc/user/install.xml create mode 100644 doc/user/java.in create mode 100644 doc/user/java.xml create mode 100644 doc/user/less-simple.in create mode 100644 doc/user/less-simple.xml create mode 100644 doc/user/libraries.in create mode 100644 doc/user/libraries.xml create mode 100644 doc/user/main.in create mode 100644 doc/user/main.xml create mode 100644 doc/user/make.in create mode 100644 doc/user/make.xml create mode 100644 doc/user/mergeflags.in create mode 100644 doc/user/mergeflags.xml create mode 100644 doc/user/misc.in create mode 100644 doc/user/misc.xml create mode 100644 doc/user/nodes.in create mode 100644 doc/user/nodes.xml create mode 100644 doc/user/output.in create mode 100644 doc/user/output.xml create mode 100644 doc/user/parseconfig.in create mode 100644 doc/user/parseconfig.xml create mode 100644 doc/user/parseflags.in create mode 100644 doc/user/parseflags.xml create mode 100644 doc/user/preface.in create mode 100644 doc/user/preface.xml create mode 100644 doc/user/python.in create mode 100644 doc/user/python.xml create mode 100644 doc/user/repositories.in create mode 100644 doc/user/repositories.xml create mode 100644 doc/user/run.in create mode 100644 doc/user/run.xml create mode 100644 doc/user/scanners.in create mode 100644 doc/user/scanners.xml create mode 100644 doc/user/sconf.in create mode 100644 doc/user/sconf.xml create mode 100644 doc/user/separate.in create mode 100644 doc/user/separate.xml create mode 100644 doc/user/sideeffect.in create mode 100644 doc/user/sideeffect.xml create mode 100644 doc/user/simple.in create mode 100644 doc/user/simple.xml create mode 100644 doc/user/sourcecode.in create mode 100644 doc/user/sourcecode.xml create mode 100644 doc/user/tasks.in create mode 100644 doc/user/tasks.xml create mode 100644 doc/user/tools.in create mode 100644 doc/user/tools.xml create mode 100644 doc/user/troubleshoot.in create mode 100644 doc/user/troubleshoot.xml create mode 100644 doc/user/variables.in create mode 100644 doc/user/variables.xml create mode 100644 doc/user/variants.in create mode 100644 doc/user/variants.xml (limited to 'doc/user') diff --git a/doc/user/MANIFEST b/doc/user/MANIFEST new file mode 100644 index 0000000..ef273d3 --- /dev/null +++ b/doc/user/MANIFEST @@ -0,0 +1,50 @@ +actions.xml +add-method.xml +alias.xml +ant.xml +builders.xml +builders-built-in.xml +builders-commands.xml +builders-writing.xml +build-install.xml +caching.xml +command-line.xml +cons.pl +copyright.xml +depends.xml +environments.xml +errors.xml +example.xml +factories.xml +file-removal.xml +hierarchy.xml +install.xml +java.xml +libraries.xml +less-simple.xml +main.xml +make.xml +mergeflags.xml +misc.xml +nodes.xml +output.xml +parseconfig.xml +parseflags.xml +preface.xml +python.xml +repositories.xml +run.xml +scanners.xml +sconf.xml +separate.xml +simple.xml +sourcecode.xml +tasks.xml +tools.xml +troubleshoot.xml +variants.xml +variables.xml +SCons-win32-install-1.jpg +SCons-win32-install-2.jpg +SCons-win32-install-3.jpg +SCons-win32-install-4.jpg diff --git a/doc/user/README b/doc/user/README new file mode 100644 index 0000000..aef479a --- /dev/null +++ b/doc/user/README @@ -0,0 +1,21 @@ +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation + +When adding a new file, add it to main.xml and MANIFEST. + +To build the .xml files from the .in files: + scons -D BUILDDOC=1 foo.xml +To build the whole PDF doc from this dir, for testing: + scons -D ../../build/doc/PDF/scons-user.pdf + +Writing examples: here's a simple template. + + + + env = Environment() + print env.Dump("CC") + + + + + scons -Q + diff --git a/doc/user/SCons-win32-install-1.jpg b/doc/user/SCons-win32-install-1.jpg new file mode 100644 index 0000000..ecc439d Binary files /dev/null and b/doc/user/SCons-win32-install-1.jpg differ diff --git a/doc/user/SCons-win32-install-2.jpg b/doc/user/SCons-win32-install-2.jpg new file mode 100644 index 0000000..f468526 Binary files /dev/null and b/doc/user/SCons-win32-install-2.jpg differ diff --git a/doc/user/SCons-win32-install-3.jpg b/doc/user/SCons-win32-install-3.jpg new file mode 100644 index 0000000..90d2ed4 Binary files /dev/null and b/doc/user/SCons-win32-install-3.jpg differ diff --git a/doc/user/SCons-win32-install-4.jpg b/doc/user/SCons-win32-install-4.jpg new file mode 100644 index 0000000..d37973b Binary files /dev/null and b/doc/user/SCons-win32-install-4.jpg differ diff --git a/doc/user/actions.in b/doc/user/actions.in new file mode 100644 index 0000000..1ea5c49 --- /dev/null +++ b/doc/user/actions.in @@ -0,0 +1,404 @@ + + + + + + + &SCons; supports several types of &build_actions; + that can be performed to build one or more target files. + Usually, a &build_action; is a command-line string + that invokes an external command. + A build action can also be an external command + specified as a list of arguments, + or even a Python function. + + + + + + Build action objects are created by the &Action; function. + This function is, in fact, what &SCons; uses + to interpret the &action; + keyword argument when you call the &Builder; function. + So the following line that creates a simple Builder: + + + + + b = Builder(action = 'build < $SOURCE > $TARGET') + + + + + Is equivalent to: + + + + + b = Builder(action = Action('build < $SOURCE > $TARGET')) + + + + + The advantage of using the &Action; function directly + is that it can take a number of additional options + to modify the action's behavior in many useful ways. + + + +
+ Command Strings as Actions + +
+ Suppressing Command-Line Printing + + + + XXX + + + +
+ +
+ Ignoring Exit Status + + + + XXX + + + +
+ +
+ +
+ Argument Lists as Actions + + + + XXX + + + +
+ +
+ Python Functions as Actions + + + + XXX + + + +
+ +
+ Modifying How an Action is Printed + +
+ XXX: the &strfunction; keyword argument + + + + XXX + + + +
+ +
+ XXX: the &cmdstr; keyword argument + + + + XXX + + + +
+ +
+ +
+ Making an Action Depend on Variable Contents: the &varlist; keyword argument + + + + XXX + + + +
+ +
+ chdir=1 + + + + XXX + + + +
+ +
+ Batch Building of Multiple Targets from Separate Sources: the &batch_key; keyword argument + + + + XXX + + + +
+ +
+ Manipulating the Exit Status of an Action: the &exitstatfunc; keyword argument + + + + XXX + + + +
+ + diff --git a/doc/user/actions.xml b/doc/user/actions.xml new file mode 100644 index 0000000..c3b8cf9 --- /dev/null +++ b/doc/user/actions.xml @@ -0,0 +1,404 @@ + + + + + + + &SCons; supports several types of &build_actions; + that can be performed to build one or more target files. + Usually, a &build_action; is a command-line string + that invokes an external command. + A build action can also be an external command + specified as a list of arguments, + or even a Python function. + + + + + + Build action objects are created by the &Action; function. + This function is, in fact, what &SCons; uses + to interpret the &action; + keyword argument when you call the &Builder; function. + So the following line that creates a simple Builder: + + + + + b = Builder(action = 'build < $SOURCE > $TARGET') + + + + + Is equivalent to: + + + + + b = Builder(action = Action('build < $SOURCE > $TARGET')) + + + + + The advantage of using the &Action; function directly + is that it can take a number of additional options + to modify the action's behavior in many useful ways. + + + +
+ Command Strings as Actions + +
+ Suppressing Command-Line Printing + + + + XXX + + + +
+ +
+ Ignoring Exit Status + + + + XXX + + + +
+ +
+ +
+ Argument Lists as Actions + + + + XXX + + + +
+ +
+ Python Functions as Actions + + + + XXX + + + +
+ +
+ Modifying How an Action is Printed + +
+ XXX: the &strfunction; keyword argument + + + + XXX + + + +
+ +
+ XXX: the &cmdstr; keyword argument + + + + XXX + + + +
+ +
+ +
+ Making an Action Depend on Variable Contents: the &varlist; keyword argument + + + + XXX + + + +
+ +
+ chdir=1 + + + + XXX + + + +
+ +
+ Batch Building of Multiple Targets from Separate Sources: the &batch_key; keyword argument + + + + XXX + + + +
+ +
+ Manipulating the Exit Status of an Action: the &exitstatfunc; keyword argument + + + + XXX + + + +
+ + diff --git a/doc/user/add-method.in b/doc/user/add-method.in new file mode 100644 index 0000000..61f13cc --- /dev/null +++ b/doc/user/add-method.in @@ -0,0 +1,127 @@ + + + + + The &AddMethod; function is used to add a method + to an environment. It's typically used to add a "pseudo-builder," + a function that looks like a &Builder; but + wraps up calls to multiple other &Builder;s + or otherwise processes its arguments + before calling one or more &Builder;s. + In the following example, + we want to install the program into the standard + /usr/bin directory hierarchy, + but also copy it into a local install/bin + directory from which a package might be built: + + + + + + def install_in_bin_dirs(env, source): + """Install source in both bin dirs""" + i1 = env.Install("$BIN", source) + i2 = env.Install("$LOCALBIN", source) + return [i1[0], i2[0]] # Return a list, like a normal builder + env = Environment(BIN='__ROOT__/usr/bin', LOCALBIN='#install/bin') + env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") + env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs + + + int main() { printf("Hello, world!\n"); } + + + + + This produces the following: + + + + scons -Q / + + + + + As mentioned, a psuedo-builder also provides more flexibility + in parsing arguments than you can get with a &Builder;. + The next example shows a pseudo-builder with a + named argument that modifies the filename, and a separate argument + for the resource file (rather than having the builder figure it out + by file extension). This example also demonstrates using the global + &AddMethod; function to add a method to the global Environment class, + so it will be used in all subsequently created environments. + + + + + + def BuildTestProg(env, testfile, resourcefile, testdir="tests"): + """Build the test program; + prepends "test_" to src and target, + and puts target into testdir.""" + srcfile = "test_%s.c" % testfile + target = "%s/test_%s" % (testdir, testfile) + if env['PLATFORM'] == 'win32': + resfile = env.RES(resourcefile) + p = env.Program(target, [srcfile, resfile]) + else: + p = env.Program(target, srcfile) + return p + AddMethod(Environment, BuildTestProg) + + env = Environment() + env.BuildTestProg('stuff', resourcefile='res.rc') + + + int main() { printf("Hello, world!\n"); } + + + res.rc + + + + + This produces the following on Linux: + + + + scons -Q + + + + And the following on Windows: + + + + scons -Q + + + + Using &AddMethod; is better than just adding an instance method + to a &consenv; because it gets called as a proper method, + and because &AddMethod; provides for copying the method + to any clones of the &consenv; instance. + diff --git a/doc/user/add-method.xml b/doc/user/add-method.xml new file mode 100644 index 0000000..761765f --- /dev/null +++ b/doc/user/add-method.xml @@ -0,0 +1,135 @@ + + + + + The &AddMethod; function is used to add a method + to an environment. It's typically used to add a "pseudo-builder," + a function that looks like a &Builder; but + wraps up calls to multiple other &Builder;s + or otherwise processes its arguments + before calling one or more &Builder;s. + In the following example, + we want to install the program into the standard + /usr/bin directory hierarchy, + but also copy it into a local install/bin + directory from which a package might be built: + + + + + def install_in_bin_dirs(env, source): + """Install source in both bin dirs""" + i1 = env.Install("$BIN", source) + i2 = env.Install("$LOCALBIN", source) + return [i1[0], i2[0]] # Return a list, like a normal builder + env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin') + env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") + env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs + + + + This produces the following: + + + + % scons -Q / + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello" + Install file: "hello" as "install/bin/hello" + + + + + As mentioned, a psuedo-builder also provides more flexibility + in parsing arguments than you can get with a &Builder;. + The next example shows a pseudo-builder with a + named argument that modifies the filename, and a separate argument + for the resource file (rather than having the builder figure it out + by file extension). This example also demonstrates using the global + &AddMethod; function to add a method to the global Environment class, + so it will be used in all subsequently created environments. + + + + + def BuildTestProg(env, testfile, resourcefile, testdir="tests"): + """Build the test program; + prepends "test_" to src and target, + and puts target into testdir.""" + srcfile = "test_%s.c" % testfile + target = "%s/test_%s" % (testdir, testfile) + if env['PLATFORM'] == 'win32': + resfile = env.RES(resourcefile) + p = env.Program(target, [srcfile, resfile]) + else: + p = env.Program(target, srcfile) + return p + AddMethod(Environment, BuildTestProg) + + env = Environment() + env.BuildTestProg('stuff', resourcefile='res.rc') + + + + This produces the following on Linux: + + + + % scons -Q + cc -o test_stuff.o -c test_stuff.c + cc -o tests/test_stuff test_stuff.o + + + + And the following on Windows: + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + rc /fores.res res.rc + cl /Fotest_stuff.obj /c test_stuff.c /nologo + link /nologo /OUT:tests\test_stuff.exe test_stuff.obj res.res + + + + Using &AddMethod; is better than just adding an instance method + to a &consenv; because it gets called as a proper method, + and because &AddMethod; provides for copying the method + to any clones of the &consenv; instance. + diff --git a/doc/user/alias.in b/doc/user/alias.in new file mode 100644 index 0000000..45a1534 --- /dev/null +++ b/doc/user/alias.in @@ -0,0 +1,102 @@ + + + + + We've already seen how you can use the &Alias; + function to create a target named install: + + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('__ROOT__/usr/bin', hello) + env.Alias('install', '__ROOT__/usr/bin') + + + int main() { printf("Hello, world!\n"); } + + + + + + You can then use this alias on the command line + to tell &SCons; more naturally that you want to install files: + + + + + scons -Q install + + + + + Like other &Builder; methods, though, + the &Alias; method returns an object + representing the alias being built. + You can then use this object as input to anothother &Builder;. + This is especially useful if you use such an object + as input to another call to the &Alias; &Builder;, + allowing you to create a hierarchy + of nested aliases: + + + + + + env = Environment() + p = env.Program('foo.c') + l = env.Library('bar.c') + env.Install('__ROOT__/usr/bin', p) + env.Install('__ROOT__/usr/lib', l) + ib = env.Alias('install-bin', '__ROOT__/usr/bin') + il = env.Alias('install-lib', '__ROOT__/usr/lib') + env.Alias('install', [ib, il]) + + + int main() { printf("foo.c\n"); } + + + void bar() { printf("bar.c\n"); } + + + + + + This example defines separate install, + install-bin, + and install-lib aliases, + allowing you finer control over what gets installed: + + + + + scons -Q install-bin + scons -Q install-lib + scons -Q -c __ROOT__/ + scons -Q install + diff --git a/doc/user/alias.xml b/doc/user/alias.xml new file mode 100644 index 0000000..04ebd5f --- /dev/null +++ b/doc/user/alias.xml @@ -0,0 +1,112 @@ + + + + + We've already seen how you can use the &Alias; + function to create a target named install: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('/usr/bin', hello) + env.Alias('install', '/usr/bin') + + + + + You can then use this alias on the command line + to tell &SCons; more naturally that you want to install files: + + + + + % scons -Q install + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello" + + + + + Like other &Builder; methods, though, + the &Alias; method returns an object + representing the alias being built. + You can then use this object as input to anothother &Builder;. + This is especially useful if you use such an object + as input to another call to the &Alias; &Builder;, + allowing you to create a hierarchy + of nested aliases: + + + + + env = Environment() + p = env.Program('foo.c') + l = env.Library('bar.c') + env.Install('/usr/bin', p) + env.Install('/usr/lib', l) + ib = env.Alias('install-bin', '/usr/bin') + il = env.Alias('install-lib', '/usr/lib') + env.Alias('install', [ib, il]) + + + + + This example defines separate install, + install-bin, + and install-lib aliases, + allowing you finer control over what gets installed: + + + + + % scons -Q install-bin + cc -o foo.o -c foo.c + cc -o foo foo.o + Install file: "foo" as "/usr/bin/foo" + % scons -Q install-lib + cc -o bar.o -c bar.c + ar rc libbar.a bar.o + ranlib libbar.a + Install file: "libbar.a" as "/usr/lib/libbar.a" + % scons -Q -c / + Removed foo.o + Removed foo + Removed /usr/bin/foo + Removed bar.o + Removed libbar.a + Removed /usr/lib/libbar.a + % scons -Q install + cc -o foo.o -c foo.c + cc -o foo foo.o + Install file: "foo" as "/usr/bin/foo" + cc -o bar.o -c bar.c + ar rc libbar.a bar.o + ranlib libbar.a + Install file: "libbar.a" as "/usr/lib/libbar.a" + diff --git a/doc/user/ant.in b/doc/user/ant.in new file mode 100644 index 0000000..41b441d --- /dev/null +++ b/doc/user/ant.in @@ -0,0 +1,52 @@ + + + + + XXX + + + +
+ Differences Between &Ant; and &SCons; + + + + XXX + + + +
+ +
+ Advantages of &SCons; Over &Ant; + + + + XXX + + + +
diff --git a/doc/user/ant.xml b/doc/user/ant.xml new file mode 100644 index 0000000..41b441d --- /dev/null +++ b/doc/user/ant.xml @@ -0,0 +1,52 @@ + + + + + XXX + + + +
+ Differences Between &Ant; and &SCons; + + + + XXX + + + +
+ +
+ Advantages of &SCons; Over &Ant; + + + + XXX + + + +
diff --git a/doc/user/build-install.in b/doc/user/build-install.in new file mode 100644 index 0000000..ab289b1 --- /dev/null +++ b/doc/user/build-install.in @@ -0,0 +1,719 @@ + + + + + This chapter will take you through the basic steps + of installing &SCons; on your system, + and building &SCons; if you don't have a + pre-built package available + (or simply prefer the flexibility of building it yourself). + Before that, however, this chapter will also describe the basic steps + involved in installing Python on your system, + in case that is necessary. + Fortunately, both &SCons; and Python + are very easy to install on almost any system, + and Python already comes installed on many systems. + + + + + +
+ Installing Python + + + + Because &SCons; is written in Python, + you must obviously have Python installed on your system + to use &SCons;. + Before you try to install Python, + you should check to see if Python is already + available on your system by typing + python -V + (capital 'V') + or + python --version + at your system's command-line prompt. + + + + + $ python -V + Python 2.5.1 + + + + + And on a Windows system with Python installed: + + + + + C:\>python -V + Python 2.5.1 + + + + + If Python is not installed on your system, + you will see an error message + stating something like "command not found" + (on UNIX or Linux) + or "'python' is not recognized + as an internal or external command, operable progam or batch file" + (on Windows). + In that case, you need to install Python + before you can install &SCons;. + + + + + + (Note that the option + was added to Python version 2.0, + so if your system only has an earlier version available + you may see an + "Unknown option: -V" + error message.) + + + + + + The standard location for information + about downloading and installing Python is + http://www.python.org/download/. + See that page for information about + how to download and install Python on your system. + + + + + + &SCons; will work with any version of Python from 1.5.2 or later. + If you need to install Python and have a choice, + we recommend using the most recent Python 2.5 version available. + Python 2.5 has significant improvements + that help speed up the performance of &SCons;. + + + +
+ +
+ Installing &SCons; From Pre-Built Packages + + + + &SCons; comes pre-packaged for installation on a number of systems, + including Linux and Windows systems. + You do not need to read this entire section, + you should need to read only the section + appropriate to the type of system you're running on. + + + +
+ Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems + + + + &SCons; comes in RPM (Red Hat Package Manager) format, + pre-built and ready to install on Red Hat Linux, + Fedora, + or any other Linux distribution that uses RPM. + Your distribution may + already have an &SCons; RPM built specifically for it; + many do, including SUSE, Mandrake and Fedora. + You can check for the availability of an &SCons; RPM + on your distribution's download servers, + or by consulting an RPM search site like + http://www.rpmfind.net/ or + http://rpm.pbone.net/. + + + + + + If your distribution supports installation via + yum, + you should be able to install &SCons; by running: + + + + + # yum install scons + + + + + If your Linux distribution does not already have + a specific &SCons; RPM file, + you can download and install from the + generic RPM provided by the &SCons; project. + This will install the + SCons script(s) in /usr/bin, + and the SCons library modules in + /usr/lib/scons. + + + + + + To install from the command line, simply download the + appropriate .rpm file, + and then run: + + + + + # rpm -Uvh scons-1.2.0.d20091224-1.noarch.rpm + + + + + Or, you can use a graphical RPM package manager. + See your package manager application's documention + for specific instructions about + how to use it to install a downloaded RPM. + + + +
+ +
+ Installing &SCons; on Debian Linux Systems + + + + Debian Linux systems use a different package management + format that also makes it very easy to install &SCons;. + + + + + + If your system is connected to the Internet, + you can install the latest official Debian package + by running: + + + + + # apt-get install scons + + + + +
+ +
+ Installing &SCons; on Windows Systems + + + + &SCons; provides a Windows installer + that makes installation extremely easy. + Download the scons-1.2.0.d20091224.win32.exe + file from the &SCons; download page at + http://www.scons.org/download.php. + Then all you need to do is execute the file + (usually by clicking on its icon in Windows Explorer). + These will take you through a small + sequence of windows that will install + &SCons; on your system. + + + + + + + + + +
+ +
+ +
+ Building and Installing &SCons; on Any System + + + + If a pre-built &SCons; package is not available for your system, + then you can still easily build and install &SCons; using the native + Python distutils package. + + + + + + The first step is to download either the + scons-1.2.0.d20091224.tar.gz + or scons-1.2.0.d20091224.zip, + which are available from the SCons download page at + http://www.scons.org/download.html. + + + + + + Unpack the archive you downloaded, + using a utility like tar + on Linux or UNIX, + or WinZip on Windows. + This will create a directory called + scons-1.2.0.d20091224, + usually in your local directory. + Then change your working directory to that directory + and install &SCons; by executing the following commands: + + + + + # cd scons-1.2.0.d20091224 + # python setup.py install + + + + + This will build &SCons;, + install the &scons; script + in the default system scripts directory + (/usr/local/bin or + C:\Python25\Scripts), + and will install the &SCons; build engine + in an appropriate stand-alone library directory + (/usr/local/lib/scons or + C:\Python25\scons). + Because these are system directories, + you may need root (on Linux or UNIX) or Administrator (on Windows) + privileges to install &SCons; like this. + + + + + +
+ Building and Installing Multiple Versions of &SCons; Side-by-Side + + + + The &SCons; setup.py script + has some extensions that support + easy installation of multiple versions of &SCons; + in side-by-side locations. + This makes it easier to download and + experiment with different versions of &SCons; + before moving your official build process to a new version, + for example. + + + + + + To install &SCons; in a version-specific location, + add the option + when you call setup.py: + + + + + # python setup.py install --version-lib + + + + + This will install the &SCons; build engine + in the + /usr/lib/scons-1.2.0.d20091224 + or + C:\Python25\scons-1.2.0.d20091224 + directory, for example. + + + + + + If you use the option + the first time you install &SCons;, + you do not need to specify it each time you install + a new version. + The &SCons; setup.py script + will detect the version-specific directory name(s) + and assume you want to install all versions + in version-specific directories. + You can override that assumption in the future + by explicitly specifying the option. + + + +
+ +
+ Installing &SCons; in Other Locations + + + + You can install &SCons; in locations other than + the default by specifying the option: + + + + + # python setup.py install --prefix=/opt/scons + + + + + This would + install the scons script in + /opt/scons/bin + and the build engine in + /opt/scons/lib/scons, + + + + + + Note that you can specify both the + and the options + at the same type, + in which case setup.py + will install the build engine + in a version-specific directory + relative to the specified prefix. + Adding to the + above example would install the build engine in + /opt/scons/lib/scons-1.2.0.d20091224. + + + +
+ +
+ Building and Installing &SCons; Without Administrative Privileges + + + + If you don't have the right privileges to install &SCons; + in a system location, + simply use the --prefix= option + to install it in a location of your choosing. + For example, + to install &SCons; in appropriate locations + relative to the user's $HOME directory, + the &scons; script in + $HOME/bin + and the build engine in + $HOME/lib/scons, + simply type: + + + + + $ python setup.py install --prefix=$HOME + + + + + You may, of course, specify any other location you prefer, + and may use the option + if you would like to install version-specific directories + relative to the specified prefix. + + + + + + This can also be used to experiment with a newer + version of &SCons; than the one installed + in your system locations. + Of course, the location in which you install the + newer version of the &scons; script + ($HOME/bin in the above example) + must be configured in your &PATH; variable + before the directory containing + the system-installed version + of the &scons; script. + + + +
+ +
+ + diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml new file mode 100644 index 0000000..ab289b1 --- /dev/null +++ b/doc/user/build-install.xml @@ -0,0 +1,719 @@ + + + + + This chapter will take you through the basic steps + of installing &SCons; on your system, + and building &SCons; if you don't have a + pre-built package available + (or simply prefer the flexibility of building it yourself). + Before that, however, this chapter will also describe the basic steps + involved in installing Python on your system, + in case that is necessary. + Fortunately, both &SCons; and Python + are very easy to install on almost any system, + and Python already comes installed on many systems. + + + + + +
+ Installing Python + + + + Because &SCons; is written in Python, + you must obviously have Python installed on your system + to use &SCons;. + Before you try to install Python, + you should check to see if Python is already + available on your system by typing + python -V + (capital 'V') + or + python --version + at your system's command-line prompt. + + + + + $ python -V + Python 2.5.1 + + + + + And on a Windows system with Python installed: + + + + + C:\>python -V + Python 2.5.1 + + + + + If Python is not installed on your system, + you will see an error message + stating something like "command not found" + (on UNIX or Linux) + or "'python' is not recognized + as an internal or external command, operable progam or batch file" + (on Windows). + In that case, you need to install Python + before you can install &SCons;. + + + + + + (Note that the option + was added to Python version 2.0, + so if your system only has an earlier version available + you may see an + "Unknown option: -V" + error message.) + + + + + + The standard location for information + about downloading and installing Python is + http://www.python.org/download/. + See that page for information about + how to download and install Python on your system. + + + + + + &SCons; will work with any version of Python from 1.5.2 or later. + If you need to install Python and have a choice, + we recommend using the most recent Python 2.5 version available. + Python 2.5 has significant improvements + that help speed up the performance of &SCons;. + + + +
+ +
+ Installing &SCons; From Pre-Built Packages + + + + &SCons; comes pre-packaged for installation on a number of systems, + including Linux and Windows systems. + You do not need to read this entire section, + you should need to read only the section + appropriate to the type of system you're running on. + + + +
+ Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems + + + + &SCons; comes in RPM (Red Hat Package Manager) format, + pre-built and ready to install on Red Hat Linux, + Fedora, + or any other Linux distribution that uses RPM. + Your distribution may + already have an &SCons; RPM built specifically for it; + many do, including SUSE, Mandrake and Fedora. + You can check for the availability of an &SCons; RPM + on your distribution's download servers, + or by consulting an RPM search site like + http://www.rpmfind.net/ or + http://rpm.pbone.net/. + + + + + + If your distribution supports installation via + yum, + you should be able to install &SCons; by running: + + + + + # yum install scons + + + + + If your Linux distribution does not already have + a specific &SCons; RPM file, + you can download and install from the + generic RPM provided by the &SCons; project. + This will install the + SCons script(s) in /usr/bin, + and the SCons library modules in + /usr/lib/scons. + + + + + + To install from the command line, simply download the + appropriate .rpm file, + and then run: + + + + + # rpm -Uvh scons-1.2.0.d20091224-1.noarch.rpm + + + + + Or, you can use a graphical RPM package manager. + See your package manager application's documention + for specific instructions about + how to use it to install a downloaded RPM. + + + +
+ +
+ Installing &SCons; on Debian Linux Systems + + + + Debian Linux systems use a different package management + format that also makes it very easy to install &SCons;. + + + + + + If your system is connected to the Internet, + you can install the latest official Debian package + by running: + + + + + # apt-get install scons + + + + +
+ +
+ Installing &SCons; on Windows Systems + + + + &SCons; provides a Windows installer + that makes installation extremely easy. + Download the scons-1.2.0.d20091224.win32.exe + file from the &SCons; download page at + http://www.scons.org/download.php. + Then all you need to do is execute the file + (usually by clicking on its icon in Windows Explorer). + These will take you through a small + sequence of windows that will install + &SCons; on your system. + + + + + + + + + +
+ +
+ +
+ Building and Installing &SCons; on Any System + + + + If a pre-built &SCons; package is not available for your system, + then you can still easily build and install &SCons; using the native + Python distutils package. + + + + + + The first step is to download either the + scons-1.2.0.d20091224.tar.gz + or scons-1.2.0.d20091224.zip, + which are available from the SCons download page at + http://www.scons.org/download.html. + + + + + + Unpack the archive you downloaded, + using a utility like tar + on Linux or UNIX, + or WinZip on Windows. + This will create a directory called + scons-1.2.0.d20091224, + usually in your local directory. + Then change your working directory to that directory + and install &SCons; by executing the following commands: + + + + + # cd scons-1.2.0.d20091224 + # python setup.py install + + + + + This will build &SCons;, + install the &scons; script + in the default system scripts directory + (/usr/local/bin or + C:\Python25\Scripts), + and will install the &SCons; build engine + in an appropriate stand-alone library directory + (/usr/local/lib/scons or + C:\Python25\scons). + Because these are system directories, + you may need root (on Linux or UNIX) or Administrator (on Windows) + privileges to install &SCons; like this. + + + + + +
+ Building and Installing Multiple Versions of &SCons; Side-by-Side + + + + The &SCons; setup.py script + has some extensions that support + easy installation of multiple versions of &SCons; + in side-by-side locations. + This makes it easier to download and + experiment with different versions of &SCons; + before moving your official build process to a new version, + for example. + + + + + + To install &SCons; in a version-specific location, + add the option + when you call setup.py: + + + + + # python setup.py install --version-lib + + + + + This will install the &SCons; build engine + in the + /usr/lib/scons-1.2.0.d20091224 + or + C:\Python25\scons-1.2.0.d20091224 + directory, for example. + + + + + + If you use the option + the first time you install &SCons;, + you do not need to specify it each time you install + a new version. + The &SCons; setup.py script + will detect the version-specific directory name(s) + and assume you want to install all versions + in version-specific directories. + You can override that assumption in the future + by explicitly specifying the option. + + + +
+ +
+ Installing &SCons; in Other Locations + + + + You can install &SCons; in locations other than + the default by specifying the option: + + + + + # python setup.py install --prefix=/opt/scons + + + + + This would + install the scons script in + /opt/scons/bin + and the build engine in + /opt/scons/lib/scons, + + + + + + Note that you can specify both the + and the options + at the same type, + in which case setup.py + will install the build engine + in a version-specific directory + relative to the specified prefix. + Adding to the + above example would install the build engine in + /opt/scons/lib/scons-1.2.0.d20091224. + + + +
+ +
+ Building and Installing &SCons; Without Administrative Privileges + + + + If you don't have the right privileges to install &SCons; + in a system location, + simply use the --prefix= option + to install it in a location of your choosing. + For example, + to install &SCons; in appropriate locations + relative to the user's $HOME directory, + the &scons; script in + $HOME/bin + and the build engine in + $HOME/lib/scons, + simply type: + + + + + $ python setup.py install --prefix=$HOME + + + + + You may, of course, specify any other location you prefer, + and may use the option + if you would like to install version-specific directories + relative to the specified prefix. + + + + + + This can also be used to experiment with a newer + version of &SCons; than the one installed + in your system locations. + Of course, the location in which you install the + newer version of the &scons; script + ($HOME/bin in the above example) + must be configured in your &PATH; variable + before the directory containing + the system-installed version + of the &scons; script. + + + +
+ +
+ + diff --git a/doc/user/builders-built-in.in b/doc/user/builders-built-in.in new file mode 100644 index 0000000..245cb8c --- /dev/null +++ b/doc/user/builders-built-in.in @@ -0,0 +1,963 @@ + + + + + &SCons; provides the ability to build a lot of different + types of files right "out of the box." + So far, we've been using &SCons;' ability to build + programs, objects and libraries to + illustrate much of the underlying functionality of &SCons; + This section will describe all of the different + types of files that you can build with &SCons;, + and the built-in &Builder; objects used to build them. + By default, all of the &Builder; objects in this section + can be built either with or without an explicit + construction environment. + + + +
+ Programs: the &Program; Builder + + + + As we've seen, the &b-link-Program; Builder + is used to build an executable program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the executable program name to be created. + For example: + + + + + Program('prog', 'file1.o') + + + + + Will create the &prog; + executable on a POSIX system, + the &prog_exe; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-PROGPREFIX; + and + &cv-link-PROGSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') + env.Program('prog', ['file1.o', 'file2.o']) + + + + + Will create a program named + myprog.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the target + program created. + For example: + + + + + Program(['hello.c', 'goodbye.c']) + + + + + Will create the &hello; + executable on a POSIX system, + the &hello_exe; executable on a Windows system. + + + + + + Two construction variables control what libraries + will be linked with the resulting program. + The &cv-link-LIBS; variable is a list of the names of + libraries that will be linked into any programs, + and the &cv-link-LIBPATH; variables is a list of + directories that will be searched for + the specified libraries. + &SCons; will construct the right command-line + options for the running system. + For example: + + + + + + env = Environment(LIBS = ['foo1', 'foo2'], + LIBPATH = ['/usr/dir1', 'dir2']) + env.Program(['hello.c', 'goodbye.c']) + + + int hello() { printf("Hello, world!\n"); } + + + int goodbye() { printf("Goodbye, world!\n"); } + + + + + + Will execute as follows on a POSIX system: + + + + + scons -Q + + + + + And execute as follows on a Windows system: + + + + + scons -Q + + + + + The &cv-LIBS; construction variable + is turned into command line options + by appending the &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; + construction variables to the beginning and end, + respectively, of each specified library. + + + + + + The &cv-LIBPATH; construction variable + is turned into command line options + by appending the &cv-link-LIBDIRPREFIX; and &cv-link-LIBDIRSUFFIX; + construction variables to the beginning and end, + respectively, of each specified library. + + + + + + Other relevant construction variables + include those used by the &b-link-Object; + builders to affect how the + source files specified as input to the &t-Program; + builders are turned into object files; + see the next section. + + + + + + The command line used to control how a program is linked + is specified by the &cv-link-LINKCOM; construction variable. + By default, it uses the + &cv-link-LINK; construction variable + and the &cv-link-LINKFLAGS; construction variable. + + + +
+ +
+ Object-File Builders + + + + &SCons; provides separate Builder objects + to create static and shared object files. + The distinction becomes especially important when + archiving object files into different types of libraries. + + + +
+ The &StaticObject; Builder + + + + The &b-link-StaticObject; Builder + is used to build an object file + suitable for static linking into a program, + or for inclusion in a static library. + The &source; argument is a single source-code file, + and the ⌖ argument is the + name of the static object file to be created. + For example: + + + + + StaticObject('file', 'file.c') + + + + + Will create the &file_o; + object file on a POSIX system, + the &file_obj; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-OBJPREFIX; + and + &cv-link-OBJSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx') + env.StaticObject('file', 'file.c') + + + + + Will create an object file named + myfile.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + beomces the base of the name + of the static object file to be created. + For example: + + + + + StaticObject('file.c') + + + + + Will create the &file_o; + executable on a POSIX system, + the &file_obj; executable on a Windows system. + + + +
+ +
+ The &SharedObject; Builder + + + + The &b-link-SharedObject; Builder + is used to build an object file + suitable for shared linking into a program, + or for inclusion in a shared library. + The &source; argument is a single source-code file, + and the ⌖ argument is the + name of the shared object file to be created. + For example: + + + + + SharedObject('file', 'file.c') + + + + + Will create the &file_o; + object file on a POSIX system, + the &file_obj; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-SHOBJPREFIX; + and + &cv-link-SHOBJSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx') + env.SharedObject('file', 'file.c') + + + + + Will create an object file named + myfile.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name + of the shared object file to be created. + For example: + + + + + SharedObject('file.c') + + + + + Will create the &file_o; + executable on a POSIX system, + the &file_obj; executable on a Windows system. + + + +
+ +
+ The &Object; Builder + + + + The &b-link-Object; Builder is a synonym for &b-link-StaticObject; + and is completely equivalent. + + + +
+ +
+ +
+ Library Builders + + + + &SCons; provides separate Builder objects + to create static and shared libraries. + + + +
+ The &StaticLibrary; Builder + + + + The &b-link-StaticLibrary; Builder + is used to create a library + suitable for static linking into a program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the static library to be created. + For example: + + + + + StaticLibrary('foo', ['file1.c', 'file2.c']) + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-LIBPREFIX; + and + &cv-link-LIBSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx') + env.StaticLibrary('lib', ['file1.o', 'file2.o']) + + + + + Will create an object file named + mylib.xxx + regardless of the system on which it is run. + + + + + StaticLibrary('foo', ['file1.c', 'file2.c']) + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name of the static object file to be created. + For example: + + + + + StaticLibrary(['file.c', 'another.c']) + + + + + Will create the &libfile_a; + library on a POSIX system, + the &file_lib; library on a Windows system. + + + +
+ +
+ The &SharedLibrary; Builder + + + + The &b-link-SharedLibrary; Builder + is used to create a shared library + suitable for linking with a program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the shared library to be created. + For example: + + + + + SharedLibrary('foo', ['file1.c', 'file2.c']) + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-SHLIBPREFIX; + and + &cv-link-SHLIBSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx') + env.SharedLibrary('shared', ['file1.o', 'file2.o']) + + + + + Will create an object file named + myshared.xxx + regardless of the system on which it is run. + + + + + SharedLibrary('foo', ['file1.c', 'file2.c']) + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name of the shared library to be created. + For example: + + + + + SharedLibrary(['file.c', 'another.c']) + + + + + Will create the &libfile_so; + library on a POSIX system, + the &file_dll; library on a Windows system. + + + +
+ +
+ The &Library; Builder + + + + The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary; + and is completely equivalent. + + + +
+ +
+ +
+ Pre-Compiled Headers: the &PCH; Builder + + + + XXX PCH() + + + +
+ +
+ Microsoft Visual C++ Resource Files: the &RES; Builder + + + + XXX RES() + + + +
+ +
+ Source Files + + + + By default + &SCons; supports two Builder objects + that know how to build source files + from other input files. + These are typically invoked "internally" + to turn files that need preprocessing into other source files. + + + +
+ The &CFile; Builder + + + + XXX CFile() + + + + + XXX CFile() programlisting + + + + XXX CFile() screen + + +
+ +
+ The &CXXFile; Builder + + + + XXX CXXFILE() + + + + + XXX CXXFILE() programlisting + + + + XXX CXXFILE() screen + + +
+ +
+ +
+ Documents + + + + &SCons; provides a number of Builder objects + for creating different types of documents. + + + +
+ The &DVI; Builder + + + + XXX DVI() para + + + + + XXX DVI() programlisting + + + + XXX DVI() screen + + +
+ +
+ The &PDF; Builder + + + + XXX PDF() para + + + +
+ +
+ The &PostScript; Builder + + + + XXX PostScript() para + + + + + XXX PostScript() programlisting + + + + XXX PostScript() screen + + +
+ +
+ +
+ Archives + + + + &SCons; provides Builder objects + for creating two different types of archive files. + + + +
+ The &Tar; Builder + + + + The &b-link-Tar; Builder object uses the &tar; + utility to create archives of files + and/or directory trees: + + + + + + env = Environment() + env.Tar('out1.tar', ['file1', 'file2']) + env.Tar('out2', 'directory') + + + file1 + + + file2 + + + directory/file3 + + + + + scons -Q . + + + + + One common requirement when creating a &tar; archive + is to create a compressed archive using the + option. + This is easily handled by specifying + the value of the &cv-link-TARFLAGS; variable + when you create the construction environment. + Note, however, that the used to + to instruct &tar; to create the archive + is part of the default value of &cv-TARFLAGS;, + so you need to set it both options: + + + + + + env = Environment(TARFLAGS = '-c -z') + env.Tar('out.tar.gz', 'directory') + + + directory/file + + + + + scons -Q . + + + + + you may also wish to set the value of the + &cv-link-TARSUFFIX; construction variable + to your desired suffix for compress &tar; archives, + so that &SCons; can append it to the target file name + without your having to specify it explicitly: + + + + + + env = Environment(TARFLAGS = '-c -z', + TARSUFFIX = '.tgz') + env.Tar('out', 'directory') + + + directory/file + + + + + scons -Q . + + +
+ +
+ The &Zip; Builder + + + + The &b-link-Zip; Builder object creates archives of files + and/or directory trees in the ZIP file format. + Python versions 1.6 or later + contain an internal &zipfile; module + that &SCons; will use. + In this case, given the following + &SConstruct; file: + + + + + + env = Environment() + env.Zip('out', ['file1', 'file2']) + + + file1 + + + file2 + + + + + + Your output will reflect the fact + that an internal Python function + is being used to create the output ZIP archive: + + + + + scons -Q . + + + + + If you're using Python version 1.5.2 to run &SCons;, + then &SCons; will try to use an external + &zip; program as follows: + + + + + % scons -Q . + zip /home/my/project/zip.out file1 file2 + + +
+ +
+ +
+ Java + + + + &SCons; provides Builder objects + for creating various types of Java output files. + + + +
+ Building Class Files: the &Java; Builder + + + + The &b-link-Java; builder takes one or more input + .java files + and turns them into one or more + .class files + Unlike most builders, however, + the &Java; builder takes + target and source directories, + not files, as input. + + + + + env = Environment() + env.Java(target = 'classes', source = 'src') + + + + + The &Java; builder will then + search the specified source directory + tree for all .java files, + and pass any out-of-date + + + + + XXX Java() screen + + +
+ +
+ The &Jar; Builder + + + + XXX The &Jar; builder object + + + + + env = Environment() + env.Java(target = 'classes', source = 'src') + env.Jar(target = '', source = 'classes') + + + + XXX Jar() screen + + +
+ +
+ Building C header and stub files: the &JavaH; Builder + + + + XXX JavaH() para + + + + + XXX JavaH() programlisting + + + + XXX JavaH() screen + + +
+ +
+ Building RMI stub and skeleton class files: the &RMIC; Builder + + + + XXX RMIC() para + + + + + XXX RMIC() programlisting + + + + XXX RMIC() screen + + +
+ +
diff --git a/doc/user/builders-built-in.xml b/doc/user/builders-built-in.xml new file mode 100644 index 0000000..bee5b38 --- /dev/null +++ b/doc/user/builders-built-in.xml @@ -0,0 +1,949 @@ + + + + + &SCons; provides the ability to build a lot of different + types of files right "out of the box." + So far, we've been using &SCons;' ability to build + programs, objects and libraries to + illustrate much of the underlying functionality of &SCons; + This section will describe all of the different + types of files that you can build with &SCons;, + and the built-in &Builder; objects used to build them. + By default, all of the &Builder; objects in this section + can be built either with or without an explicit + construction environment. + + + +
+ Programs: the &Program; Builder + + + + As we've seen, the &b-link-Program; Builder + is used to build an executable program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the executable program name to be created. + For example: + + + + + Program('prog', 'file1.o') + + + + + Will create the &prog; + executable on a POSIX system, + the &prog_exe; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-PROGPREFIX; + and + &cv-link-PROGSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') + env.Program('prog', ['file1.o', 'file2.o']) + + + + + Will create a program named + myprog.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the target + program created. + For example: + + + + + Program(['hello.c', 'goodbye.c']) + + + + + Will create the &hello; + executable on a POSIX system, + the &hello_exe; executable on a Windows system. + + + + + + Two construction variables control what libraries + will be linked with the resulting program. + The &cv-link-LIBS; variable is a list of the names of + libraries that will be linked into any programs, + and the &cv-link-LIBPATH; variables is a list of + directories that will be searched for + the specified libraries. + &SCons; will construct the right command-line + options for the running system. + For example: + + + + + env = Environment(LIBS = ['foo1', 'foo2'], + LIBPATH = ['/usr/dir1', 'dir2']) + env.Program(['hello.c', 'goodbye.c']) + + + + + Will execute as follows on a POSIX system: + + + + + % scons -Q + cc -o goodbye.o -c goodbye.c + cc -o hello.o -c hello.c + cc -o hello hello.o goodbye.o -L/usr/dir1 -Ldir2 -lfoo1 -lfoo2 + + + + + And execute as follows on a Windows system: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fogoodbye.obj /c goodbye.c /nologo + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:hello.exe /LIBPATH:\usr\dir1 /LIBPATH:dir2 foo1.lib foo2.lib hello.obj goodbye.obj + + + + + The &cv-LIBS; construction variable + is turned into command line options + by appending the &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; + construction variables to the beginning and end, + respectively, of each specified library. + + + + + + The &cv-LIBPATH; construction variable + is turned into command line options + by appending the &cv-link-LIBDIRPREFIX; and &cv-link-LIBDIRSUFFIX; + construction variables to the beginning and end, + respectively, of each specified library. + + + + + + Other relevant construction variables + include those used by the &b-link-Object; + builders to affect how the + source files specified as input to the &t-Program; + builders are turned into object files; + see the next section. + + + + + + The command line used to control how a program is linked + is specified by the &cv-link-LINKCOM; construction variable. + By default, it uses the + &cv-link-LINK; construction variable + and the &cv-link-LINKFLAGS; construction variable. + + + +
+ +
+ Object-File Builders + + + + &SCons; provides separate Builder objects + to create static and shared object files. + The distinction becomes especially important when + archiving object files into different types of libraries. + + + +
+ The &StaticObject; Builder + + + + The &b-link-StaticObject; Builder + is used to build an object file + suitable for static linking into a program, + or for inclusion in a static library. + The &source; argument is a single source-code file, + and the ⌖ argument is the + name of the static object file to be created. + For example: + + + + + StaticObject('file', 'file.c') + + + + + Will create the &file_o; + object file on a POSIX system, + the &file_obj; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-OBJPREFIX; + and + &cv-link-OBJSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx') + env.StaticObject('file', 'file.c') + + + + + Will create an object file named + myfile.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + beomces the base of the name + of the static object file to be created. + For example: + + + + + StaticObject('file.c') + + + + + Will create the &file_o; + executable on a POSIX system, + the &file_obj; executable on a Windows system. + + + +
+ +
+ The &SharedObject; Builder + + + + The &b-link-SharedObject; Builder + is used to build an object file + suitable for shared linking into a program, + or for inclusion in a shared library. + The &source; argument is a single source-code file, + and the ⌖ argument is the + name of the shared object file to be created. + For example: + + + + + SharedObject('file', 'file.c') + + + + + Will create the &file_o; + object file on a POSIX system, + the &file_obj; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-SHOBJPREFIX; + and + &cv-link-SHOBJSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx') + env.SharedObject('file', 'file.c') + + + + + Will create an object file named + myfile.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name + of the shared object file to be created. + For example: + + + + + SharedObject('file.c') + + + + + Will create the &file_o; + executable on a POSIX system, + the &file_obj; executable on a Windows system. + + + +
+ +
+ The &Object; Builder + + + + The &b-link-Object; Builder is a synonym for &b-link-StaticObject; + and is completely equivalent. + + + +
+ +
+ +
+ Library Builders + + + + &SCons; provides separate Builder objects + to create static and shared libraries. + + + +
+ The &StaticLibrary; Builder + + + + The &b-link-StaticLibrary; Builder + is used to create a library + suitable for static linking into a program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the static library to be created. + For example: + + + + + StaticLibrary('foo', ['file1.c', 'file2.c']) + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-LIBPREFIX; + and + &cv-link-LIBSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx') + env.StaticLibrary('lib', ['file1.o', 'file2.o']) + + + + + Will create an object file named + mylib.xxx + regardless of the system on which it is run. + + + + + StaticLibrary('foo', ['file1.c', 'file2.c']) + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name of the static object file to be created. + For example: + + + + + StaticLibrary(['file.c', 'another.c']) + + + + + Will create the &libfile_a; + library on a POSIX system, + the &file_lib; library on a Windows system. + + + +
+ +
+ The &SharedLibrary; Builder + + + + The &b-link-SharedLibrary; Builder + is used to create a shared library + suitable for linking with a program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the shared library to be created. + For example: + + + + + SharedLibrary('foo', ['file1.c', 'file2.c']) + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-SHLIBPREFIX; + and + &cv-link-SHLIBSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx') + env.SharedLibrary('shared', ['file1.o', 'file2.o']) + + + + + Will create an object file named + myshared.xxx + regardless of the system on which it is run. + + + + + SharedLibrary('foo', ['file1.c', 'file2.c']) + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name of the shared library to be created. + For example: + + + + + SharedLibrary(['file.c', 'another.c']) + + + + + Will create the &libfile_so; + library on a POSIX system, + the &file_dll; library on a Windows system. + + + +
+ +
+ The &Library; Builder + + + + The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary; + and is completely equivalent. + + + +
+ +
+ +
+ Pre-Compiled Headers: the &PCH; Builder + + + + XXX PCH() + + + +
+ +
+ Microsoft Visual C++ Resource Files: the &RES; Builder + + + + XXX RES() + + + +
+ +
+ Source Files + + + + By default + &SCons; supports two Builder objects + that know how to build source files + from other input files. + These are typically invoked "internally" + to turn files that need preprocessing into other source files. + + + +
+ The &CFile; Builder + + + + XXX CFile() + + + + + XXX CFile() programlisting + + + + XXX CFile() screen + + +
+ +
+ The &CXXFile; Builder + + + + XXX CXXFILE() + + + + + XXX CXXFILE() programlisting + + + + XXX CXXFILE() screen + + +
+ +
+ +
+ Documents + + + + &SCons; provides a number of Builder objects + for creating different types of documents. + + + +
+ The &DVI; Builder + + + + XXX DVI() para + + + + + XXX DVI() programlisting + + + + XXX DVI() screen + + +
+ +
+ The &PDF; Builder + + + + XXX PDF() para + + + +
+ +
+ The &PostScript; Builder + + + + XXX PostScript() para + + + + + XXX PostScript() programlisting + + + + XXX PostScript() screen + + +
+ +
+ +
+ Archives + + + + &SCons; provides Builder objects + for creating two different types of archive files. + + + +
+ The &Tar; Builder + + + + The &b-link-Tar; Builder object uses the &tar; + utility to create archives of files + and/or directory trees: + + + + + env = Environment() + env.Tar('out1.tar', ['file1', 'file2']) + env.Tar('out2', 'directory') + + + + % scons -Q . + tar -c -f out1.tar file1 file2 + tar -c -f out2.tar directory + + + + + One common requirement when creating a &tar; archive + is to create a compressed archive using the + option. + This is easily handled by specifying + the value of the &cv-link-TARFLAGS; variable + when you create the construction environment. + Note, however, that the used to + to instruct &tar; to create the archive + is part of the default value of &cv-TARFLAGS;, + so you need to set it both options: + + + + + env = Environment(TARFLAGS = '-c -z') + env.Tar('out.tar.gz', 'directory') + + + + % scons -Q . + tar -c -z -f out.tar.gz directory + + + + + you may also wish to set the value of the + &cv-link-TARSUFFIX; construction variable + to your desired suffix for compress &tar; archives, + so that &SCons; can append it to the target file name + without your having to specify it explicitly: + + + + + env = Environment(TARFLAGS = '-c -z', + TARSUFFIX = '.tgz') + env.Tar('out', 'directory') + + + + % scons -Q . + tar -c -z -f out.tgz directory + + +
+ +
+ The &Zip; Builder + + + + The &b-link-Zip; Builder object creates archives of files + and/or directory trees in the ZIP file format. + Python versions 1.6 or later + contain an internal &zipfile; module + that &SCons; will use. + In this case, given the following + &SConstruct; file: + + + + + env = Environment() + env.Zip('out', ['file1', 'file2']) + + + + + Your output will reflect the fact + that an internal Python function + is being used to create the output ZIP archive: + + + + + % scons -Q . + zip(["out.zip"], ["file1", "file2"]) + + + + + If you're using Python version 1.5.2 to run &SCons;, + then &SCons; will try to use an external + &zip; program as follows: + + + + + % scons -Q . + zip /home/my/project/zip.out file1 file2 + + +
+ +
+ +
+ Java + + + + &SCons; provides Builder objects + for creating various types of Java output files. + + + +
+ Building Class Files: the &Java; Builder + + + + The &b-link-Java; builder takes one or more input + .java files + and turns them into one or more + .class files + Unlike most builders, however, + the &Java; builder takes + target and source directories, + not files, as input. + + + + + env = Environment() + env.Java(target = 'classes', source = 'src') + + + + + The &Java; builder will then + search the specified source directory + tree for all .java files, + and pass any out-of-date + + + + + XXX Java() screen + + +
+ +
+ The &Jar; Builder + + + + XXX The &Jar; builder object + + + + + env = Environment() + env.Java(target = 'classes', source = 'src') + env.Jar(target = '', source = 'classes') + + + + XXX Jar() screen + + +
+ +
+ Building C header and stub files: the &JavaH; Builder + + + + XXX JavaH() para + + + + + XXX JavaH() programlisting + + + + XXX JavaH() screen + + +
+ +
+ Building RMI stub and skeleton class files: the &RMIC; Builder + + + + XXX RMIC() para + + + + + XXX RMIC() programlisting + + + + XXX RMIC() screen + + +
+ +
diff --git a/doc/user/builders-commands.in b/doc/user/builders-commands.in new file mode 100644 index 0000000..687d12d --- /dev/null +++ b/doc/user/builders-commands.in @@ -0,0 +1,156 @@ + + + + + + + Creating a &Builder; and attaching it to a &consenv; + allows for a lot of flexibility when you + want to re-use actions + to build multiple files of the same type. + This can, however, be cumbersome + if you only need to execute one specific command + to build a single file (or group of files). + For these situations, &SCons; supports a + &Command; &Builder; that arranges + for a specific action to be executed + to build a specific file or files. + This looks a lot like the other builders + (like &b-link-Program;, &b-link-Object;, etc.), + but takes as an additional argument + the command to be executed to build the file: + + + + + + env = Environment() + env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") + + + foo.in + + + + + + When executed, + &SCons; runs the specified command, + substituting &cv-link-SOURCE; and &cv-link-TARGET; + as expected: + + + + + scons -Q + + + + + This is often more convenient than + creating a &Builder; object + and adding it to the &cv-link-BUILDERS; variable + of a &consenv; + + + + + + Note that the action you specify to the + &Command; &Builder; can be any legal &SCons; &Action;, + such as a Python function: + + + + + + env = Environment() + def build(target, source, env): + # Whatever it takes to build + return None + env.Command('foo.out', 'foo.in', build) + + + foo.in + + + + + + Which executes as follows: + + + + + scons -Q + + + + + Note that &cv-link-SOURCE; and &cv-link-TARGET; are expanded + in the source and target as well as of SCons 1.1, + so you can write: + + + + + + env.Command('${SOURCE.basename}.out', 'foo.in', build) + + + + + + + which does the same thing as the previous example, but allows you + to avoid repeating yourself. + + + diff --git a/doc/user/builders-commands.xml b/doc/user/builders-commands.xml new file mode 100644 index 0000000..111b1d2 --- /dev/null +++ b/doc/user/builders-commands.xml @@ -0,0 +1,146 @@ + + + + + + + Creating a &Builder; and attaching it to a &consenv; + allows for a lot of flexibility when you + want to re-use actions + to build multiple files of the same type. + This can, however, be cumbersome + if you only need to execute one specific command + to build a single file (or group of files). + For these situations, &SCons; supports a + &Command; &Builder; that arranges + for a specific action to be executed + to build a specific file or files. + This looks a lot like the other builders + (like &b-link-Program;, &b-link-Object;, etc.), + but takes as an additional argument + the command to be executed to build the file: + + + + + env = Environment() + env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") + + + + + When executed, + &SCons; runs the specified command, + substituting &cv-link-SOURCE; and &cv-link-TARGET; + as expected: + + + + + % scons -Q + sed 's/x/y/' < foo.in > foo.out + + + + + This is often more convenient than + creating a &Builder; object + and adding it to the &cv-link-BUILDERS; variable + of a &consenv; + + + + + + Note that the action you specify to the + &Command; &Builder; can be any legal &SCons; &Action;, + such as a Python function: + + + + + env = Environment() + def build(target, source, env): + # Whatever it takes to build + return None + env.Command('foo.out', 'foo.in', build) + + + + + Which executes as follows: + + + + + % scons -Q + build(["foo.out"], ["foo.in"]) + + + + + Note that &cv-link-SOURCE; and &cv-link-TARGET; are expanded + in the source and target as well as of SCons 1.1, + so you can write: + + + + + env.Command('${SOURCE.basename}.out', 'foo.in', build) + + + + + + which does the same thing as the previous example, but allows you + to avoid repeating yourself. + + + diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in new file mode 100644 index 0000000..4f54d99 --- /dev/null +++ b/doc/user/builders-writing.in @@ -0,0 +1,1087 @@ + + + + + + + Although &SCons; provides many useful methods + for building common software products: + programs, libraries, documents. + you frequently want to be + able to build some other type of file + not supported directly by &SCons;. + Fortunately, &SCons; makes it very easy + to define your own &Builder; objects + for any custom file types you want to build. + (In fact, the &SCons; interfaces for creating + &Builder; objects are flexible enough and easy enough to use + that all of the the &SCons; built-in &Builder; objects + are created the mechanisms described in this section.) + + + +
+ Writing Builders That Execute External Commands + + + + The simplest &Builder; to create is + one that executes an external command. + For example, if we want to build + an output file by running the contents + of the input file through a command named + foobuild, + creating that &Builder; might look like: + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + + + + + All the above line does is create a free-standing + &Builder; object. + The next section will show us how to actually use it. + + + +
+ +
+ Attaching a Builder to a &ConsEnv; + + + + A &Builder; object isn't useful + until it's attached to a &consenv; + so that we can call it to arrange + for files to be built. + This is done through the &cv-link-BUILDERS; + &consvar; in an environment. + The &cv-BUILDERS; variable is a Python dictionary + that maps the names by which you want to call + various &Builder; objects to the objects themselves. + For example, if we want to call the + &Builder; we just defined by the name + Foo, + our &SConstruct; file might look like: + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + env.Foo('file.foo', 'file.input') + + + file.input + + + cat + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + + + + + With the &Builder; attached to our &consenv; + with the name Foo, + we can now actually call it like so: + + + + + env.Foo('file.foo', 'file.input') + + + + + Then when we run &SCons; it looks like: + + + + + scons -Q + + + + + Note, however, that the default &cv-BUILDERS; + variable in a &consenv; + comes with a default set of &Builder; objects + already defined: + &b-link-Program;, &b-link-Library;, etc. + And when we explicitly set the &cv-BUILDERS; variable + when we create the &consenv;, + the default &Builder;s are no longer part of + the environment: + + + + + + + import SCons.Defaults; SCons.Defaults.ConstructionEnvironment['TOOLS'] = {}; bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + file.input + + + hello.c + + + + + scons -Q + + + + + To be able to use both our own defined &Builder; objects + and the default &Builder; objects in the same &consenv;, + you can either add to the &cv-BUILDERS; variable + using the &Append; function: + + + + + + env = Environment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env.Append(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + file.input + + + hello.c + + + cat + + + + + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env.Append(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + + Or you can explicitly set the appropriately-named + key in the &cv-BUILDERS; dictionary: + + + + + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env['BUILDERS']['Foo'] = bld + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + + Either way, the same &consenv; + can then use both the newly-defined + Foo &Builder; + and the default &b-link-Program; &Builder;: + + + + + scons -Q + + +
+ +
+ Letting &SCons; Handle The File Suffixes + + + + By supplying additional information + when you create a &Builder;, + you can let &SCons; add appropriate file + suffixes to the target and/or the source file. + For example, rather than having to specify + explicitly that you want the Foo + &Builder; to build the file.foo + target file from the file.input source file, + you can give the .foo + and .input suffixes to the &Builder;, + making for more compact and readable calls to + the Foo &Builder;: + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET', + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + env.Foo('file1') + env.Foo('file2') + + + file1.input + + + file2.input + + + cat + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET', + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file1') + env.Foo('file2') + + + + scons -Q + + + + + You can also supply a prefix keyword argument + if it's appropriate to have &SCons; append a prefix + to the beginning of target file names. + + + +
+ +
+ Builders That Execute Python Functions + + + + In &SCons;, you don't have to call an external command + to build a file. + You can, instead, define a Python function + that a &Builder; object can invoke + to build your target file (or files). + Such a &buildfunc; definition looks like: + + + + + def build_function(target, source, env): + # Code to build "target" from "source" + return None + + + + + The arguments of a &buildfunc; are: + + + + + + + target + + + + + A list of Node objects representing + the target or targets to be + built by this builder function. + The file names of these target(s) + may be extracted using the Python &str; function. + + + + + + + source + + + + + A list of Node objects representing + the sources to be + used by this builder function to build the targets. + The file names of these source(s) + may be extracted using the Python &str; function. + + + + + + + env + + + + + The &consenv; used for building the target(s). + The builder function may use any of the + environment's construction variables + in any way to affect how it builds the targets. + + + + + + + + + + The builder function must + return a 0 or None value + if the target(s) are built successfully. + The builder function + may raise an exception + or return any non-zero value + to indicate that the build is unsuccessful, + + + + + + Once you've defined the Python function + that will build your target file, + defining a &Builder; object for it is as + simple as specifying the name of the function, + instead of an external command, + as the &Builder;'s + action + argument: + + + + + + def build_function(target, source, env): + # Code to build "target" from "source" + return None + bld = Builder(action = build_function, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + file.input + + + + + + And notice that the output changes slightly, + reflecting the fact that a Python function, + not an external command, + is now called to build the target file: + + + + + scons -Q + + +
+ +
+ Builders That Create Actions Using a &Generator; + + + + &SCons; Builder objects can create an action "on the fly" + by using a function called a &generator;. + This provides a great deal of flexibility to + construct just the right list of commands + to build your target. + A &generator; looks like: + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (target[0], source[0]) + + + + + The arguments of a &generator; are: + + + + + + + source + + + + + A list of Node objects representing + the sources to be built + by the command or other action + generated by this function. + The file names of these source(s) + may be extracted using the Python &str; function. + + + + + + + + target + + + + + A list of Node objects representing + the target or targets to be built + by the command or other action + generated by this function. + The file names of these target(s) + may be extracted using the Python &str; function. + + + + + + + + env + + + + + The &consenv; used for building the target(s). + The generator may use any of the + environment's construction variables + in any way to determine what command + or other action to return. + + + + + + + + for_signature + + + + + A flag that specifies whether the + generator is being called to contribute to a build signature, + as opposed to actually executing the command. + + + + + + + + + + + + + The &generator; must return a + command string or other action that will be used to + build the specified target(s) from the specified source(s). + + + + + + Once you've defined a &generator;, + you create a &Builder; to use it + by specifying the generator keyword argument + instead of action. + + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (source[0], target[0]) + bld = Builder(generator = generate_actions, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + env.Foo('file') + + + file.input + + + cat + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (source[0], target[0]) + bld = Builder(generator = generate_actions, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + scons -Q + + + + + Note that it's illegal to specify both an + action + and a + generator + for a &Builder;. + + + +
+ +
+ Builders That Modify the Target or Source Lists Using an &Emitter; + + + + &SCons; supports the ability for a Builder to modify the + lists of target(s) from the specified source(s). + You do this by defining an &emitter; function + that takes as its arguments + the list of the targets passed to the builder, + the list of the sources passed to the builder, + and the construction environment. + The emitter function should return the modified + lists of targets that should be built + and sources from which the targets will be built. + + + + + + For example, suppose you want to define a Builder + that always calls a foobuild program, + and you want to automatically add + a new target file named + new_target + and a new source file named + new_source + whenever it's called. + The &SConstruct; file might look like this: + + + + + + def modify_targets(target, source, env): + target.append('new_target') + source.append('new_source') + return target, source + bld = Builder(action = 'foobuild $TARGETS - $SOURCES', + suffix = '.foo', + src_suffix = '.input', + emitter = modify_targets) + env = Environment(BUILDERS = {'Foo' : bld}) + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + env.Foo('file') + + + file.input + + + new_source + + + cat + + + + + def modify_targets(target, source, env): + target.append('new_target') + source.append('new_source') + return target, source + bld = Builder(action = 'foobuild $TARGETS - $SOURCES', + suffix = '.foo', + src_suffix = '.input', + emitter = modify_targets) + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + + And would yield the following output: + + + + + scons -Q + + + + + One very flexible thing that you can do is + use a construction variable to specify + different emitter functions for different + construction variable. + To do this, specify a string + containing a construction variable + expansion as the emitter when you call + the &Builder; function, + and set that construction variable to + the desired emitter function + in different construction environments: + + + + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + import os + env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + + + file1.input + + + file2.input + + + modify1.input + + + modify2.input + + + cat + + + + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + + + + + + In this example, the modify1.in + and modify2.in files + get added to the source lists + of the different commands: + + + + + scons -Q + + +
+ + + +
+ Where To Put Your Custom Builders and Tools + + + + The site_scons directory gives you a place to + put Python modules you can import into your SConscripts + (site_scons), add-on tools that can integrate into &SCons; + (site_scons/site_tools), and a site_scons/site_init.py file that + gets read before any &SConstruct; or &SConscript;, allowing you to + change &SCons;'s default behavior. + + + + + + If you get a tool from somewhere (the &SCons; wiki or a third party, + for instance) and you'd like to use it in your project, the + site_scons dir is the simplest place to put it. + Tools come in two flavors; either a Python function that operates on + an &Environment; or a Python file containing two functions, exists() + and generate(). + + + + + + A single-function Tool can just be included in your + site_scons/site_init.py file where it will be + parsed and made available for use. For instance, you could have a + site_scons/site_init.py file like this: + + + + + + def TOOL_ADD_HEADER(env): + """A Tool to add a header from $HEADER to the source file""" + add_header = Builder(action=['echo "$HEADER" > $TARGET', + 'cat $SOURCE >> $TARGET']) + env.Append(BUILDERS = {'AddHeader' : add_header}) + env['HEADER'] = '' # set default value + + + env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====") + env.AddHeader('tgt', 'src') + + + hi there + + + + + + and a &SConstruct; like this: + + + + + # Use TOOL_ADD_HEADER from site_scons/site_init.py + env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====") + env.AddHeader('tgt', 'src') + + + + + The TOOL_ADD_HEADER tool method will be + called to add the AddHeader tool to the + environment. + + + + + + + Similarly, a more full-fledged tool with + exists() and generate() + methods can be installed in + site_scons/site_tools/toolname.py. Since + site_scons/site_tools is automatically added + to the head of the tool search path, any tool found there will be + available to all environments. Furthermore, a tool found there + will override a built-in tool of the same name, so if you need to + change the behavior of a built-in tool, site_scons gives you the + hook you need. + + + + Many people have a library of utility Python functions they'd like + to include in &SConscript;s; just put that module in + site_scons/my_utils.py or any valid Python module name of your + choice. For instance you can do something like this in + site_scons/my_utils.py to add build_id and MakeWorkDir functions: + + + + + from SCons.Script import * # for Execute and Mkdir + def build_id(): + """Return a build ID (stub version)""" + return "100" + def MakeWorkDir(workdir): + """Create the specified dir immediately""" + Execute(Mkdir(workdir)) + + + import my_utils + MakeWorkDir('/tmp/work') + print "build_id=" + my_utils.build_id() + + + + + + And then in your &SConscript; or any sub-&SConscript; anywhere in + your build, you can import my_utils and use it: + + + + + import my_utils + print "build_id=" + my_utils.build_id() + my_utils.MakeWorkDir('/tmp/work') + + + + Note that although you can put this library in + site_scons/site_init.py, + it is no better there than site_scons/my_utils.py + since you still have to import that module into your &SConscript;. + Also note that in order to refer to objects in the SCons namespace + such as &Environment; or &Mkdir; or &Execute; in any file other + than a &SConstruct; or &SConscript; you always need to do + + + from SCons.Script import * + + + + This is true in modules in site_scons such as + site_scons/site_init.py as well. + + + + + If you have a machine-wide site dir you'd like to use instead of + ./site_scons, use the + --site-dir option to point to your dir. + site_init.py and + site_tools will be located under that dir. + To avoid using a site_scons dir at all, even + if it exists, use the --no-site-dir option. + + + +
+ + + diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml new file mode 100644 index 0000000..b05cce6 --- /dev/null +++ b/doc/user/builders-writing.xml @@ -0,0 +1,953 @@ + + + + + + + Although &SCons; provides many useful methods + for building common software products: + programs, libraries, documents. + you frequently want to be + able to build some other type of file + not supported directly by &SCons;. + Fortunately, &SCons; makes it very easy + to define your own &Builder; objects + for any custom file types you want to build. + (In fact, the &SCons; interfaces for creating + &Builder; objects are flexible enough and easy enough to use + that all of the the &SCons; built-in &Builder; objects + are created the mechanisms described in this section.) + + + +
+ Writing Builders That Execute External Commands + + + + The simplest &Builder; to create is + one that executes an external command. + For example, if we want to build + an output file by running the contents + of the input file through a command named + foobuild, + creating that &Builder; might look like: + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + + + + + All the above line does is create a free-standing + &Builder; object. + The next section will show us how to actually use it. + + + +
+ +
+ Attaching a Builder to a &ConsEnv; + + + + A &Builder; object isn't useful + until it's attached to a &consenv; + so that we can call it to arrange + for files to be built. + This is done through the &cv-link-BUILDERS; + &consvar; in an environment. + The &cv-BUILDERS; variable is a Python dictionary + that maps the names by which you want to call + various &Builder; objects to the objects themselves. + For example, if we want to call the + &Builder; we just defined by the name + Foo, + our &SConstruct; file might look like: + + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + + + + + With the &Builder; attached to our &consenv; + with the name Foo, + we can now actually call it like so: + + + + + env.Foo('file.foo', 'file.input') + + + + + Then when we run &SCons; it looks like: + + + + + % scons -Q + foobuild < file.input > file.foo + + + + + Note, however, that the default &cv-BUILDERS; + variable in a &consenv; + comes with a default set of &Builder; objects + already defined: + &b-link-Program;, &b-link-Library;, etc. + And when we explicitly set the &cv-BUILDERS; variable + when we create the &consenv;, + the default &Builder;s are no longer part of + the environment: + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + % scons -Q + AttributeError: SConsEnvironment instance has no attribute 'Program': + File "/home/my/project/SConstruct", line 4: + env.Program('hello.c') + + + + + To be able to use both our own defined &Builder; objects + and the default &Builder; objects in the same &consenv;, + you can either add to the &cv-BUILDERS; variable + using the &Append; function: + + + + + + + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env.Append(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + + Or you can explicitly set the appropriately-named + key in the &cv-BUILDERS; dictionary: + + + + + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env['BUILDERS']['Foo'] = bld + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + + Either way, the same &consenv; + can then use both the newly-defined + Foo &Builder; + and the default &b-link-Program; &Builder;: + + + + + % scons -Q + foobuild < file.input > file.foo + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Letting &SCons; Handle The File Suffixes + + + + By supplying additional information + when you create a &Builder;, + you can let &SCons; add appropriate file + suffixes to the target and/or the source file. + For example, rather than having to specify + explicitly that you want the Foo + &Builder; to build the file.foo + target file from the file.input source file, + you can give the .foo + and .input suffixes to the &Builder;, + making for more compact and readable calls to + the Foo &Builder;: + + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET', + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file1') + env.Foo('file2') + + + + % scons -Q + foobuild < file1.input > file1.foo + foobuild < file2.input > file2.foo + + + + + You can also supply a prefix keyword argument + if it's appropriate to have &SCons; append a prefix + to the beginning of target file names. + + + +
+ +
+ Builders That Execute Python Functions + + + + In &SCons;, you don't have to call an external command + to build a file. + You can, instead, define a Python function + that a &Builder; object can invoke + to build your target file (or files). + Such a &buildfunc; definition looks like: + + + + + def build_function(target, source, env): + # Code to build "target" from "source" + return None + + + + + The arguments of a &buildfunc; are: + + + + + + + target + + + + + A list of Node objects representing + the target or targets to be + built by this builder function. + The file names of these target(s) + may be extracted using the Python &str; function. + + + + + + + source + + + + + A list of Node objects representing + the sources to be + used by this builder function to build the targets. + The file names of these source(s) + may be extracted using the Python &str; function. + + + + + + + env + + + + + The &consenv; used for building the target(s). + The builder function may use any of the + environment's construction variables + in any way to affect how it builds the targets. + + + + + + + + + + The builder function must + return a 0 or None value + if the target(s) are built successfully. + The builder function + may raise an exception + or return any non-zero value + to indicate that the build is unsuccessful, + + + + + + Once you've defined the Python function + that will build your target file, + defining a &Builder; object for it is as + simple as specifying the name of the function, + instead of an external command, + as the &Builder;'s + action + argument: + + + + + def build_function(target, source, env): + # Code to build "target" from "source" + return None + bld = Builder(action = build_function, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + + And notice that the output changes slightly, + reflecting the fact that a Python function, + not an external command, + is now called to build the target file: + + + + + % scons -Q + build_function(["file.foo"], ["file.input"]) + + +
+ +
+ Builders That Create Actions Using a &Generator; + + + + &SCons; Builder objects can create an action "on the fly" + by using a function called a &generator;. + This provides a great deal of flexibility to + construct just the right list of commands + to build your target. + A &generator; looks like: + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (target[0], source[0]) + + + + + The arguments of a &generator; are: + + + + + + + source + + + + + A list of Node objects representing + the sources to be built + by the command or other action + generated by this function. + The file names of these source(s) + may be extracted using the Python &str; function. + + + + + + + + target + + + + + A list of Node objects representing + the target or targets to be built + by the command or other action + generated by this function. + The file names of these target(s) + may be extracted using the Python &str; function. + + + + + + + + env + + + + + The &consenv; used for building the target(s). + The generator may use any of the + environment's construction variables + in any way to determine what command + or other action to return. + + + + + + + + for_signature + + + + + A flag that specifies whether the + generator is being called to contribute to a build signature, + as opposed to actually executing the command. + + + + + + + + + + + + + The &generator; must return a + command string or other action that will be used to + build the specified target(s) from the specified source(s). + + + + + + Once you've defined a &generator;, + you create a &Builder; to use it + by specifying the generator keyword argument + instead of action. + + + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (source[0], target[0]) + bld = Builder(generator = generate_actions, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + % scons -Q + foobuild < file.input > file.foo + + + + + Note that it's illegal to specify both an + action + and a + generator + for a &Builder;. + + + +
+ +
+ Builders That Modify the Target or Source Lists Using an &Emitter; + + + + &SCons; supports the ability for a Builder to modify the + lists of target(s) from the specified source(s). + You do this by defining an &emitter; function + that takes as its arguments + the list of the targets passed to the builder, + the list of the sources passed to the builder, + and the construction environment. + The emitter function should return the modified + lists of targets that should be built + and sources from which the targets will be built. + + + + + + For example, suppose you want to define a Builder + that always calls a foobuild program, + and you want to automatically add + a new target file named + new_target + and a new source file named + new_source + whenever it's called. + The &SConstruct; file might look like this: + + + + + + + def modify_targets(target, source, env): + target.append('new_target') + source.append('new_source') + return target, source + bld = Builder(action = 'foobuild $TARGETS - $SOURCES', + suffix = '.foo', + src_suffix = '.input', + emitter = modify_targets) + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + + And would yield the following output: + + + + + % scons -Q + foobuild file.foo new_target - file.input new_source + + + + + One very flexible thing that you can do is + use a construction variable to specify + different emitter functions for different + construction variable. + To do this, specify a string + containing a construction variable + expansion as the emitter when you call + the &Builder; function, + and set that construction variable to + the desired emitter function + in different construction environments: + + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + import os + env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + + + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + + + + + + In this example, the modify1.in + and modify2.in files + get added to the source lists + of the different commands: + + + + + % scons -Q + my_command file1.input modify1.in > file1.foo + my_command file2.input modify2.in > file2.foo + + +
+ + + +
+ Where To Put Your Custom Builders and Tools + + + + The site_scons directory gives you a place to + put Python modules you can import into your SConscripts + (site_scons), add-on tools that can integrate into &SCons; + (site_scons/site_tools), and a site_scons/site_init.py file that + gets read before any &SConstruct; or &SConscript;, allowing you to + change &SCons;'s default behavior. + + + + + + If you get a tool from somewhere (the &SCons; wiki or a third party, + for instance) and you'd like to use it in your project, the + site_scons dir is the simplest place to put it. + Tools come in two flavors; either a Python function that operates on + an &Environment; or a Python file containing two functions, exists() + and generate(). + + + + + + A single-function Tool can just be included in your + site_scons/site_init.py file where it will be + parsed and made available for use. For instance, you could have a + site_scons/site_init.py file like this: + + + + + def TOOL_ADD_HEADER(env): + """A Tool to add a header from $HEADER to the source file""" + add_header = Builder(action=['echo "$HEADER" > $TARGET', + 'cat $SOURCE >> $TARGET']) + env.Append(BUILDERS = {'AddHeader' : add_header}) + env['HEADER'] = '' # set default value + + + + + and a &SConstruct; like this: + + + + + # Use TOOL_ADD_HEADER from site_scons/site_init.py + env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====") + env.AddHeader('tgt', 'src') + + + + + The TOOL_ADD_HEADER tool method will be + called to add the AddHeader tool to the + environment. + + + + + + + Similarly, a more full-fledged tool with + exists() and generate() + methods can be installed in + site_scons/site_tools/toolname.py. Since + site_scons/site_tools is automatically added + to the head of the tool search path, any tool found there will be + available to all environments. Furthermore, a tool found there + will override a built-in tool of the same name, so if you need to + change the behavior of a built-in tool, site_scons gives you the + hook you need. + + + + Many people have a library of utility Python functions they'd like + to include in &SConscript;s; just put that module in + site_scons/my_utils.py or any valid Python module name of your + choice. For instance you can do something like this in + site_scons/my_utils.py to add build_id and MakeWorkDir functions: + + + + from SCons.Script import * # for Execute and Mkdir + def build_id(): + """Return a build ID (stub version)""" + return "100" + def MakeWorkDir(workdir): + """Create the specified dir immediately""" + Execute(Mkdir(workdir)) + + + + + And then in your &SConscript; or any sub-&SConscript; anywhere in + your build, you can import my_utils and use it: + + + + + import my_utils + print "build_id=" + my_utils.build_id() + my_utils.MakeWorkDir('/tmp/work') + + + + Note that although you can put this library in + site_scons/site_init.py, + it is no better there than site_scons/my_utils.py + since you still have to import that module into your &SConscript;. + Also note that in order to refer to objects in the SCons namespace + such as &Environment; or &Mkdir; or &Execute; in any file other + than a &SConstruct; or &SConscript; you always need to do + + + from SCons.Script import * + + + + This is true in modules in site_scons such as + site_scons/site_init.py as well. + + + + + If you have a machine-wide site dir you'd like to use instead of + ./site_scons, use the + --site-dir option to point to your dir. + site_init.py and + site_tools will be located under that dir. + To avoid using a site_scons dir at all, even + if it exists, use the --no-site-dir option. + + + +
+ + + diff --git a/doc/user/builders.in b/doc/user/builders.in new file mode 100644 index 0000000..a2f94ce --- /dev/null +++ b/doc/user/builders.in @@ -0,0 +1,57 @@ + + + + + + +This appendix contains descriptions of all of the +Builders that are potentially +available "out of the box" in this version of SCons. + + + + + +&builders-gen; + + diff --git a/doc/user/builders.xml b/doc/user/builders.xml new file mode 100644 index 0000000..a2f94ce --- /dev/null +++ b/doc/user/builders.xml @@ -0,0 +1,57 @@ + + + + + + +This appendix contains descriptions of all of the +Builders that are potentially +available "out of the box" in this version of SCons. + + + + + +&builders-gen; + + diff --git a/doc/user/caching.in b/doc/user/caching.in new file mode 100644 index 0000000..31b1103 --- /dev/null +++ b/doc/user/caching.in @@ -0,0 +1,502 @@ + + + + + On multi-developer software projects, + you can sometimes speed up every developer's builds a lot by + allowing them to share the derived files that they build. + &SCons; makes this easy, as well as reliable. + + + +
+ Specifying the Shared Cache Directory + + + + To enable sharing of derived files, + use the &CacheDir; function + in any &SConscript; file: + + + + + + env = Environment() + env.Program('hello.c') + CacheDir('cache') + + + hello.c + + + + + CacheDir('/usr/local/build_cache') + + + + + + Note that the directory you specify must already exist + and be readable and writable by all developers + who will be sharing derived files. + It should also be in some central location + that all builds will be able to access. + In environments where developers are using separate systems + (like individual workstations) for builds, + this directory would typically be + on a shared or NFS-mounted file system. + + + + + + Here's what happens: + When a build has a &CacheDir; specified, + every time a file is built, + it is stored in the shared cache directory + along with its MD5 build signature. + + + Actually, the MD5 signature is used as the name of the file + in the shared cache directory in which the contents are stored. + + + On subsequent builds, + before an action is invoked to build a file, + &SCons; will check the shared cache directory + to see if a file with the exact same build + signature already exists. + If so, the derived file will not be built locally, + but will be copied into the local build directory + from the shared cache directory, + like so: + + + + + scons -Q + scons -Q -c + scons -Q + + + + + Note that the &CacheDir; feature still calculates + MD5 build sigantures for the shared cache file names + even if you configure &SCons; to use timestamps + to decide if files are up to date. + (See the + chapter for information about the &Decider; function.) + Consequently, using &CacheDir; may reduce or eliminate any + potential performance improvements + from using timestamps for up-to-date decisions. + + + +
+ +
+ Keeping Build Output Consistent + + + + One potential drawback to using a shared cache + is that the output printed by &SCons; + can be inconsistent from invocation to invocation, + because any given file may be rebuilt one time + and retrieved from the shared cache the next time. + This can make analyzing build output more difficult, + especially for automated scripts that + expect consistent output each time. + + + + + + If, however, you use the --cache-show option, + &SCons; will print the command line that it + would have executed + to build the file, + even when it is retrieving the file from the shared cache. + This makes the build output consistent + every time the build is run: + + + + + scons -Q + scons -Q -c + scons -Q --cache-show + + + + + The trade-off, of course, is that you no longer + know whether or not &SCons; + has retrieved a derived file from cache + or has rebuilt it locally. + + + +
+ +
+ Not Using the Shared Cache for Specific Files + + + + You may want to disable caching for certain + specific files in your configuration. + For example, if you only want to put + executable files in a central cache, + but not the intermediate object files, + you can use the &NoCache; + function to specify that the + object files should not be cached: + + + + + + env = Environment() + obj = env.Object('hello.c') + env.Program('hello.c') + CacheDir('cache') + NoCache('hello.o') + + + hello.c + + + + + + + + Then when you run &scons; after cleaning + the built targets, + it will recompile the object file locally + (since it doesn't exist in the shared cache directory), + but still realize that the shared cache directory + contains an up-to-date executable program + that can be retrieved instead of re-linking: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + cc -o hello.o -c hello.c + Retrieved `hello' from cache + + +
+ +
+ Disabling the Shared Cache + + + + Retrieving an already-built file + from the shared cache + is usually a significant time-savings + over rebuilding the file, + but how much of a savings + (or even whether it saves time at all) + can depend a great deal on your + system or network configuration. + For example, retrieving cached files + from a busy server over a busy network + might end up being slower than + rebuilding the files locally. + + + + + + In these cases, you can specify + the --cache-disable + command-line option to tell &SCons; + to not retrieve already-built files from the + shared cache directory: + + + + + scons -Q + scons -Q -c + scons -Q + scons -Q -c + scons -Q --cache-disable + + +
+ +
+ Populating a Shared Cache With Already-Built Files + + + + Sometimes, you may have one or more derived files + already built in your local build tree + that you wish to make available to other people doing builds. + For example, you may find it more effective to perform + integration builds with the cache disabled + (per the previous section) + and only populate the shared cache directory + with the built files after the integration build + has completed successfully. + This way, the cache will only get filled up + with derived files that are part of a complete, successful build + not with files that might be later overwritten + while you debug integration problems. + + + + + + In this case, you can use the + the --cache-force option + to tell &SCons; to put all derived files in the cache, + even if the files already exist in your local tree + from having been built by a previous invocation: + + + + + scons -Q --cache-disable + scons -Q -c + scons -Q --cache-disable + scons -Q --cache-force + scons -Q + + + + + Notice how the above sample run + demonstrates that the --cache-disable + option avoids putting the built + hello.o + and + hello files in the cache, + but after using the --cache-force option, + the files have been put in the cache + for the next invocation to retrieve. + + + +
+ +
+ Minimizing Cache Contention: the <literal>--random</literal> Option + + + + If you allow multiple builds to update the + shared cache directory simultaneously, + two builds that occur at the same time + can sometimes start "racing" + with one another to build the same files + in the same order. + If, for example, + you are linking multiple files into an executable program: + + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + f1.c + f2.c + f3.c + f4.c + f5.c + f6.c + + + + + &SCons; will normally build the input object files + on which the program depends in their normal, sorted order: + + + + + scons -Q + + + + + But if two such builds take place simultaneously, + they may each look in the cache at nearly the same + time and both decide that f1.o + must be rebuilt and pushed into the shared cache directory, + then both decide that f2.o + must be rebuilt (and pushed into the shared cache directory), + then both decide that f3.o + must be rebuilt... + This won't cause any actual build problems--both + builds will succeed, + generate correct output files, + and populate the cache--but + it does represent wasted effort. + + + + + + To alleviate such contention for the cache, + you can use the --random command-line option + to tell &SCons; to build dependencies + in a random order: + + + + + + + % scons -Q --random + cc -o f3.o -c f3.c + cc -o f1.o -c f1.c + cc -o f5.o -c f5.c + cc -o f2.o -c f2.c + cc -o f4.o -c f4.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + Multiple builds using the --random option + will usually build their dependencies in different, + random orders, + which minimizes the chances for a lot of + contention for same-named files + in the shared cache directory. + Multiple simultaneous builds might still race to try to build + the same target file on occasion, + but long sequences of inefficient contention + should be rare. + + + + + + Note, of course, + the --random option + will cause the output that &SCons; prints + to be inconsistent from invocation to invocation, + which may be an issue when + trying to compare output from different build runs. + + + + + + If you want to make sure dependencies will be built + in a random order without having to specify + the --random on very command line, + you can use the &SetOption; function to + set the random option + within any &SConscript; file: + + + + + + SetOption('random', 1) + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + f1.c + f2.c + f3.c + f4.c + f5.c + f6.c + + +
+ + + + diff --git a/doc/user/caching.xml b/doc/user/caching.xml new file mode 100644 index 0000000..073539c --- /dev/null +++ b/doc/user/caching.xml @@ -0,0 +1,506 @@ + + + + + On multi-developer software projects, + you can sometimes speed up every developer's builds a lot by + allowing them to share the derived files that they build. + &SCons; makes this easy, as well as reliable. + + + +
+ Specifying the Shared Cache Directory + + + + To enable sharing of derived files, + use the &CacheDir; function + in any &SConscript; file: + + + + + CacheDir('/usr/local/build_cache') + + + + + Note that the directory you specify must already exist + and be readable and writable by all developers + who will be sharing derived files. + It should also be in some central location + that all builds will be able to access. + In environments where developers are using separate systems + (like individual workstations) for builds, + this directory would typically be + on a shared or NFS-mounted file system. + + + + + + Here's what happens: + When a build has a &CacheDir; specified, + every time a file is built, + it is stored in the shared cache directory + along with its MD5 build signature. + + + Actually, the MD5 signature is used as the name of the file + in the shared cache directory in which the contents are stored. + + + On subsequent builds, + before an action is invoked to build a file, + &SCons; will check the shared cache directory + to see if a file with the exact same build + signature already exists. + If so, the derived file will not be built locally, + but will be copied into the local build directory + from the shared cache directory, + like so: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + Retrieved `hello.o' from cache + Retrieved `hello' from cache + + + + + Note that the &CacheDir; feature still calculates + MD5 build sigantures for the shared cache file names + even if you configure &SCons; to use timestamps + to decide if files are up to date. + (See the + chapter for information about the &Decider; function.) + Consequently, using &CacheDir; may reduce or eliminate any + potential performance improvements + from using timestamps for up-to-date decisions. + + + +
+ +
+ Keeping Build Output Consistent + + + + One potential drawback to using a shared cache + is that the output printed by &SCons; + can be inconsistent from invocation to invocation, + because any given file may be rebuilt one time + and retrieved from the shared cache the next time. + This can make analyzing build output more difficult, + especially for automated scripts that + expect consistent output each time. + + + + + + If, however, you use the --cache-show option, + &SCons; will print the command line that it + would have executed + to build the file, + even when it is retrieving the file from the shared cache. + This makes the build output consistent + every time the build is run: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q --cache-show + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + The trade-off, of course, is that you no longer + know whether or not &SCons; + has retrieved a derived file from cache + or has rebuilt it locally. + + + +
+ +
+ Not Using the Shared Cache for Specific Files + + + + You may want to disable caching for certain + specific files in your configuration. + For example, if you only want to put + executable files in a central cache, + but not the intermediate object files, + you can use the &NoCache; + function to specify that the + object files should not be cached: + + + + + env = Environment() + obj = env.Object('hello.c') + env.Program('hello.c') + CacheDir('cache') + NoCache('hello.o') + + + + + Then when you run &scons; after cleaning + the built targets, + it will recompile the object file locally + (since it doesn't exist in the shared cache directory), + but still realize that the shared cache directory + contains an up-to-date executable program + that can be retrieved instead of re-linking: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + cc -o hello.o -c hello.c + Retrieved `hello' from cache + + +
+ +
+ Disabling the Shared Cache + + + + Retrieving an already-built file + from the shared cache + is usually a significant time-savings + over rebuilding the file, + but how much of a savings + (or even whether it saves time at all) + can depend a great deal on your + system or network configuration. + For example, retrieving cached files + from a busy server over a busy network + might end up being slower than + rebuilding the files locally. + + + + + + In these cases, you can specify + the --cache-disable + command-line option to tell &SCons; + to not retrieve already-built files from the + shared cache directory: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + Retrieved `hello.o' from cache + Retrieved `hello' from cache + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q --cache-disable + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Populating a Shared Cache With Already-Built Files + + + + Sometimes, you may have one or more derived files + already built in your local build tree + that you wish to make available to other people doing builds. + For example, you may find it more effective to perform + integration builds with the cache disabled + (per the previous section) + and only populate the shared cache directory + with the built files after the integration build + has completed successfully. + This way, the cache will only get filled up + with derived files that are part of a complete, successful build + not with files that might be later overwritten + while you debug integration problems. + + + + + + In this case, you can use the + the --cache-force option + to tell &SCons; to put all derived files in the cache, + even if the files already exist in your local tree + from having been built by a previous invocation: + + + + + % scons -Q --cache-disable + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q --cache-disable + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q --cache-force + scons: `.' is up to date. + % scons -Q + scons: `.' is up to date. + + + + + Notice how the above sample run + demonstrates that the --cache-disable + option avoids putting the built + hello.o + and + hello files in the cache, + but after using the --cache-force option, + the files have been put in the cache + for the next invocation to retrieve. + + + +
+ +
+ Minimizing Cache Contention: the <literal>--random</literal> Option + + + + If you allow multiple builds to update the + shared cache directory simultaneously, + two builds that occur at the same time + can sometimes start "racing" + with one another to build the same files + in the same order. + If, for example, + you are linking multiple files into an executable program: + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + + + + &SCons; will normally build the input object files + on which the program depends in their normal, sorted order: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + cc -o f4.o -c f4.c + cc -o f5.o -c f5.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + But if two such builds take place simultaneously, + they may each look in the cache at nearly the same + time and both decide that f1.o + must be rebuilt and pushed into the shared cache directory, + then both decide that f2.o + must be rebuilt (and pushed into the shared cache directory), + then both decide that f3.o + must be rebuilt... + This won't cause any actual build problems--both + builds will succeed, + generate correct output files, + and populate the cache--but + it does represent wasted effort. + + + + + + To alleviate such contention for the cache, + you can use the --random command-line option + to tell &SCons; to build dependencies + in a random order: + + + + + + + % scons -Q --random + cc -o f3.o -c f3.c + cc -o f1.o -c f1.c + cc -o f5.o -c f5.c + cc -o f2.o -c f2.c + cc -o f4.o -c f4.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + Multiple builds using the --random option + will usually build their dependencies in different, + random orders, + which minimizes the chances for a lot of + contention for same-named files + in the shared cache directory. + Multiple simultaneous builds might still race to try to build + the same target file on occasion, + but long sequences of inefficient contention + should be rare. + + + + + + Note, of course, + the --random option + will cause the output that &SCons; prints + to be inconsistent from invocation to invocation, + which may be an issue when + trying to compare output from different build runs. + + + + + + If you want to make sure dependencies will be built + in a random order without having to specify + the --random on very command line, + you can use the &SetOption; function to + set the random option + within any &SConscript; file: + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + SetOption('random', 1) + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + +
+ + + + diff --git a/doc/user/command-line.in b/doc/user/command-line.in new file mode 100644 index 0000000..686146e --- /dev/null +++ b/doc/user/command-line.in @@ -0,0 +1,2327 @@ + + + + + &SCons; provides a number of ways + for the writer of the &SConscript; files + to give the users who will run &SCons; + a great deal of control over the build execution. + The arguments that the user can specify on + the command line are broken down into three types: + + + + + + + Options + + + + + Command-line options always begin with + one or two - (hyphen) characters. + &SCons; provides ways for you to examine + and set options values from within your &SConscript; files, + as well as the ability to define your own + custom options. + See , below. + + + + + + + Variables + + + + + Any command-line argument containing an = + (equal sign) is considered a variable setting with the form + variable=value + &SCons; provides direct access to + all of the command-line variable settings, + the ability to apply command-line variable settings + to construction environments, + and functions for configuring + specific types of variables + (Boolean values, path names, etc.) + with automatic validation of the user's specified values. + See , below. + + + + + + + Targets + + + + + Any command-line argument that is not an option + or a variable setting + (does not begin with a hyphen + and does not contain an equal sign) + is considered a target that the user + (presumably) wants &SCons; to build. + A list of Node objects representing + the target or targets to build. + &SCons; provides access to the list of specified targets, + as well as ways to set the default list of targets + from within the &SConscript; files. + See , below. + + + + + + + +
+ Command-Line Options + + + + &SCons; has many command-line options + that control its behavior. + A &SCons; command-line option + always begins with one or two - (hyphen) + characters. + + + +
+ Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable + + + + Users may find themselves supplying + the same command-line options every time + they run &SCons;. + For example, you might find it saves time + to specify a value of -j 2 + to have &SCons; run up to two build commands in parallel. + To avoid having to type -j 2 by hand + every time, + you can set the external environment variable + &SCONSFLAGS; to a string containing + command-line options that you want &SCons; to use. + + + + + + If, for example, + you're using a POSIX shell that's + compatible with the Bourne shell, + and you always want &SCons; to use the + -Q option, + you can set the &SCONSFLAGS; + environment as follows: + + + + + + def b(target, source, env): + pass + def s(target, source, env): + return " ... [build output] ..." + a = Action(b, strfunction = s) + env = Environment(BUILDERS = {'A' : Builder(action=a)}) + env.A('foo.out', 'foo.in') + + + foo.in + + + + + scons + export SCONSFLAGS="-Q" + scons + + + + + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: + + + + + $ setenv SCONSFLAGS "-Q" + + + + + Windows users may typically want to set the + &SCONSFLAGS; in the appropriate tab of the + System Properties window. + + + +
+ +
+ Getting Values Set by Command-Line Options: the &GetOption; Function + + + + &SCons; provides the &GetOption; function + to get the values set by the various command-line options. + One common use of this is to check whether or not + the -h or --help option + has been specified. + Normally, &SCons; does not print its help text + until after it has read all of the &SConscript; files, + because it's possible that help text has been added + by some subsidiary &SConscript; file deep in the + source tree hierarchy. + Of course, reading all of the &SConscript; files + takes extra time. + + + + + + If you know that your configuration does not define + any additional help text in subsidiary &SConscript; files, + you can speed up the command-line help available to users + by using the &GetOption; function to load the + subsidiary &SConscript; files only if the + the user has not specified + the -h or --help option, + like so: + + + + + + + + In general, the string that you pass to the + &GetOption; function to fetch the value of a command-line + option setting is the same as the "most common" long option name + (beginning with two hyphen characters), + although there are some exceptions. + The list of &SCons; command-line options + and the &GetOption; strings for fetching them, + are available in the + section, + below. + + + +
+ +
+ Setting Values of Command-Line Options: the &SetOption; Function + + + + You can also set the values of &SCons; + command-line options from within the &SConscript; files + by using the &SetOption; function. + The strings that you use to set the values of &SCons; + command-line options are available in the + section, + below. + + + + + + One use of the &SetOption; function is to + specify a value for the -j + or --jobs option, + so that users get the improved performance + of a parallel build without having to specify the option by hand. + A complicating factor is that a good value + for the -j option is + somewhat system-dependent. + One rough guideline is that the more processors + your system has, + the higher you want to set the + -j value, + in order to take advantage of the number of CPUs. + + + + + + For example, suppose the administrators + of your development systems + have standardized on setting a + NUM_CPU environment variable + to the number of processors on each system. + A little bit of Python code + to access the environment variable + and the &SetOption; function + provide the right level of flexibility: + + + + + + import os + num_cpu = int(os.environ.get('NUM_CPU', 2)) + SetOption('num_jobs', num_cpu) + print "running with -j", GetOption('num_jobs') + + + foo.in + + + + + + The above snippet of code + sets the value of the --jobs option + to the value specified in the + $NUM_CPU environment variable. + (This is one of the exception cases + where the string is spelled differently from + the from command-line option. + The string for fetching or setting the --jobs + value is num_jobs + for historical reasons.) + The code in this example prints the num_jobs + value for illustrative purposes. + It uses a default value of 2 + to provide some minimal parallelism even on + single-processor systems: + + + + + scons -Q + + + + + But if the $NUM_CPU + environment variable is set, + then we use that for the default number of jobs: + + + + + export NUM_CPU="4" + scons -Q + + + + + But any explicit + -j or --jobs + value the user specifies an the command line is used first, + regardless of whether or not + the $NUM_CPU environment + variable is set: + + + + + scons -Q -j 7 + export NUM_CPU="4" + scons -Q -j 3 + + +
+ +
+ Strings for Getting or Setting Values of &SCons; Command-Line Options + + + + The strings that you can pass to the &GetOption; + and &SetOption; functions usually correspond to the + first long-form option name + (beginning with two hyphen characters: --), + after replacing any remaining hyphen characters + with underscores. + + + + + + The full list of strings and the variables they + correspond to is as follows: + + + + + + + + + + String for &GetOption; and &SetOption; + Command-Line Option(s) + + + + + + + + cache_debug + + + + + cache_disable + + + + + cache_force + + + + + cache_show + + + + + clean + , + , + + + + + config + + + + + directory + , + + + + + diskcheck + + + + + duplicate + + + + + file + , + , + , + + + + + help + , + + + + + ignore_errors + + + + + implicit_cache + + + + + implicit_deps_changed + + + + + implicit_deps_unchanged + + + + + interactive + , + + + + + keep_going + , + + + + + max_drift + + + + + no_exec + , + , + , + , + + + + + no_site_dir + + + + + num_jobs + , + + + + + profile_file + + + + + question + , + + + + + random + + + + + repository + , + , + + + + + silent + , + , + + + + + site_dir + + + + + stack_size + + + + + taskmastertrace_file + + + + + warn + + + + + + + + +
+ +
+ Adding Custom Command-Line Options: the &AddOption; Function + + + + &SCons; also allows you to define your own + command-line options with the &AddOption; function. + The &AddOption; function takes the same arguments + as the optparse.add_option function + from the standard Python library. + + + The &AddOption; function is, + in fact, implemented using a subclass + of the optparse.OptionParser. + + + Once you have added a custom command-line option + with the &AddOption; function, + the value of the option (if any) is immediately available + using the standard &GetOption; function. + (The value can also be set using &SetOption;, + although that's not very useful in practice + because a default value can be specified in + directly in the &AddOption; call.) + + + + + + One useful example of using this functionality + is to provide a for users: + + + + + + AddOption('--prefix', + dest='prefix', + type='string', + nargs=1, + action='store', + metavar='DIR', + help='installation prefix') + + env = Environment(PREFIX = GetOption('prefix')) + + installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in') + Default(installed_foo) + + + foo.in + + + + + + The above code uses the &GetOption; function + to set the $PREFIX + construction variable to any + value that the user specifies with a command-line + option of --prefix. + Because $PREFIX + will expand to a null string if it's not initialized, + running &SCons; without the + option of --prefix + will install the file in the + /usr/bin/ directory: + + + + + scons -Q -n + + + + + But specifying --prefix=/tmp/install + on the command line causes the file to be installed in the + /tmp/install/usr/bin/ directory: + + + + + scons -Q -n --prefix=/tmp/install + + +
+ +
+ +
+ Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables + + + + You may want to control various aspects + of your build by allowing the user + to specify variable=value + values on the command line. + For example, suppose you + want users to be able to + build a debug version of a program + by running &SCons; as follows: + + + + + % scons -Q debug=1 + + + + + &SCons; provides an &ARGUMENTS; dictionary + that stores all of the + variable=value + assignments from the command line. + This allows you to modify + aspects of your build in response + to specifications on the command line. + (Note that unless you want to require + that users always + specify a variable, + you probably want to use + the Python + ARGUMENTS.get() function, + which allows you to specify a default value + to be used if there is no specification + on the command line.) + + + + + + The following code sets the &cv-link-CCFLAGS; construction + variable in response to the debug + flag being set in the &ARGUMENTS; dictionary: + + + + + + env = Environment() + debug = ARGUMENTS.get('debug', 0) + if int(debug): + env.Append(CCFLAGS = '-g') + env.Program('prog.c') + + + prog.c + + + + + + This results in the -g + compiler option being used when + debug=1 + is used on the command line: + + + + + scons -Q debug=0 + scons -Q debug=0 + scons -Q debug=1 + scons -Q debug=1 + + + + + Notice that &SCons; keeps track of + the last values used to build the object files, + and as a result correctly rebuilds + the object and executable files + only when the value of the debug + argument has changed. + + + + + + The &ARGUMENTS; dictionary has two minor drawbacks. + First, because it is a dictionary, + it can only store one value for each specified keyword, + and thus only "remembers" the last setting + for each keyword on the command line. + This makes the &ARGUMENTS; dictionary + inappropriate if users should be able to + specify multiple values + on the command line for a given keyword. + Second, it does not preserve + the order in which the variable settings + were specified, + which is a problem if + you want the configuration to + behave differently in response + to the order in which the build + variable settings were specified on the command line. + + + + + + To accomodate these requirements, + &SCons; provides an &ARGLIST; variable + that gives you direct access to + variable=value + settings on the command line, + in the exact order they were specified, + and without removing any duplicate settings. + Each element in the &ARGLIST; variable + is itself a two-element list + containing the keyword and the value + of the setting, + and you must loop through, + or otherwise select from, + the elements of &ARGLIST; to + process the specific settings you want + in whatever way is appropriate for your configuration. + For example, + the following code to let the user + add to the &CPPDEFINES; construction variable + by specifying multiple + define= + settings on the command line: + + + + + + cppdefines = [] + for key, value in ARGLIST: + if key == 'define': + cppdefines.append(value) + env = Environment(CPPDEFINES = cppdefines) + env.Object('prog.c') + + + prog.c + + + + + + Yields the following output: + + + + + scons -Q define=FOO + scons -Q define=FOO define=BAR + + + + + Note that the &ARGLIST; and &ARGUMENTS; + variables do not interfere with each other, + but merely provide slightly different views + into how the user specified + variable=value + settings on the command line. + You can use both variables in the same + &SCons; configuration. + In general, the &ARGUMENTS; dictionary + is more convenient to use, + (since you can just fetch variable + settings through a dictionary access), + and the &ARGLIST; list + is more flexible + (since you can examine the + specific order in which + the user's command-line variabe settings). + + + +
+ Controlling Command-Line Build Variables + + + + Being able to use a command-line build variable like + debug=1 is handy, + but it can be a chore to write specific Python code + to recognize each such variable, + check for errors and provide appropriate messages, + and apply the values to a construction variable. + To help with this, + &SCons; supports a class to + define such build variables easily, + and a mechanism to apply the + build variables to a construction environment. + This allows you to control how the build variables affect + construction environments. + + + + + + For example, suppose that you want users to set + a &RELEASE; construction variable on the + command line whenever the time comes to build + a program for release, + and that the value of this variable + should be added to the command line + with the appropriate -D option + (or other command line option) + to pass the value to the C compiler. + Here's how you might do that by setting + the appropriate value in a dictionary for the + &cv-link-CPPDEFINES; construction variable: + + + + + + vars = Variables() + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + + + foo.c + + + bar.c + + + + + + This &SConstruct; file first creates a &Variables; object + (the vars = Variables() call), + and then uses the object's &Add; + method to indicate that the &RELEASE; + variable can be set on the command line, + and that its default value will be 0 + (the third argument to the &Add; method). + The second argument is a line of help text; + we'll learn how to use it in the next section. + + + + + + We then pass the created &Variables; + object as a &variables; keyword argument + to the &Environment; call + used to create the construction environment. + This then allows a user to set the + &RELEASE; build variable on the command line + and have the variable show up in + the command line used to build each object from + a C source file: + + + + + scons -Q RELEASE=1 + + + + + NOTE: Before &SCons; release 0.98.1, these build variables + were known as "command-line build options." + The class was actually named the &Options; class, + and in the sections below, + the various functions were named + &BoolOption;, &EnumOption;, &ListOption;, + &PathOption;, &PackageOption; and &AddOptions;. + These older names still work, + and you may encounter them in older + &SConscript; fles, + but their use is discouraged + and will be officially deprecated some day. + + + +
+ +
+ Providing Help for Command-Line Build Variables + + + + To make command-line build variables most useful, + you ideally want to provide + some help text that will describe + the available variables + when the user runs scons -h. + You could write this text by hand, + but &SCons; provides an easier way. + &Variables; objects support a + &GenerateHelpText; method + that will, as its name suggests, + generate text that describes + the various variables that + have been added to it. + You then pass the output from this method to + the &Help; function: + + + + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars) + Help(vars.GenerateHelpText(env)) + + + + + + &SCons; will now display some useful text + when the -h option is used: + + + + + scons -Q -h + + + + + Notice that the help output shows the default value, + and the current actual value of the build variable. + + + +
+ +
+ Reading Build Variables From a File + + + + Giving the user a way to specify the + value of a build variable on the command line + is useful, + but can still be tedious + if users must specify the variable + every time they run &SCons;. + We can let users provide customized build variable settings + in a local file by providing a + file name when we create the + &Variables; object: + + + + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + Help(vars.GenerateHelpText(env)) + + + foo.c + + + bar.c + + + RELEASE = 1 + + + + + + This then allows the user to control the &RELEASE; + variable by setting it in the &custom_py; file: + + + + + + + + Note that this file is actually executed + like a Python script. + Now when we run &SCons;: + + + + + scons -Q + + + + + And if we change the contents of &custom_py; to: + + + + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + Help(vars.GenerateHelpText(env)) + + + foo.c + + + bar.c + + + RELEASE = 0 + + + + + + The object files are rebuilt appropriately + with the new variable: + + + + + scons -Q + + +
+ +
+ Pre-Defined Build Variable Functions + + + + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build variables. + + + +
+ True/False Values: the &BoolVariable; Build Variable Function + + + + It's often handy to be able to specify a + variable that controls a simple Boolean variable + with a &true; or &false; value. + It would be even more handy to accomodate + users who have different preferences for how to represent + &true; or &false; values. + The &BoolVariable; function + makes it easy to accomodate these + common representations of + &true; or &false;. + + + + + + The &BoolVariable; function takes three arguments: + the name of the build variable, + the default value of the build variable, + and the help string for the variable. + It then returns appropriate information for + passing to the &Add; method of a &Variables; object, like so: + + + + + + vars = Variables('custom.py') + vars.Add(BoolVariable('RELEASE', 'Set to build for release', 0)) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program('foo.c') + + + foo.c + + + + + + With this build variable, + the &RELEASE; variable can now be enabled by + setting it to the value yes + or t: + + + + + scons -Q RELEASE=yes foo.o + + + + scons -Q RELEASE=t foo.o + + + + + Other values that equate to &true; include + y, + 1, + on + and + all. + + + + + + Conversely, &RELEASE; may now be given a &false; + value by setting it to + no + or + f: + + + + + scons -Q RELEASE=no foo.o + + + + scons -Q RELEASE=f foo.o + + + + + Other values that equate to &false; include + n, + 0, + off + and + none. + + + + + + Lastly, if a user tries to specify + any other value, + &SCons; supplies an appropriate error message: + + + + + scons -Q RELEASE=bad_value foo.o + + +
+ +
+ Single Value From a List: the &EnumVariable; Build Variable Function + + + + Suppose that we want a user to be able to + set a &COLOR; variable + that selects a background color to be + displayed by an application, + but that we want to restrict the + choices to a specific set of allowed colors. + This can be set up quite easily + using the &EnumVariable;, + which takes a list of &allowed_values + in addition to the variable name, + default value, + and help text arguments: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'))) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + foo.c + + + + + + The user can now explicity set the &COLOR; build variable + to any of the specified allowed values: + + + + + scons -Q COLOR=red foo.o + scons -Q COLOR=blue foo.o + scons -Q COLOR=green foo.o + + + + + But, almost more importantly, + an attempt to set &COLOR; + to a value that's not in the list + generates an error message: + + + + + scons -Q COLOR=magenta foo.o + + + + + The &EnumVariable; function also supports a way + to map alternate names to allowed values. + Suppose, for example, + that we want to allow the user + to use the word navy as a synonym for + blue. + We do this by adding a ↦ dictionary + that will map its key values + to the desired legal value: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'})) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + foo.c + + + + + + As desired, the user can then use + navy on the command line, + and &SCons; will translate it into blue + when it comes time to use the &COLOR; + variable to build a target: + + + + + scons -Q COLOR=navy foo.o + + + + + By default, when using the &EnumVariable; function, + arguments that differ + from the legal values + only in case + are treated as illegal values: + + + + + scons -Q COLOR=Red foo.o + scons -Q COLOR=BLUE foo.o + scons -Q COLOR=nAvY foo.o + + + + + The &EnumVariable; function can take an additional + &ignorecase; keyword argument that, + when set to 1, + tells &SCons; to allow case differences + when the values are specified: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=1)) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + foo.c + + + + + + Which yields the output: + + + + + scons -Q COLOR=Red foo.o + scons -Q COLOR=BLUE foo.o + scons -Q COLOR=nAvY foo.o + scons -Q COLOR=green foo.o + + + + + Notice that an &ignorecase; value of 1 + preserves the case-spelling that the user supplied. + If you want &SCons; to translate the names + into lower-case, + regardless of the case used by the user, + specify an &ignorecase; value of 2: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=2)) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + foo.c + + + + + + Now &SCons; will use values of + red, + green or + blue + regardless of how the user spells + those values on the command line: + + + + + scons -Q COLOR=Red foo.o + scons -Q COLOR=nAvY foo.o + scons -Q COLOR=GREEN foo.o + + +
+ +
+ Multiple Values From a List: the &ListVariable; Build Variable Function + + + + Another way in which you might want to allow users + to control a build variable is to + specify a list of one or more legal values. + &SCons; supports this through the &ListVariable; function. + If, for example, we want a user to be able to set a + &COLORS; variable to one or more of the legal list of values: + + + + + + vars = Variables('custom.py') + vars.Add(ListVariable('COLORS', 'List of colors', 0, + ['red', 'green', 'blue'])) + env = Environment(variables = vars, + CPPDEFINES={'COLORS' : '"${COLORS}"'}) + env.Program('foo.c') + + + foo.c + + + + + + A user can now specify a comma-separated list + of legal values, + which will get translated into a space-separated + list for passing to the any build commands: + + + + + scons -Q COLORS=red,blue foo.o + scons -Q COLORS=blue,green,red foo.o + + + + + In addition, the &ListVariable; function + allows the user to specify explicit keywords of + &all; or &none; + to select all of the legal values, + or none of them, respectively: + + + + + scons -Q COLORS=all foo.o + scons -Q COLORS=none foo.o + + + + + And, of course, an illegal value + still generates an error message: + + + + + scons -Q COLORS=magenta foo.o + + +
+ +
+ Path Names: the &PathVariable; Build Variable Function + + + + &SCons; supports a &PathVariable; function + to make it easy to create a build variable + to control an expected path name. + If, for example, you need to + define a variable in the preprocessor + that controls the location of a + configuration file: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '__ROOT__/etc/my_config')) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + /opt/location + + + + + + This then allows the user to + override the &CONFIG; build variable + on the command line as necessary: + + + + + scons -Q foo.o + scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o + + + + + By default, &PathVariable; checks to make sure + that the specified path exists and generates an error if it + doesn't: + + + + + scons -Q CONFIG=__ROOT__/does/not/exist foo.o + + + + + &PathVariable; provides a number of methods + that you can use to change this behavior. + If you want to ensure that any specified paths are, + in fact, files and not directories, + use the &PathVariable_PathIsFile; method: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '__ROOT__/etc/my_config', + PathVariable.PathIsFile)) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + + + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathVariable_PathIsDir; method: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '__ROOT__/var/my_dbdir', + PathVariable.PathIsDir)) + env = Environment(variables = vars, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + + + + If you want to make sure that any specified paths + are directories, + and you would like the directory created + if it doesn't already exist, + use the &PathVariable_PathIsDirCreate; method: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '__ROOT__/var/my_dbdir', + PathVariable.PathIsDirCreate)) + env = Environment(variables = vars, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + + + + Lastly, if you don't care whether the path exists, + is a file, or a directory, + use the &PathVariable_PathAccept; method + to accept any path that the user supplies: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('OUTPUT', + 'Path to output file or directory', + None, + PathVariable.PathAccept)) + env = Environment(variables = vars, + CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) + env.Program('foo.c') + + + foo.c + + + +
+ +
+ Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function + + + + Sometimes you want to give users + even more control over a path name variable, + allowing them to explicitly enable or + disable the path name + by using yes or no keywords, + in addition to allow them + to supply an explicit path name. + &SCons; supports the &PackageVariable; + function to support this: + + + + + + vars = Variables('custom.py') + vars.Add(PackageVariable('PACKAGE', + 'Location package', + '__ROOT__/opt/location')) + env = Environment(variables = vars, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + /opt/location + + + + + + When the &SConscript; file uses the &PackageVariable; funciton, + user can now still use the default + or supply an overriding path name, + but can now explicitly set the + specified variable to a value + that indicates the package should be enabled + (in which case the default should be used) + or disabled: + + + + + scons -Q foo.o + scons -Q PACKAGE=__ROOT__/usr/local/location foo.o + scons -Q PACKAGE=yes foo.o + scons -Q PACKAGE=no foo.o + + +
+ +
+ +
+ Adding Multiple Command-Line Build Variables at Once + + + + Lastly, &SCons; provides a way to add + multiple build variables to a &Variables; object at once. + Instead of having to call the &Add; method + multiple times, + you can call the &AddVariables; + method with a list of build variables + to be added to the object. + Each build variable is specified + as either a tuple of arguments, + just like you'd pass to the &Add; method itself, + or as a call to one of the pre-defined + functions for pre-packaged command-line build variables. + in any order: + + + + + + vars = Variables() + vars.AddVariables( + ('RELEASE', 'Set to 1 to build for release', 0), + ('CONFIG', 'Configuration file', '/etc/my_config'), + BoolVariable('warnings', 'compilation with -Wall and similiar', 1), + EnumVariable('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=0), # case sensitive + ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + names = list_of_libs), + PackageVariable('x11', + 'use X11 installed here (yes = search some places)', + 'yes'), + PathVariable('qtdir', 'where the root of Qt is installed', qtdir), + ) + + + + + + +
+ +
+ Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function + + + + Users may, of course, + occasionally misspell variable names in their command-line settings. + &SCons; does not generate an error or warning + for any unknown variables the users specifies on the command line. + (This is in no small part because you may be + processing the arguments directly using the &ARGUMENTS; dictionary, + and therefore &SCons; can't know in the general case + whether a given "misspelled" variable is + really unknown and a potential problem, + or something that your &SConscript; file + will handle directly with some Python code.) + + + + + + If, however, you're using a &Variables; object to + define a specific set of command-line build variables + that you expect users to be able to set, + you may want to provide an error + message or warning of your own + if the user supplies a variable setting + that is not among + the defined list of variable names known to the &Variables; object. + You can do this by calling the &UnknownVariables; + method of the &Variables; object: + + + + + + vars = Variables(None) + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + unknown = vars.UnknownVariables() + if unknown: + print "Unknown variables:", unknown.keys() + Exit(1) + env.Program('foo.c') + + + foo.c + + + + + + The &UnknownVariables; method returns a dictionary + containing the keywords and values + of any variables the user specified on the command line + that are not + among the variables known to the &Variables; object + (from having been specified using + the &Variables; object's&Add; method). + In the examble above, + we check for whether the dictionary + returned by the &UnknownVariables; is non-empty, + and if so print the Python list + containing the names of the unknwown variables + and then call the &Exit; function + to terminate &SCons;: + + + + + scons -Q NOT_KNOWN=foo + + + + + Of course, you can process the items in the + dictionary returned by the &UnknownVariables; function + in any way appropriate to your build configuration, + including just printing a warning message + but not exiting, + logging an error somewhere, + etc. + + + + + + Note that you must delay the call of &UnknownVariables; + until after you have applied the &Variables; object + to a construction environment + with the variables= + keyword argument of an &Environment; call. + + + +
+ +
+ +
+ Command-Line Targets + +
+ Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable + + + + &SCons; supports a &COMMAND_LINE_TARGETS; variable + that lets you fetch the list of targets that the + user specified on the command line. + You can use the targets to manipulate the + build in any way you wish. + As a simple example, + suppose that you want to print a reminder + to the user whenever a specific program is built. + You can do this by checking for the + target in the &COMMAND_LINE_TARGETS; list: + + + + + + if 'bar' in COMMAND_LINE_TARGETS: + print "Don't forget to copy `bar' to the archive!" + Default(Program('foo.c')) + Program('bar.c') + + + foo.c + + + foo.c + + + + + + Then, running &SCons; with the default target + works as it always does, + but explicity specifying the &bar; target + on the command line generates the warning message: + + + + + scons -Q + scons -Q bar + + + + + Another practical use for the &COMMAND_LINE_TARGETS; variable + might be to speed up a build + by only reading certain subsidiary &SConscript; + files if a specific target is requested. + + + +
+ +
+ Controlling the Default Targets: the &Default; Function + + + + One of the most basic things you can control + is which targets &SCons; will build by default--that is, + when there are no targets specified on the command line. + As mentioned previously, + &SCons; will normally build every target + in or below the current directory + by default--that is, when you don't + explicitly specify one or more targets + on the command line. + Sometimes, however, you may want + to specify explicitly that only + certain programs, or programs in certain directories, + should be built by default. + You do this with the &Default; function: + + + + + + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + + + hello.c + + + goodbye.c + + + + + + This &SConstruct; file knows how to build two programs, + &hello; and &goodbye;, + but only builds the + &hello; program by default: + + + + + scons -Q + scons -Q + scons -Q goodbye + + + + + Note that, even when you use the &Default; + function in your &SConstruct; file, + you can still explicitly specify the current directory + (.) on the command line + to tell &SCons; to build + everything in (or below) the current directory: + + + + + scons -Q . + + + + + You can also call the &Default; + function more than once, + in which case each call + adds to the list of targets to be built by default: + + + + + + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) + + + prog1.c + + + prog2.c + + + prog3.c + + + + + + Or you can specify more than one target + in a single call to the &Default; function: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + + + + + Either of these last two examples + will build only the + prog1 + and + prog3 + programs by default: + + + + + scons -Q + scons -Q . + + + + + You can list a directory as + an argument to &Default;: + + + + + + env = Environment() + env.Program(['prog1/main.c', 'prog1/foo.c']) + env.Program(['prog2/main.c', 'prog2/bar.c']) + Default('prog1') + + + + + int main() { printf("prog1/main.c\n"); } + + + int foo() { printf("prog1/foo.c\n"); } + + + int main() { printf("prog2/main.c\n"); } + + + int bar() { printf("prog2/bar.c\n"); } + + + + + + In which case only the target(s) in that + directory will be built by default: + + + + + scons -Q + scons -Q + scons -Q . + + + + + Lastly, if for some reason you don't want + any targets built by default, + you can use the Python None + variable: + + + + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) + + + prog1.c + + + prog2.c + + + + + + Which would produce build output like: + + + + + scons -Q + scons -Q . + + +
+ Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable + + + + &SCons; supports a &DEFAULT_TARGETS; variable + that lets you get at the current list of default targets. + The &DEFAULT_TARGETS variable has + two important differences from the &COMMAND_LINE_TARGETS; variable. + First, the &DEFAULT_TARGETS; variable is a list of + internal &SCons; nodes, + so you need to convert the list elements to strings + if you want to print them or look for a specific target name. + Fortunately, you can do this easily + by using the Python map function + to run the list through str: + + + + + + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) + + + prog1.c + + + + + + (Keep in mind that all of the manipulation of the + &DEFAULT_TARGETS; list takes place during the + first phase when &SCons; is reading up the &SConscript; files, + which is obvious if + we leave off the -Q flag when we run &SCons;:) + + + + + scons + + + + + Second, + the contents of the &DEFAULT_TARGETS; list change + in response to calls to the &Default: function, + as you can see from the following &SConstruct; file: + + + + + + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) + prog2 = Program('prog2.c') + Default(prog2) + print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) + + + prog1.c + + + prog2.c + + + + + + Which yields the output: + + + + + scons + + + + + In practice, this simply means that you + need to pay attention to the order in + which you call the &Default; function + and refer to the &DEFAULT_TARGETS; list, + to make sure that you don't examine the + list before you've added the default targets + you expect to find in it. + + + +
+ +
+ +
+ Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable + + + + We've already been introduced to the + &COMMAND_LINE_TARGETS; variable, + which contains a list of targets specified on the command line, + and the &DEFAULT_TARGETS; variable, + which contains a list of targets specified + via calls to the &Default; method or function. + Sometimes, however, + you want a list of whatever targets + &SCons; will try to build, + regardless of whether the targets came from the + command line or a &Default; call. + You could code this up by hand, as follows: + + + + + if COMMAND_LINE_TARGETS: + targets = COMMAND_LINE_TARGETS + else: + targets = DEFAULT_TARGETS + + + + + &SCons;, however, provides a convenient + &BUILD_TARGETS; variable + that eliminates the need for this by-hand manipulation. + Essentially, the &BUILD_TARGETS; variable + contains a list of the command-line targets, + if any were specified, + and if no command-line targets were specified, + it contains a list of the targets specified + via the &Default; method or function. + + + + + + Because &BUILD_TARGETS; may contain a list of &SCons; nodes, + you must convert the list elements to strings + if you want to print them or look for a specific target name, + just like the &DEFAULT_TARGETS; list: + + + + + + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) + + + prog1.c + + + prog2.c + + + + + + Notice how the value of &BUILD_TARGETS; + changes depending on whether a target is + specified on the command line: + + + + + scons -Q + scons -Q prog2 + scons -Q -c . + + +
+ +
diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml new file mode 100644 index 0000000..630a9b2 --- /dev/null +++ b/doc/user/command-line.xml @@ -0,0 +1,2243 @@ + + + + + &SCons; provides a number of ways + for the writer of the &SConscript; files + to give the users who will run &SCons; + a great deal of control over the build execution. + The arguments that the user can specify on + the command line are broken down into three types: + + + + + + + Options + + + + + Command-line options always begin with + one or two - (hyphen) characters. + &SCons; provides ways for you to examine + and set options values from within your &SConscript; files, + as well as the ability to define your own + custom options. + See , below. + + + + + + + Variables + + + + + Any command-line argument containing an = + (equal sign) is considered a variable setting with the form + variable=value + &SCons; provides direct access to + all of the command-line variable settings, + the ability to apply command-line variable settings + to construction environments, + and functions for configuring + specific types of variables + (Boolean values, path names, etc.) + with automatic validation of the user's specified values. + See , below. + + + + + + + Targets + + + + + Any command-line argument that is not an option + or a variable setting + (does not begin with a hyphen + and does not contain an equal sign) + is considered a target that the user + (presumably) wants &SCons; to build. + A list of Node objects representing + the target or targets to build. + &SCons; provides access to the list of specified targets, + as well as ways to set the default list of targets + from within the &SConscript; files. + See , below. + + + + + + + +
+ Command-Line Options + + + + &SCons; has many command-line options + that control its behavior. + A &SCons; command-line option + always begins with one or two - (hyphen) + characters. + + + +
+ Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable + + + + Users may find themselves supplying + the same command-line options every time + they run &SCons;. + For example, you might find it saves time + to specify a value of -j 2 + to have &SCons; run up to two build commands in parallel. + To avoid having to type -j 2 by hand + every time, + you can set the external environment variable + &SCONSFLAGS; to a string containing + command-line options that you want &SCons; to use. + + + + + + If, for example, + you're using a POSIX shell that's + compatible with the Bourne shell, + and you always want &SCons; to use the + -Q option, + you can set the &SCONSFLAGS; + environment as follows: + + + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + ... [build output] ... + scons: done building targets. + % export SCONSFLAGS="-Q" + % scons + ... [build output] ... + + + + + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: + + + + + $ setenv SCONSFLAGS "-Q" + + + + + Windows users may typically want to set the + &SCONSFLAGS; in the appropriate tab of the + System Properties window. + + + +
+ +
+ Getting Values Set by Command-Line Options: the &GetOption; Function + + + + &SCons; provides the &GetOption; function + to get the values set by the various command-line options. + One common use of this is to check whether or not + the -h or --help option + has been specified. + Normally, &SCons; does not print its help text + until after it has read all of the &SConscript; files, + because it's possible that help text has been added + by some subsidiary &SConscript; file deep in the + source tree hierarchy. + Of course, reading all of the &SConscript; files + takes extra time. + + + + + + If you know that your configuration does not define + any additional help text in subsidiary &SConscript; files, + you can speed up the command-line help available to users + by using the &GetOption; function to load the + subsidiary &SConscript; files only if the + the user has not specified + the -h or --help option, + like so: + + + + + + + + In general, the string that you pass to the + &GetOption; function to fetch the value of a command-line + option setting is the same as the "most common" long option name + (beginning with two hyphen characters), + although there are some exceptions. + The list of &SCons; command-line options + and the &GetOption; strings for fetching them, + are available in the + section, + below. + + + +
+ +
+ Setting Values of Command-Line Options: the &SetOption; Function + + + + You can also set the values of &SCons; + command-line options from within the &SConscript; files + by using the &SetOption; function. + The strings that you use to set the values of &SCons; + command-line options are available in the + section, + below. + + + + + + One use of the &SetOption; function is to + specify a value for the -j + or --jobs option, + so that users get the improved performance + of a parallel build without having to specify the option by hand. + A complicating factor is that a good value + for the -j option is + somewhat system-dependent. + One rough guideline is that the more processors + your system has, + the higher you want to set the + -j value, + in order to take advantage of the number of CPUs. + + + + + + For example, suppose the administrators + of your development systems + have standardized on setting a + NUM_CPU environment variable + to the number of processors on each system. + A little bit of Python code + to access the environment variable + and the &SetOption; function + provide the right level of flexibility: + + + + + import os + num_cpu = int(os.environ.get('NUM_CPU', 2)) + SetOption('num_jobs', num_cpu) + print "running with -j", GetOption('num_jobs') + + + + + The above snippet of code + sets the value of the --jobs option + to the value specified in the + $NUM_CPU environment variable. + (This is one of the exception cases + where the string is spelled differently from + the from command-line option. + The string for fetching or setting the --jobs + value is num_jobs + for historical reasons.) + The code in this example prints the num_jobs + value for illustrative purposes. + It uses a default value of 2 + to provide some minimal parallelism even on + single-processor systems: + + + + + % scons -Q + running with -j 2 + scons: `.' is up to date. + + + + + But if the $NUM_CPU + environment variable is set, + then we use that for the default number of jobs: + + + + + % export NUM_CPU="4" + % scons -Q + running with -j 4 + scons: `.' is up to date. + + + + + But any explicit + -j or --jobs + value the user specifies an the command line is used first, + regardless of whether or not + the $NUM_CPU environment + variable is set: + + + + + % scons -Q -j 7 + running with -j 7 + scons: `.' is up to date. + % export NUM_CPU="4" + % scons -Q -j 3 + running with -j 3 + scons: `.' is up to date. + + +
+ +
+ Strings for Getting or Setting Values of &SCons; Command-Line Options + + + + The strings that you can pass to the &GetOption; + and &SetOption; functions usually correspond to the + first long-form option name + (beginning with two hyphen characters: --), + after replacing any remaining hyphen characters + with underscores. + + + + + + The full list of strings and the variables they + correspond to is as follows: + + + + + + + + + + String for &GetOption; and &SetOption; + Command-Line Option(s) + + + + + + + + cache_debug + + + + + cache_disable + + + + + cache_force + + + + + cache_show + + + + + clean + , + , + + + + + config + + + + + directory + , + + + + + diskcheck + + + + + duplicate + + + + + file + , + , + , + + + + + help + , + + + + + ignore_errors + + + + + implicit_cache + + + + + implicit_deps_changed + + + + + implicit_deps_unchanged + + + + + interactive + , + + + + + keep_going + , + + + + + max_drift + + + + + no_exec + , + , + , + , + + + + + no_site_dir + + + + + num_jobs + , + + + + + profile_file + + + + + question + , + + + + + random + + + + + repository + , + , + + + + + silent + , + , + + + + + site_dir + + + + + stack_size + + + + + taskmastertrace_file + + + + + warn + + + + + + + + +
+ +
+ Adding Custom Command-Line Options: the &AddOption; Function + + + + &SCons; also allows you to define your own + command-line options with the &AddOption; function. + The &AddOption; function takes the same arguments + as the optparse.add_option function + from the standard Python library. + + + The &AddOption; function is, + in fact, implemented using a subclass + of the optparse.OptionParser. + + + Once you have added a custom command-line option + with the &AddOption; function, + the value of the option (if any) is immediately available + using the standard &GetOption; function. + (The value can also be set using &SetOption;, + although that's not very useful in practice + because a default value can be specified in + directly in the &AddOption; call.) + + + + + + One useful example of using this functionality + is to provide a for users: + + + + + AddOption('--prefix', + dest='prefix', + type='string', + nargs=1, + action='store', + metavar='DIR', + help='installation prefix') + + env = Environment(PREFIX = GetOption('prefix')) + + installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in') + Default(installed_foo) + + + + + The above code uses the &GetOption; function + to set the $PREFIX + construction variable to any + value that the user specifies with a command-line + option of --prefix. + Because $PREFIX + will expand to a null string if it's not initialized, + running &SCons; without the + option of --prefix + will install the file in the + /usr/bin/ directory: + + + + + % scons -Q -n + Install file: "foo.in" as "/usr/bin/foo.in" + + + + + But specifying --prefix=/tmp/install + on the command line causes the file to be installed in the + /tmp/install/usr/bin/ directory: + + + + + % scons -Q -n --prefix=/tmp/install + Install file: "foo.in" as "/tmp/install/usr/bin/foo.in" + + +
+ +
+ +
+ Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables + + + + You may want to control various aspects + of your build by allowing the user + to specify variable=value + values on the command line. + For example, suppose you + want users to be able to + build a debug version of a program + by running &SCons; as follows: + + + + + % scons -Q debug=1 + + + + + &SCons; provides an &ARGUMENTS; dictionary + that stores all of the + variable=value + assignments from the command line. + This allows you to modify + aspects of your build in response + to specifications on the command line. + (Note that unless you want to require + that users always + specify a variable, + you probably want to use + the Python + ARGUMENTS.get() function, + which allows you to specify a default value + to be used if there is no specification + on the command line.) + + + + + + The following code sets the &cv-link-CCFLAGS; construction + variable in response to the debug + flag being set in the &ARGUMENTS; dictionary: + + + + + env = Environment() + debug = ARGUMENTS.get('debug', 0) + if int(debug): + env.Append(CCFLAGS = '-g') + env.Program('prog.c') + + + + + This results in the -g + compiler option being used when + debug=1 + is used on the command line: + + + + + % scons -Q debug=0 + cc -o prog.o -c prog.c + cc -o prog prog.o + % scons -Q debug=0 + scons: `.' is up to date. + % scons -Q debug=1 + scons: `.' is up to date. + % scons -Q debug=1 + scons: `.' is up to date. + + + + + Notice that &SCons; keeps track of + the last values used to build the object files, + and as a result correctly rebuilds + the object and executable files + only when the value of the debug + argument has changed. + + + + + + The &ARGUMENTS; dictionary has two minor drawbacks. + First, because it is a dictionary, + it can only store one value for each specified keyword, + and thus only "remembers" the last setting + for each keyword on the command line. + This makes the &ARGUMENTS; dictionary + inappropriate if users should be able to + specify multiple values + on the command line for a given keyword. + Second, it does not preserve + the order in which the variable settings + were specified, + which is a problem if + you want the configuration to + behave differently in response + to the order in which the build + variable settings were specified on the command line. + + + + + + To accomodate these requirements, + &SCons; provides an &ARGLIST; variable + that gives you direct access to + variable=value + settings on the command line, + in the exact order they were specified, + and without removing any duplicate settings. + Each element in the &ARGLIST; variable + is itself a two-element list + containing the keyword and the value + of the setting, + and you must loop through, + or otherwise select from, + the elements of &ARGLIST; to + process the specific settings you want + in whatever way is appropriate for your configuration. + For example, + the following code to let the user + add to the &CPPDEFINES; construction variable + by specifying multiple + define= + settings on the command line: + + + + + cppdefines = [] + for key, value in ARGLIST: + if key == 'define': + cppdefines.append(value) + env = Environment(CPPDEFINES = cppdefines) + env.Object('prog.c') + + + + + Yields the following output: + + + + + % scons -Q define=FOO + cc -o prog.o -c -DFOO prog.c + % scons -Q define=FOO define=BAR + scons: `.' is up to date. + + + + + Note that the &ARGLIST; and &ARGUMENTS; + variables do not interfere with each other, + but merely provide slightly different views + into how the user specified + variable=value + settings on the command line. + You can use both variables in the same + &SCons; configuration. + In general, the &ARGUMENTS; dictionary + is more convenient to use, + (since you can just fetch variable + settings through a dictionary access), + and the &ARGLIST; list + is more flexible + (since you can examine the + specific order in which + the user's command-line variabe settings). + + + +
+ Controlling Command-Line Build Variables + + + + Being able to use a command-line build variable like + debug=1 is handy, + but it can be a chore to write specific Python code + to recognize each such variable, + check for errors and provide appropriate messages, + and apply the values to a construction variable. + To help with this, + &SCons; supports a class to + define such build variables easily, + and a mechanism to apply the + build variables to a construction environment. + This allows you to control how the build variables affect + construction environments. + + + + + + For example, suppose that you want users to set + a &RELEASE; construction variable on the + command line whenever the time comes to build + a program for release, + and that the value of this variable + should be added to the command line + with the appropriate -D option + (or other command line option) + to pass the value to the C compiler. + Here's how you might do that by setting + the appropriate value in a dictionary for the + &cv-link-CPPDEFINES; construction variable: + + + + + vars = Variables() + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + + + + + This &SConstruct; file first creates a &Variables; object + (the vars = Variables() call), + and then uses the object's &Add; + method to indicate that the &RELEASE; + variable can be set on the command line, + and that its default value will be 0 + (the third argument to the &Add; method). + The second argument is a line of help text; + we'll learn how to use it in the next section. + + + + + + We then pass the created &Variables; + object as a &variables; keyword argument + to the &Environment; call + used to create the construction environment. + This then allows a user to set the + &RELEASE; build variable on the command line + and have the variable show up in + the command line used to build each object from + a C source file: + + + + + % scons -Q RELEASE=1 + cc -o bar.o -c -DRELEASE_BUILD=1 bar.c + cc -o foo.o -c -DRELEASE_BUILD=1 foo.c + cc -o foo foo.o bar.o + + + + + NOTE: Before &SCons; release 0.98.1, these build variables + were known as "command-line build options." + The class was actually named the &Options; class, + and in the sections below, + the various functions were named + &BoolOption;, &EnumOption;, &ListOption;, + &PathOption;, &PackageOption; and &AddOptions;. + These older names still work, + and you may encounter them in older + &SConscript; fles, + but their use is discouraged + and will be officially deprecated some day. + + + +
+ +
+ Providing Help for Command-Line Build Variables + + + + To make command-line build variables most useful, + you ideally want to provide + some help text that will describe + the available variables + when the user runs scons -h. + You could write this text by hand, + but &SCons; provides an easier way. + &Variables; objects support a + &GenerateHelpText; method + that will, as its name suggests, + generate text that describes + the various variables that + have been added to it. + You then pass the output from this method to + the &Help; function: + + + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars) + Help(vars.GenerateHelpText(env)) + + + + + &SCons; will now display some useful text + when the -h option is used: + + + + + % scons -Q -h + + RELEASE: Set to 1 to build for release + default: 0 + actual: 0 + + Use scons -H for help about command-line options. + + + + + Notice that the help output shows the default value, + and the current actual value of the build variable. + + + +
+ +
+ Reading Build Variables From a File + + + + Giving the user a way to specify the + value of a build variable on the command line + is useful, + but can still be tedious + if users must specify the variable + every time they run &SCons;. + We can let users provide customized build variable settings + in a local file by providing a + file name when we create the + &Variables; object: + + + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + Help(vars.GenerateHelpText(env)) + + + + + This then allows the user to control the &RELEASE; + variable by setting it in the &custom_py; file: + + + + + RELEASE = 1 + + + + + Note that this file is actually executed + like a Python script. + Now when we run &SCons;: + + + + + % scons -Q + cc -o bar.o -c -DRELEASE_BUILD=1 bar.c + cc -o foo.o -c -DRELEASE_BUILD=1 foo.c + cc -o foo foo.o bar.o + + + + + And if we change the contents of &custom_py; to: + + + + + RELEASE = 0 + + + + + The object files are rebuilt appropriately + with the new variable: + + + + + % scons -Q + cc -o bar.o -c -DRELEASE_BUILD=0 bar.c + cc -o foo.o -c -DRELEASE_BUILD=0 foo.c + cc -o foo foo.o bar.o + + +
+ +
+ Pre-Defined Build Variable Functions + + + + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build variables. + + + +
+ True/False Values: the &BoolVariable; Build Variable Function + + + + It's often handy to be able to specify a + variable that controls a simple Boolean variable + with a &true; or &false; value. + It would be even more handy to accomodate + users who have different preferences for how to represent + &true; or &false; values. + The &BoolVariable; function + makes it easy to accomodate these + common representations of + &true; or &false;. + + + + + + The &BoolVariable; function takes three arguments: + the name of the build variable, + the default value of the build variable, + and the help string for the variable. + It then returns appropriate information for + passing to the &Add; method of a &Variables; object, like so: + + + + + vars = Variables('custom.py') + vars.Add(BoolVariable('RELEASE', 'Set to build for release', 0)) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program('foo.c') + + + + + With this build variable, + the &RELEASE; variable can now be enabled by + setting it to the value yes + or t: + + + + + % scons -Q RELEASE=yes foo.o + cc -o foo.o -c -DRELEASE_BUILD=True foo.c + + + + % scons -Q RELEASE=t foo.o + cc -o foo.o -c -DRELEASE_BUILD=True foo.c + + + + + Other values that equate to &true; include + y, + 1, + on + and + all. + + + + + + Conversely, &RELEASE; may now be given a &false; + value by setting it to + no + or + f: + + + + + % scons -Q RELEASE=no foo.o + cc -o foo.o -c -DRELEASE_BUILD=False foo.c + + + + % scons -Q RELEASE=f foo.o + cc -o foo.o -c -DRELEASE_BUILD=False foo.c + + + + + Other values that equate to &false; include + n, + 0, + off + and + none. + + + + + + Lastly, if a user tries to specify + any other value, + &SCons; supplies an appropriate error message: + + + + + % scons -Q RELEASE=bad_value foo.o + + scons: *** Error converting option: RELEASE + Invalid value for boolean option: bad_value + File "/home/my/project/SConstruct", line 4, in ? + + +
+ +
+ Single Value From a List: the &EnumVariable; Build Variable Function + + + + Suppose that we want a user to be able to + set a &COLOR; variable + that selects a background color to be + displayed by an application, + but that we want to restrict the + choices to a specific set of allowed colors. + This can be set up quite easily + using the &EnumVariable;, + which takes a list of &allowed_values + in addition to the variable name, + default value, + and help text arguments: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'))) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + The user can now explicity set the &COLOR; build variable + to any of the specified allowed values: + + + + + % scons -Q COLOR=red foo.o + cc -o foo.o -c -DCOLOR="red" foo.c + % scons -Q COLOR=blue foo.o + scons: `foo.o' is up to date. + % scons -Q COLOR=green foo.o + scons: `foo.o' is up to date. + + + + + But, almost more importantly, + an attempt to set &COLOR; + to a value that's not in the list + generates an error message: + + + + + % scons -Q COLOR=magenta foo.o + + scons: *** Invalid value for option COLOR: magenta + File "/home/my/project/SConstruct", line 5, in ? + + + + + The &EnumVariable; function also supports a way + to map alternate names to allowed values. + Suppose, for example, + that we want to allow the user + to use the word navy as a synonym for + blue. + We do this by adding a ↦ dictionary + that will map its key values + to the desired legal value: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'})) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + As desired, the user can then use + navy on the command line, + and &SCons; will translate it into blue + when it comes time to use the &COLOR; + variable to build a target: + + + + + % scons -Q COLOR=navy foo.o + cc -o foo.o -c -DCOLOR="blue" foo.c + + + + + By default, when using the &EnumVariable; function, + arguments that differ + from the legal values + only in case + are treated as illegal values: + + + + + % scons -Q COLOR=Red foo.o + + scons: *** Invalid value for option COLOR: Red + File "/home/my/project/SConstruct", line 5, in ? + % scons -Q COLOR=BLUE foo.o + + scons: *** Invalid value for option COLOR: BLUE + File "/home/my/project/SConstruct", line 5, in ? + % scons -Q COLOR=nAvY foo.o + + scons: *** Invalid value for option COLOR: nAvY + File "/home/my/project/SConstruct", line 5, in ? + + + + + The &EnumVariable; function can take an additional + &ignorecase; keyword argument that, + when set to 1, + tells &SCons; to allow case differences + when the values are specified: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=1)) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + Which yields the output: + + + + + % scons -Q COLOR=Red foo.o + cc -o foo.o -c -DCOLOR="Red" foo.c + % scons -Q COLOR=BLUE foo.o + scons: `foo.o' is up to date. + % scons -Q COLOR=nAvY foo.o + scons: `foo.o' is up to date. + % scons -Q COLOR=green foo.o + scons: `foo.o' is up to date. + + + + + Notice that an &ignorecase; value of 1 + preserves the case-spelling that the user supplied. + If you want &SCons; to translate the names + into lower-case, + regardless of the case used by the user, + specify an &ignorecase; value of 2: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=2)) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + Now &SCons; will use values of + red, + green or + blue + regardless of how the user spells + those values on the command line: + + + + + % scons -Q COLOR=Red foo.o + cc -o foo.o -c -DCOLOR="red" foo.c + % scons -Q COLOR=nAvY foo.o + scons: `foo.o' is up to date. + % scons -Q COLOR=GREEN foo.o + scons: `foo.o' is up to date. + + +
+ +
+ Multiple Values From a List: the &ListVariable; Build Variable Function + + + + Another way in which you might want to allow users + to control a build variable is to + specify a list of one or more legal values. + &SCons; supports this through the &ListVariable; function. + If, for example, we want a user to be able to set a + &COLORS; variable to one or more of the legal list of values: + + + + + vars = Variables('custom.py') + vars.Add(ListVariable('COLORS', 'List of colors', 0, + ['red', 'green', 'blue'])) + env = Environment(variables = vars, + CPPDEFINES={'COLORS' : '"${COLORS}"'}) + env.Program('foo.c') + + + + + A user can now specify a comma-separated list + of legal values, + which will get translated into a space-separated + list for passing to the any build commands: + + + + + % scons -Q COLORS=red,blue foo.o + cc -o foo.o -c -DCOLORS="red blue" foo.c + % scons -Q COLORS=blue,green,red foo.o + scons: `foo.o' is up to date. + + + + + In addition, the &ListVariable; function + allows the user to specify explicit keywords of + &all; or &none; + to select all of the legal values, + or none of them, respectively: + + + + + % scons -Q COLORS=all foo.o + cc -o foo.o -c -DCOLORS="red green blue" foo.c + % scons -Q COLORS=none foo.o + scons: `foo.o' is up to date. + + + + + And, of course, an illegal value + still generates an error message: + + + + + % scons -Q COLORS=magenta foo.o + + scons: *** Error converting option: COLORS + Invalid value(s) for option: magenta + File "/home/my/project/SConstruct", line 5, in ? + + +
+ +
+ Path Names: the &PathVariable; Build Variable Function + + + + &SCons; supports a &PathVariable; function + to make it easy to create a build variable + to control an expected path name. + If, for example, you need to + define a variable in the preprocessor + that controls the location of a + configuration file: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '/etc/my_config')) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + + + This then allows the user to + override the &CONFIG; build variable + on the command line as necessary: + + + + + % scons -Q foo.o + cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c + % scons -Q CONFIG=/usr/local/etc/other_config foo.o + scons: `foo.o' is up to date. + + + + + By default, &PathVariable; checks to make sure + that the specified path exists and generates an error if it + doesn't: + + + + + % scons -Q CONFIG=/does/not/exist foo.o + + scons: *** Path for option CONFIG does not exist: /does/not/exist + File "/home/my/project/SConstruct", line 6, in ? + + + + + &PathVariable; provides a number of methods + that you can use to change this behavior. + If you want to ensure that any specified paths are, + in fact, files and not directories, + use the &PathVariable_PathIsFile; method: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '/etc/my_config', + PathVariable.PathIsFile)) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathVariable_PathIsDir; method: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '/var/my_dbdir', + PathVariable.PathIsDir)) + env = Environment(variables = vars, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + + + If you want to make sure that any specified paths + are directories, + and you would like the directory created + if it doesn't already exist, + use the &PathVariable_PathIsDirCreate; method: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '/var/my_dbdir', + PathVariable.PathIsDirCreate)) + env = Environment(variables = vars, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + + + Lastly, if you don't care whether the path exists, + is a file, or a directory, + use the &PathVariable_PathAccept; method + to accept any path that the user supplies: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('OUTPUT', + 'Path to output file or directory', + None, + PathVariable.PathAccept)) + env = Environment(variables = vars, + CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) + env.Program('foo.c') + + +
+ +
+ Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function + + + + Sometimes you want to give users + even more control over a path name variable, + allowing them to explicitly enable or + disable the path name + by using yes or no keywords, + in addition to allow them + to supply an explicit path name. + &SCons; supports the &PackageVariable; + function to support this: + + + + + vars = Variables('custom.py') + vars.Add(PackageVariable('PACKAGE', + 'Location package', + '/opt/location')) + env = Environment(variables = vars, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + + + + + When the &SConscript; file uses the &PackageVariable; funciton, + user can now still use the default + or supply an overriding path name, + but can now explicitly set the + specified variable to a value + that indicates the package should be enabled + (in which case the default should be used) + or disabled: + + + + + % scons -Q foo.o + cc -o foo.o -c -DPACKAGE="/opt/location" foo.c + % scons -Q PACKAGE=/usr/local/location foo.o + scons: `foo.o' is up to date. + % scons -Q PACKAGE=yes foo.o + scons: `foo.o' is up to date. + % scons -Q PACKAGE=no foo.o + scons: `foo.o' is up to date. + + +
+ +
+ +
+ Adding Multiple Command-Line Build Variables at Once + + + + Lastly, &SCons; provides a way to add + multiple build variables to a &Variables; object at once. + Instead of having to call the &Add; method + multiple times, + you can call the &AddVariables; + method with a list of build variables + to be added to the object. + Each build variable is specified + as either a tuple of arguments, + just like you'd pass to the &Add; method itself, + or as a call to one of the pre-defined + functions for pre-packaged command-line build variables. + in any order: + + + + + vars = Variables() + vars.AddVariables( + ('RELEASE', 'Set to 1 to build for release', 0), + ('CONFIG', 'Configuration file', '/etc/my_config'), + BoolVariable('warnings', 'compilation with -Wall and similiar', 1), + EnumVariable('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=0), # case sensitive + ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + names = list_of_libs), + PackageVariable('x11', + 'use X11 installed here (yes = search some places)', + 'yes'), + PathVariable('qtdir', 'where the root of Qt is installed', qtdir), + ) + + + + + +
+ +
+ Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function + + + + Users may, of course, + occasionally misspell variable names in their command-line settings. + &SCons; does not generate an error or warning + for any unknown variables the users specifies on the command line. + (This is in no small part because you may be + processing the arguments directly using the &ARGUMENTS; dictionary, + and therefore &SCons; can't know in the general case + whether a given "misspelled" variable is + really unknown and a potential problem, + or something that your &SConscript; file + will handle directly with some Python code.) + + + + + + If, however, you're using a &Variables; object to + define a specific set of command-line build variables + that you expect users to be able to set, + you may want to provide an error + message or warning of your own + if the user supplies a variable setting + that is not among + the defined list of variable names known to the &Variables; object. + You can do this by calling the &UnknownVariables; + method of the &Variables; object: + + + + + vars = Variables(None) + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + unknown = vars.UnknownVariables() + if unknown: + print "Unknown variables:", unknown.keys() + Exit(1) + env.Program('foo.c') + + + + + The &UnknownVariables; method returns a dictionary + containing the keywords and values + of any variables the user specified on the command line + that are not + among the variables known to the &Variables; object + (from having been specified using + the &Variables; object's&Add; method). + In the examble above, + we check for whether the dictionary + returned by the &UnknownVariables; is non-empty, + and if so print the Python list + containing the names of the unknwown variables + and then call the &Exit; function + to terminate &SCons;: + + + + + % scons -Q NOT_KNOWN=foo + Unknown variables: ['NOT_KNOWN'] + + + + + Of course, you can process the items in the + dictionary returned by the &UnknownVariables; function + in any way appropriate to your build configuration, + including just printing a warning message + but not exiting, + logging an error somewhere, + etc. + + + + + + Note that you must delay the call of &UnknownVariables; + until after you have applied the &Variables; object + to a construction environment + with the variables= + keyword argument of an &Environment; call. + + + +
+ +
+ +
+ Command-Line Targets + +
+ Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable + + + + &SCons; supports a &COMMAND_LINE_TARGETS; variable + that lets you fetch the list of targets that the + user specified on the command line. + You can use the targets to manipulate the + build in any way you wish. + As a simple example, + suppose that you want to print a reminder + to the user whenever a specific program is built. + You can do this by checking for the + target in the &COMMAND_LINE_TARGETS; list: + + + + + if 'bar' in COMMAND_LINE_TARGETS: + print "Don't forget to copy `bar' to the archive!" + Default(Program('foo.c')) + Program('bar.c') + + + + + Then, running &SCons; with the default target + works as it always does, + but explicity specifying the &bar; target + on the command line generates the warning message: + + + + + % scons -Q + cc -o foo.o -c foo.c + cc -o foo foo.o + % scons -Q bar + Don't forget to copy `bar' to the archive! + cc -o bar.o -c bar.c + cc -o bar bar.o + + + + + Another practical use for the &COMMAND_LINE_TARGETS; variable + might be to speed up a build + by only reading certain subsidiary &SConscript; + files if a specific target is requested. + + + +
+ +
+ Controlling the Default Targets: the &Default; Function + + + + One of the most basic things you can control + is which targets &SCons; will build by default--that is, + when there are no targets specified on the command line. + As mentioned previously, + &SCons; will normally build every target + in or below the current directory + by default--that is, when you don't + explicitly specify one or more targets + on the command line. + Sometimes, however, you may want + to specify explicitly that only + certain programs, or programs in certain directories, + should be built by default. + You do this with the &Default; function: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + + + + + This &SConstruct; file knows how to build two programs, + &hello; and &goodbye;, + but only builds the + &hello; program by default: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + scons: `hello' is up to date. + % scons -Q goodbye + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + + + + + Note that, even when you use the &Default; + function in your &SConstruct; file, + you can still explicitly specify the current directory + (.) on the command line + to tell &SCons; to build + everything in (or below) the current directory: + + + + + % scons -Q . + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + You can also call the &Default; + function more than once, + in which case each call + adds to the list of targets to be built by default: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) + + + + + Or you can specify more than one target + in a single call to the &Default; function: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + + + + + Either of these last two examples + will build only the + prog1 + and + prog3 + programs by default: + + + + + % scons -Q + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog3.o -c prog3.c + cc -o prog3 prog3.o + % scons -Q . + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + + + + + You can list a directory as + an argument to &Default;: + + + + + env = Environment() + env.Program(['prog1/main.c', 'prog1/foo.c']) + env.Program(['prog2/main.c', 'prog2/bar.c']) + Default('prog1') + + + + + In which case only the target(s) in that + directory will be built by default: + + + + + % scons -Q + cc -o prog1/foo.o -c prog1/foo.c + cc -o prog1/main.o -c prog1/main.c + cc -o prog1/main prog1/main.o prog1/foo.o + % scons -Q + scons: `prog1' is up to date. + % scons -Q . + cc -o prog2/bar.o -c prog2/bar.c + cc -o prog2/main.o -c prog2/main.c + cc -o prog2/main prog2/main.o prog2/bar.o + + + + + Lastly, if for some reason you don't want + any targets built by default, + you can use the Python None + variable: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) + + + + + Which would produce build output like: + + + + + % scons -Q + scons: *** No targets specified and no Default() targets found. Stop. + % scons -Q . + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + + +
+ Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable + + + + &SCons; supports a &DEFAULT_TARGETS; variable + that lets you get at the current list of default targets. + The &DEFAULT_TARGETS variable has + two important differences from the &COMMAND_LINE_TARGETS; variable. + First, the &DEFAULT_TARGETS; variable is a list of + internal &SCons; nodes, + so you need to convert the list elements to strings + if you want to print them or look for a specific target name. + Fortunately, you can do this easily + by using the Python map function + to run the list through str: + + + + + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) + + + + + (Keep in mind that all of the manipulation of the + &DEFAULT_TARGETS; list takes place during the + first phase when &SCons; is reading up the &SConscript; files, + which is obvious if + we leave off the -Q flag when we run &SCons;:) + + + + + % scons + scons: Reading SConscript files ... + DEFAULT_TARGETS is ['prog1'] + scons: done reading SConscript files. + scons: Building targets ... + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + scons: done building targets. + + + + + Second, + the contents of the &DEFAULT_TARGETS; list change + in response to calls to the &Default;: function, + as you can see from the following &SConstruct; file: + + + + + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) + prog2 = Program('prog2.c') + Default(prog2) + print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) + + + + + Which yields the output: + + + + + % scons + scons: Reading SConscript files ... + DEFAULT_TARGETS is now ['prog1'] + DEFAULT_TARGETS is now ['prog1', 'prog2'] + scons: done reading SConscript files. + scons: Building targets ... + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + scons: done building targets. + + + + + In practice, this simply means that you + need to pay attention to the order in + which you call the &Default; function + and refer to the &DEFAULT_TARGETS; list, + to make sure that you don't examine the + list before you've added the default targets + you expect to find in it. + + + +
+ +
+ +
+ Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable + + + + We've already been introduced to the + &COMMAND_LINE_TARGETS; variable, + which contains a list of targets specified on the command line, + and the &DEFAULT_TARGETS; variable, + which contains a list of targets specified + via calls to the &Default; method or function. + Sometimes, however, + you want a list of whatever targets + &SCons; will try to build, + regardless of whether the targets came from the + command line or a &Default; call. + You could code this up by hand, as follows: + + + + + if COMMAND_LINE_TARGETS: + targets = COMMAND_LINE_TARGETS + else: + targets = DEFAULT_TARGETS + + + + + &SCons;, however, provides a convenient + &BUILD_TARGETS; variable + that eliminates the need for this by-hand manipulation. + Essentially, the &BUILD_TARGETS; variable + contains a list of the command-line targets, + if any were specified, + and if no command-line targets were specified, + it contains a list of the targets specified + via the &Default; method or function. + + + + + + Because &BUILD_TARGETS; may contain a list of &SCons; nodes, + you must convert the list elements to strings + if you want to print them or look for a specific target name, + just like the &DEFAULT_TARGETS; list: + + + + + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) + + + + + Notice how the value of &BUILD_TARGETS; + changes depending on whether a target is + specified on the command line: + + + + + % scons -Q + BUILD_TARGETS is ['prog1'] + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + % scons -Q prog2 + BUILD_TARGETS is ['prog2'] + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + % scons -Q -c . + BUILD_TARGETS is ['.'] + Removed prog1.o + Removed prog1 + Removed prog2.o + Removed prog2 + + +
+ +
diff --git a/doc/user/cons.pl b/doc/user/cons.pl new file mode 100644 index 0000000..8afbfec --- /dev/null +++ b/doc/user/cons.pl @@ -0,0 +1,720 @@ +=head1 Introduction + +B is a system for constructing, primarily, software, but is quite +different from previous software construction systems. Cons was designed +from the ground up to deal easily with the construction of software spread +over multiple source directories. Cons makes it easy to create build scripts +that are simple, understandable and maintainable. Cons ensures that complex +software is easily and accurately reproducible. + +Cons uses a number of techniques to accomplish all of this. Construction +scripts are just Perl scripts, making them both easy to comprehend and very +flexible. Global scoping of variables is replaced with an import/export +mechanism for sharing information between scripts, significantly improving +the readability and maintainability of each script. B are introduced: these are Perl objects that capture the +information required for controlling the build process. Multiple +environments are used when different semantics are required for generating +products in the build tree. Cons implements automatic dependency analysis +and uses this to globally sequence the entire build. Variant builds are +easily produced from a single source tree. Intelligent build subsetting is +possible, when working on localized changes. Overrides can be setup to +easily override build instructions without modifying any scripts. MD5 +cryptographic B are associated with derived files, and are used +to accurately determine whether a given file needs to be rebuilt. + +While offering all of the above, and more, Cons remains simple and easy to +use. This will, hopefully, become clear as you read the remainder of this +document. + + + +=head2 Automatic global build sequencing + +Because Cons does full and accurate dependency analysis, and does this +globally, for the entire build, Cons is able to use this information to take +full control of the B of the build. This sequencing is evident +in the above examples, and is equivalent to what you would expect for make, +given a full set of dependencies. With Cons, this extends trivially to +larger, multi-directory builds. As a result, all of the complexity involved +in making sure that a build is organized correctly--including multi-pass +hierarchical builds--is eliminated. We'll discuss this further in the next +sections. + + + +=head1 A Model for sharing files + + +=head2 Some simple conventions + +In any complex software system, a method for sharing build products needs to +be established. We propose a simple set of conventions which are trivial to +implement with Cons, but very effective. + +The basic rule is to require that all build products which need to be shared +between directories are shared via an intermediate directory. We have +typically called this F, and, in a C environment, provided +conventional sub-directories of this directory, such as F, F, +F, etc. + +These directories are defined by the top-level F file. A simple +F file for a B application, organized using +multiple directories, might look like this: + + # Construct file for Hello, World! + + # Where to put all our shared products. + $EXPORT = '#export'; + + Export qw( CONS INCLUDE LIB BIN ); + + # Standard directories for sharing products. + $INCLUDE = "$EXPORT/include"; + $LIB = "$EXPORT/lib"; + $BIN = "$EXPORT/bin"; + + # A standard construction environment. + $CONS = new cons ( + CPPPATH => $INCLUDE, # Include path for C Compilations + LIBPATH => $LIB, # Library path for linking programs + LIBS => '-lworld', # List of standard libraries + ); + + Build qw( + hello/Conscript + world/Conscript + ); + +The F directory's F file looks like this: + + # Conscript file for directory world + Import qw( CONS INCLUDE LIB ); + + # Install the products of this directory + Install $CONS $LIB, 'libworld.a'; + Install $CONS $INCLUDE, 'world.h'; + + # Internal products + Library $CONS 'libworld.a', 'world.c'; + +and the F directory's F file looks like this: + + # Conscript file for directory hello + Import qw( CONS BIN ); + + # Exported products + Install $CONS $BIN, 'hello'; + + # Internal products + Program $CONS 'hello', 'hello.c'; + +To construct a B program with this directory structure, go to +the top-level directory, and invoke C with the appropriate +arguments. In the following example, we tell Cons to build the directory +F. To build a directory, Cons recursively builds all known products +within that directory (only if they need rebuilding, of course). If any of +those products depend upon other products in other directories, then those +will be built, too. + + % cons export + Install world/world.h as export/include/world.h + cc -Iexport/include -c hello/hello.c -o hello/hello.o + cc -Iexport/include -c world/world.c -o world/world.o + ar r world/libworld.a world/world.o + ar: creating world/libworld.a + ranlib world/libworld.a + Install world/libworld.a as export/lib/libworld.a + cc -o hello/hello hello/hello.o -Lexport/lib -lworld + Install hello/hello as export/bin/hello + + +=head2 Clean, understandable, location-independent scripts + +You'll note that the two F files are very clean and +to-the-point. They simply specify products of the directory and how to build +those products. The build instructions are minimal: they specify which +construction environment to use, the name of the product, and the name of +the inputs. Note also that the scripts are location-independent: if you wish +to reorganize your source tree, you are free to do so: you only have to +change the F file (in this example), to specify the new locations +of the F files. The use of an export tree makes this goal easy. + +Note, too, how Cons takes care of little details for you. All the F +directories, for example, were made automatically. And the installed files +were really hard-linked into the respective export directories, to save +space and time. This attention to detail saves considerable work, and makes +it even easier to produce simple, maintainable scripts. + + + +=head1 Signatures + +Cons uses file B to decide if a derived file is out-of-date +and needs rebuilding. In essence, if the contents of a file change, +or the manner in which the file is built changes, the file's signature +changes as well. This allows Cons to decide with certainty when a file +needs rebuilding, because Cons can detect, quickly and reliably, whether +any of its dependency files have been changed. + + +=head2 MD5 content and build signatures + +Cons uses the B (B) algorithm to compute file +signatures. The MD5 algorithm computes a strong cryptographic checksum +for any given input string. Cons can, based on configuration, use two +different MD5 signatures for a given file: + +The B of a file is an MD5 checksum of the file's +contents. Consequently, when the contents of a file change, its content +signature changes as well. + +The B of a file is a combined MD5 checksum of: + +=over 4 + +the signatures of all the input files used to build the file + +the signatures of all dependency files discovered by source scanners +(for example, C<.h> files) + +the signatures of all dependency files specified explicitly via the +C method) + +the command-line string used to build the file + +=back + +The build signature is, in effect, a digest of all the dependency +information for the specified file. Consequently, a file's build +signature changes whenever any part of its dependency information +changes: a new file is added, the contents of a file on which it depends +change, there's a change to the command line used to build the file (or +any of its dependency files), etc. + +For example, in the previous section, the build signature of the +F file will include: + +=over 4 + +the signature of the F file + +the signatures of any header files that Cons detects are included, +directly or indirectly, by F + +the text of the actual command line was used to generate F + +=back + +Similarly, the build signature of the F file will include +all the signatures of its constituents (and hence, transitively, the +signatures of B constituents), as well as the command line that +created the file. + +Note that there is no need for a derived file to depend upon any +particular F or F file. If changes to these files +affect a file, then this will be automatically reflected in its build +signature, since relevant parts of the command line are included in the +signature. Unrelated F or F changes will have no +effect. + + +=head2 Storing signatures in .consign files + +Before Cons exits, it stores the calculated signatures for all of the +files it built or examined in F<.consign> files, one per directory. +Cons uses this stored information on later invocations to decide if +derived files need to be rebuilt. + +After the previous example was compiled, the F<.consign> file in the +F directory looked like this: + + world.h:985533370 - d181712f2fdc07c1f05d97b16bfad904 + world.o:985533372 2a0f71e0766927c0532977b0d2158981 + world.c:985533370 - c712f77189307907f4189b5a7ab62ff3 + libworld.a:985533374 69e568fc5241d7d25be86d581e1fb6aa + +After the file name and colon, the first number is a timestamp of the +file's modification time (on UNIX systems, this is typically the number +of seconds since January 1st, 1970). The second value is the build +signature of the file (or ``-'' in the case of files with no build +signature--that is, source files). The third value, if any, is the +content signature of the file. + + +=head2 Using build signatures to decide when to rebuild files + +When Cons is deciding whether to build or rebuild a derived file, it +first computes the file's current build signature. If the file doesn't +exist, it must obviously be built. + +If, however, the file already exists, Cons next compares the +modification timestamp of the file against the timestamp value in +the F<.consign> file. If the timestamps match, Cons compares the +newly-computed build signature against the build signature in the +F<.consign> file. If the timestamps do not match or the build +signatures do not match, the derived file is rebuilt. + +After the file is built or rebuilt, Cons arranges to store the +newly-computed build signature in the F<.consign> file when it exits. + + +=head2 Signature example + +The use of these signatures is an extremely simple, efficient, and +effective method of improving--dramatically--the reproducibility of a +system. + +We'll demonstrate this with a simple example: + + # Simple "Hello, World!" Construct file + $CFLAGS = '-g' if $ARG{DEBUG} eq 'on'; + $CONS = new cons(CFLAGS => $CFLAGS); + Program $CONS 'hello', 'hello.c'; + +Notice how Cons recompiles at the appropriate times: + + % cons hello + cc -c hello.c -o hello.o + cc -o hello hello.o + % cons hello + cons: "hello" is up-to-date. + % cons DEBUG=on hello + cc -g -c hello.c -o hello.o + cc -o hello hello.o + % cons DEBUG=on hello + cons: "hello" is up-to-date. + % cons hello + cc -c hello.c -o hello.o + cc -o hello hello.o + + +=head2 Source-file signature configuration + +Cons provides a C method that allows you to configure +how the signature should be calculated for any source file when its +signature is being used to decide if a dependent file is up-to-date. +The arguments to the C method consist of one or more +pairs of strings: + + SourceSignature 'auto/*.c' => 'content', + '*' => 'stored-content'; + +The first string in each pair is a pattern to match against derived file +path names. The pattern is a file-globbing pattern, not a Perl regular +expression; the pattern <*.l> will match all Lex source files. The C<*> +wildcard will match across directory separators; the pattern C +would match all C source files in any subdirectory underneath the C +subdirectory. + +The second string in each pair contains one of the following keywords to +specify how signatures should be calculated for source files that match +the pattern. The available keywords are: + +=over 4 + +=item content + +Use the content signature of the source file when calculating signatures +of files that depend on it. This guarantees correct calculation of the +file's signature for all builds, by telling Cons to read the contents of +a source file to calculate its content signature each time it is run. + +=item stored-content + +Use the source file's content signature as stored in the F<.consign> +file, provided the file's timestamp matches the cached timestamp value +in the F<.consign> file. This optimizes performance, with the slight +risk of an incorrect build if a source file's contents have been changed +so quickly after its previous update that the timestamp still matches +the stored timestamp in the F<.consign> file even though the contents +have changed. + +=back + +The Cons default behavior of always calculating a source file's +signature from the file's contents is equivalent to specifying: + + SourceSignature '*' => 'content'; + +The C<*> will match all source files. The C keyword +specifies that Cons will read the contents of a source file to calculate +its signature each time it is run. + +A useful global performance optimization is: + + SourceSignature '*' => 'stored-content'; + +This specifies that Cons will use pre-computed content signatures +from F<.consign> files, when available, rather than re-calculating a +signature from the the source file's contents each time Cons is run. In +practice, this is safe for most build situations, and only a problem +when source files are changed automatically (by scripts, for example). +The Cons default, however, errs on the side of guaranteeing a correct +build in all situations. + +Cons tries to match source file path names against the patterns in the +order they are specified in the C arguments: + + SourceSignature '/usr/repository/objects/*' => 'stored-content', + '/usr/repository/*' => 'content', + '*.y' => 'content', + '*' => 'stored-content'; + +In this example, all source files under the F +directory will use F<.consign> file content signatures, source files +anywhere else underneath F will not use F<.consign> +signature values, all Yacc source files (C<*.y>) anywhere else will not +use F<.consign> signature values, and any other source file will use +F<.consign> signature values. + + +=head2 Derived-file signature configuration + +Cons provides a C construction variable that allows you to +configure how signatures are calculated for any derived file when its +signature is being used to decide if a dependent file is up-to-date. +The value of the C construction variable is a Perl array +reference that holds one or more pairs of strings, like the arguments to +the C method. + +The first string in each pair is a pattern to match against derived file +path names. The pattern is a file-globbing pattern, not a Perl regular +expression; the pattern `*.obj' will match all (Win32) object files. +The C<*> wildcard will match across directory separators; the pattern +`foo/*.a' would match all (UNIX) library archives in any subdirectory +underneath the foo subdirectory. + +The second string in each pair contains one of the following keywords +to specify how signatures should be calculated for derived files that +match the pattern. The available keywords are the same as for the +C method, with an additional keyword: + +=over 4 + +=item build + +Use the build signature of the derived file when calculating signatures +of files that depend on it. This guarantees correct builds by forcing +Cons to rebuild any and all files that depend on the derived file. + +=item content + +Use the content signature of the derived file when calculating signatures +of files that depend on it. This guarantees correct calculation of the +file's signature for all builds, by telling Cons to read the contents of +a derived file to calculate its content signature each time it is run. + +=item stored-content + +Use the derived file's content signature as stored in the F<.consign> +file, provided the file's timestamp matches the cached timestamp value +in the F<.consign> file. This optimizes performance, with the slight +risk of an incorrect build if a derived file's contents have been +changed so quickly after a Cons build that the file's timestamp still +matches the stored timestamp in the F<.consign> file. + +=back + +The Cons default behavior (as previously described) for using +derived-file signatures is equivalent to: + + $env = new cons(SIGNATURE => ['*' => 'build']); + +The C<*> will match all derived files. The C keyword specifies +that all derived files' build signatures will be used when calculating +whether a dependent file is up-to-date. + +A useful alternative default C configuration for many sites: + + $env = new cons(SIGNATURE => ['*' => 'content']); + +In this configuration, derived files have their signatures calculated +from the file contents. This adds slightly to Cons' workload, but has +the useful effect of "stopping" further rebuilds if a derived file is +rebuilt to exactly the same file contents as before, which usually +outweighs the additional computation Cons must perform. + +For example, changing a comment in a C file and recompiling should +generate the exact same object file (assuming the compiler doesn't +insert a timestamp in the object file's header). In that case, +specifying C or C for the signature calculation +will cause Cons to recognize that the object file did not actually +change as a result of being rebuilt, and libraries or programs that +include the object file will not be rebuilt. When C is +specified, however, Cons will only "know" that the object file was +rebuilt, and proceed to rebuild any additional files that include the +object file. + +Note that Cons tries to match derived file path names against the +patterns in the order they are specified in the C array +reference: + + $env = new cons(SIGNATURE => ['foo/*.o' => 'build', + '*.o' => 'content', + '*.a' => 'stored-content', + '*' => 'content']); + +In this example, all object files underneath the F subdirectory +will use build signatures, all other object files (including object +files underneath other subdirectories!) will use F<.consign> file +content signatures, libraries will use F<.consign> file build +signatures, and all other derived files will use content signatures. + + +=head2 Debugging signature calculation + +Cons provides a C<-S> option that can be used to specify what internal +Perl package Cons should use to calculate signatures. The default Cons +behavior is equivalent to specifying C<-S md5> on the command line. + +The only other package (currently) available is an C +package that prints out detailed information about the MD5 signature +calculations performed by Cons: + + % cons -S md5::debug hello + sig::md5::srcsig(hello.c) + => |52d891204c62fe93ecb95281e1571938| + sig::md5::collect(52d891204c62fe93ecb95281e1571938) + => |fb0660af4002c40461a2f01fbb5ffd03| + sig::md5::collect(52d891204c62fe93ecb95281e1571938, + fb0660af4002c40461a2f01fbb5ffd03, + cc -c %< -o %>) + => |f7128da6c3fe3c377dc22ade70647b39| + sig::md5::current(|| + eq |f7128da6c3fe3c377dc22ade70647b39|) + cc -c hello.c -o hello.o + sig::md5::collect() + => |d41d8cd98f00b204e9800998ecf8427e| + sig::md5::collect(f7128da6c3fe3c377dc22ade70647b39, + d41d8cd98f00b204e9800998ecf8427e, + cc -o %> %< ) + => |a0bdce7fd09e0350e7efbbdb043a00b0| + sig::md5::current(|| + eq |a0bdce7fd09e0350e7efbbdb043a00b0|) + cc -o hello, hello.o + + + + + + + +=head1 Temporary overrides + +Cons provides a very simple mechanism for overriding aspects of a build. The +essence is that you write an override file containing one or more +C commands, and you specify this on the command line, when you run +C: + + % cons -o over export + +will build the F directory, with all derived files subject to the +overrides present in the F file. If you leave out the C<-o> option, +then everything necessary to remove all overrides will be rebuilt. + + +=head2 Overriding environment variables + +The override file can contain two types of overrides. The first is incoming +environment variables. These are normally accessible by the F +file from the C<%ENV> hash variable. These can trivially be overridden in +the override file by setting the appropriate elements of C<%ENV> (these +could also be overridden in the user's environment, of course). + + +=head2 The Override command + +The second type of override is accomplished with the C command, +which looks like this: + + Override , => , => , ...; + +The regular expression I is matched against every derived file that +is a candidate for the build. If the derived file matches, then the +variable/value pairs are used to override the values in the construction +environment associated with the derived file. + +Let's suppose that we have a construction environment like this: + + $CONS = new cons( + COPT => '', + CDBG => '-g', + CFLAGS => '%COPT %CDBG', + ); + +Then if we have an override file F containing this command: + + Override '\.o$', COPT => '-O', CDBG => ''; + +then any C invocation with C<-o over> that creates F<.o> files via +this environment will cause them to be compiled with C<-O >and no C<-g>. The +override could, of course, be restricted to a single directory by the +appropriate selection of a regular expression. + +Here's the original version of the Hello, World! program, built with this +environment. Note that Cons rebuilds the appropriate pieces when the +override is applied or removed: + + % cons hello + cc -g -c hello.c -o hello.o + cc -o hello hello.o + % cons -o over hello + cc -O -c hello.c -o hello.o + cc -o hello hello.o + % cons -o over hello + cons: "hello" is up-to-date. + % cons hello + cc -g -c hello.c -o hello.o + cc -o hello hello.o + +It's important that the C command only be used for temporary, +on-the-fly overrides necessary for development because the overrides are not +platform independent and because they rely too much on intimate knowledge of +the workings of the scripts. For temporary use, however, they are exactly +what you want. + +Note that it is still useful to provide, say, the ability to create a fully +optimized version of a system for production use--from the F and +F files. This way you can tailor the optimized system to the +platform. Where optimizer trade-offs need to be made (particular files may +not be compiled with full optimization, for example), then these can be +recorded for posterity (and reproducibility) directly in the scripts. + + + +=head2 The C method + +The C method is a combination of the C and C +methods. Rather than generating an executable program directly, this command +allows you to specify your own command to actually generate a module. The +method is invoked as follows: + + Module $env , , ; + +This command is useful in instances where you wish to create, for example, +dynamically loaded modules, or statically linked code libraries. + + + + +=head2 The C method + +The C method returns the construction variables for building +various components with one of the rule sets supported by Cons. The +currently supported rule sets are: + +=over 4 + +=item msvc + +Rules for the Microsoft Visual C++ compiler suite. + +=item unix + +Generic rules for most UNIX-like compiler suites. + +=back + +On systems with more than one available compiler suite, this allows you +to easily create side-by-side environments for building software with +multiple tools: + + $msvcenv = new cons(RuleSet("msvc")); + $cygnusenv = new cons(RuleSet("unix")); + +In the future, this could also be extended to other platforms that +have different default rule sets. + + +=head2 The C method + +The C method sets the default construction variables that +will be returned by the C method to the specified arguments: + + DefaultRules(CC => 'gcc', + CFLAGS => '', + CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>'); + $env = new cons(); + # $env now contains *only* the CC, CFLAGS, + # and CCCOM construction variables + +Combined with the C method, this also provides an easy way +to set explicitly the default build environment to use some supported +toolset other than the Cons defaults: + + # use a UNIX-like tool suite (like cygwin) on Win32 + DefaultRules(RuleSet('unix')); + $env = new cons(); + +Note that the C method completely replaces the default +construction environment with the specified arguments, it does not +simply override the existing defaults. To override one or more +variables in a supported C, append the variables and values: + + DefaultRules(RuleSet('unix'), CFLAGS => '-O3'); + $env1 = new cons(); + $env2 = new cons(); + # both $env1 and $env2 have 'unix' defaults + # with CFLAGS set to '-O3' + + + + + + + + +=head2 The C method + +The C mathod returns the real source path name of a file, +as opposed to the path name within a build directory. It is invoked +as follows: + + $path = SourcePath ; + + +=head2 The C method + +The C method returns true if the supplied path is a derivable +file, and returns undef (false) otherwise. +It is invoked as follows: + + $result = ConsPath ; + + +=head2 The C method + +The C method looks up multiple path names in a string separated +by the default path separator for the operating system (':' on UNIX +systems, ';' on Windows NT), and returns the fully-qualified names. +It is invoked as follows: + + @paths = SplitPath ; + +The C method will convert names prefixed '#' to the +appropriate top-level build name (without the '#') and will convert +relative names to top-level names. + + +=head2 The C method + +The C method returns the build path name(s) of a directory or +list of directories. It is invoked as follows: + + $cwd = DirPath ; + +The most common use for the C method is: + + $cwd = DirPath '.'; + +to fetch the path to the current directory of a subsidiary F +file. + + +=head2 The C method + +The C method returns the build path name(s) of a file or +list of files. It is invoked as follows: + + $file = FilePath ; diff --git a/doc/user/copyright.in b/doc/user/copyright.in new file mode 100644 index 0000000..341698d --- /dev/null +++ b/doc/user/copyright.in @@ -0,0 +1,32 @@ + + +
+ + + SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight + + +
diff --git a/doc/user/copyright.xml b/doc/user/copyright.xml new file mode 100644 index 0000000..341698d --- /dev/null +++ b/doc/user/copyright.xml @@ -0,0 +1,32 @@ + + +
+ + + SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight + + +
diff --git a/doc/user/depends.in b/doc/user/depends.in new file mode 100644 index 0000000..dfd52e3 --- /dev/null +++ b/doc/user/depends.in @@ -0,0 +1,1816 @@ + + + + + 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 not + 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: + + + + + + Program('hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + scons -Q + scons -Q + + + + + 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: + + + + + scons -Q hello + scons -Q hello + + + + + Note that &SCons; reports "...is up to date" + only for target files named explicitly on the command line, + to avoid cluttering the output. + + + +
+ Deciding When an Input File Has Changed: the &Decider; Function + + + + Another aspect of avoiding unnecessary rebuilds + is the fundamental build tool behavior + of rebuilding + 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. + + + +
+ Using MD5 Signatures to Decide if a File Has Changed + + + + 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): + + + + + scons -Q hello + touch hello.c + scons -Q hello + + + + + Even though the file's modification time has changed, + &SCons; realizes that the contents of the + &hello_c; file have not 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: + + + + + scons -Q hello + edit hello.c + scons -Q hello + + + + + Note that you can, if you wish, + specify this default behavior + (MD5 signatures) explicitly + using the &Decider; function as follows: + + + + + Program('hello.c') + Decider('MD5') + + + + + You can also use the string 'content' + as a synonym for 'MD5' + when calling the &Decider; function. + + + +
+ Ramifications of Using MD5 Signatures + + + + 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. + + + + + + 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: + + + + + scons -Q hello + edit hello.c + scons -Q hello + + + + + 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. + + + +
+ +
+ +
+ Using Time Stamps to Decide If a File Has Changed + + + + 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. + + + + + + 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 + newer + than the target file. + To do this, call the &Decider; + function as follows: + + + + + + Program('hello.c') + Decider('timestamp-newer') + + + int main() { printf("Hello, world!\n"); } + + + + + + This makes &SCons; act like &Make; + when a file's modification time is updated + (using the &touch; command, for example): + + + + + scons -Q hello + touch hello.c + scons -Q hello + + + + + And, in fact, because this behavior is the same + as the behavior of &Make;, + you can also use the string 'make' + as a synonym for 'timestamp-newer' + when calling the &Decider; function: + + + + + Program('hello.c') + Decider('make') + + + + + One drawback to using times stamps exactly like &Make; + is that if an input file's modification time suddenly + becomes older 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. + + + + + + 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 + 'timestamp-match' + when calling the &Decider; function: + + + + + + Program('hello.c') + Decider('timestamp-match') + + + int main() { printf("Hello, world!\n"); } + + + + + + When configured this way, + &SCons; will rebuild a target whenever + a source file's modification time has changed. + So if we use the touch -t + option to change the modification time of + &hello_c; to an old date (January 1, 1989), + &SCons; will still rebuild the target file: + + + + + scons -Q hello + touch -t 198901010000 hello.c + scons -Q hello + + + + + In general, the only reason to prefer + timestamp-newer + instead of + timestamp-match, + 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. + + + +
+ +
+ Deciding If a File Has Changed Using Both MD Signatures and Time Stamps + + + + 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 'MD5-timestamp' + argument as follows: + + + + + + Program('hello.c') + Decider('MD5-timestamp') + + + int main() { printf("Hello, world!\n"); } + + + + + + So configured, &SCons will still behave like + it does when using Decider('MD5'): + + + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch hello.c + % scons -Q hello + scons: `hello' is up to date. + % edit hello.c + [CHANGE THE CONTENTS OF hello.c] + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + 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. + + + + + + The only drawback to using + Decider('MD5-timestamp') + is that &SCons; will not + 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 + Decider('MD5-timestamp') + may not be appropriate. + + + +
+ +
+ Writing Your Own Custom &Decider; Function + + + + 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. + + + + + + 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 done with a custom + &Decider; function that might look something like this: + + + + + + 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) + + + int main() { printf("Hello, world!\n"); } + + + + + + Note that in the function definition, + the dependency + (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 + str(). + + + + + + The third argument, prev_ni, + is an object that holds the + signature or timestamp information + that was recorded about the dependency + the last time the target was built. + A prev_ni object can hold + different information, + depending on the type of thing that the + dependency argument represents. + For normal files, + the prev_ni object + has the following attributes: + + + + + + + .csig + + + + The content signature, + or MD5 checksum, of the contents of the + dependency + file the list time the ⌖ was built. + + + + + + + .size + + + + The size in bytes of the dependency + file the list time the target was built. + + + + + + + .timestamp + + + + The modification time of the dependency + file the list time the ⌖ was built. + + + + + + + + + + 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. + + + +
+ +
+ Mixing Different Ways of Deciding If a File Has Changed + + + + 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 + env.Decider + method to affect only the configuration + decisions for targets built with a + specific construction environment. + + + + + + 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: + + + + + + env1 = Environment(CPPPATH = ['.']) + env2 = env1.Clone() + env2.Decider('timestamp-match') + env1.Program('prog-MD5', 'program1.c') + env2.Program('prog-timestamp', 'program2.c') + + + #include "inc.h" + int main() { printf("Hello, world!\n"); } + + + #include "inc.h" + int main() { printf("Hello, world!\n"); } + + + #define INC 1 + + + + + + If both of the programs include the same + inc.h file, + then updating the modification time of + inc.h + (using the &touch; command) + will cause only prog-timestamp + to be rebuilt: + + + + + scons -Q + touch inc.h + scons -Q + + +
+ +
+ +
+ Older Functions for Deciding When an Input File Has Changed + + + + &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. + Although they're not officially deprecated yet, + 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 existing &SConscript; files. + + + +
+ The &SourceSignatures; Function + + + + The &SourceSignatures; function is fairly straightforward, + and supports two different argument values + to configure whether source file changes should be decided + using MD5 signatures: + + + + + Program('hello.c') + SourceSignatures('MD5') + + + + + Or using time stamps: + + + + + Program('hello.c') + SourceSignatures('timestamp') + + + + + These are roughly equivalent to specifying + Decider('MD5') + or + Decider('timestamp-match'), + respectively, + although it only affects how SCons makes + decisions about dependencies on + source files--that is, + files that are not built from any other files. + + + +
+ +
+ The &TargetSignatures; Function + + + + The &TargetSignatures; function + specifies how &SCons; decides + when a target file has changed + when it is used as a + dependency of (input to) another target--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. + + 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. + + + + + + + The &TargetSignatures; function supports the same + 'MD5' and 'timestamp' + argument values that are supported by the &SourceSignatures;, + with the same meanings, but applied to target files. + That is, in the example: + + + + + Program('hello.c') + TargetSignatures('MD5') + + + + + 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: + + + + + Program('hello.c') + TargetSignatures('timestamp') + + + + + 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. + + + + + + The &TargetSignatures; function supports + two additional argument values: + 'source' and 'build'. + The 'source' 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: + + + + + Program('hello.c') + TargetSignatures('source') + SourceSignatures('timestamp') + + + + + 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. + + + + + + Lastly, the 'build' 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. + + + + + + This mimics the behavior of + build signatures + 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 + Decider('MD5-timestamp'). + + + +
+ +
+ +
+ Implicit Dependencies: The &cv-CPPPATH; Construction Variable + + + + Now suppose that our "Hello, World!" program + actually has an #include line + to include the &hello_h; file in the compilation: + + + + + + Program('hello.c', CPPPATH = '.') + + + #include <hello.h> + int + main() + { + printf("Hello, %s!\n", string); + } + + + #define string "world" + + + + + + And, for completeness, the &hello_h; file looks like this: + + + + + + + + + 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: + + + + + + + + + The &cv-link-CPPPATH; value + tells &SCons; to look in the current directory + ('.') + for any files included by C source files + (.c or .h files). + With this assignment in the &SConstruct; file: + + + + + scons -Q hello + scons -Q hello + edit hello.h + scons -Q hello + + + + + First, notice that &SCons; + added the -I. argument + from the &cv-CPPPATH; variable + so that the compilation would find the + &hello_h; file in the local directory. + + + + + + Second, realize that &SCons; knows that the &hello; + program must be rebuilt + because it scans the contents of + the &hello_c; file + for the #include lines that indicate + another file is being included in the compilation. + &SCons; records these as + implicit dependencies + 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. + + + + + + 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: + + + + + + Program('hello.c', CPPPATH = ['include', '/home/project/inc']) + + + int main() { printf("Hello, world!\n"); } + + + + + + Will look like this on POSIX or Linux: + + + + + scons -Q hello + + + + + And like this on Windows: + + + + + scons -Q hello.exe + + +
+ +
+ Caching Implicit Dependencies + + + + Scanning each file for #include 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 rebuild + 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). + + + + + + + + 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: + + + + + scons -Q --implicit-cache hello + scons -Q hello + + + + + 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: + + + + + SetOption('implicit_cache', 1) + + + + + &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 not rebuild "correctly" in the + following cases: + + + + + + + + + + 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. + + + + + + + + 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. + + + + + + +
+ The &implicit-deps-changed; Option + + + + 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: + + + + + scons -Q --implicit-deps-changed hello + scons -Q hello + + + + + In this case, &SCons; will re-scan all of the implicit dependencies + and cache updated copies of the information. + + + +
+ +
+ The &implicit-deps-unchanged; Option + + + + 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 #include lines. + In this case, + you can use the &implicit-deps-unchanged; option: + + + + + scons -Q --implicit-deps-unchanged hello + scons -Q hello + + + + + 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. + + + +
+ + + +
+ +
+ Explicit Dependencies: the &Depends; Function + + + + 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: + + + + + hello = Program('hello.c') + Depends(hello, 'other_file') + + + + + + % scons -Q hello + cc -c hello.c -o hello.o + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + % edit other_file + [CHANGE THE CONTENTS OF other_file] + % scons -Q hello + cc -c hello.c -o hello.o + cc -o hello hello.o + + + + + 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): + + + + + hello = Program('hello.c') + goodbye = Program('goodbye.c') + Depends(hello, goodbye) + + + + + in which case the dependency or dependencies + will be built before the target(s): + + + + + % scons -Q hello + cc -c goodbye.c -o goodbye.o + cc -o goodbye goodbye.o + cc -c hello.c -o hello.o + cc -o hello hello.o + + +
+ +
+ Dependencies From External Files: the &ParseDepends; + Function + + + + &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. + + + + + + The following example illustrates a case where the built-in C + scanner is unable to extract the implicit dependency on a header + file. + + + + + + #define FOO_HEADER <foo.h> + #include FOO_HEADER + + int main() { + return FOO; + } + + + Program('hello', 'hello.c', CPPPATH='.') + + + #define FOO 42 + + + + + scons -Q + edit foo.h + scons -Q + + + + + Apparently, the scanner does not know about the header dependency. + Being not a full-fledged C preprocessor, the scanner does not + expand the macro. + + + + + + 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. + + + + + + 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: + + + + + + + + #define FOO_HEADER <foo.h> + #include FOO_HEADER + + int main() { + return FOO; + } + + + obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') + SideEffect('hello.d', obj) + ParseDepends('hello.d') + Program('hello', obj) + + + #define FOO 42 + + + hello.o: hello.c foo.h + + + + + scons -Q + edit foo.h + scons -Q + + + + + Parsing dependencies from a compiler-generated + .d file has a chicken-and-egg problem, that + causes unnecessary rebuilds: + + + + + + #define FOO_HEADER <foo.h> + #include FOO_HEADER + + int main() { + return FOO; + } + + + obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') + SideEffect('hello.d', obj) + ParseDepends('hello.d') + Program('hello', obj) + + + #define FOO 42 + + + + + + + % scons -Q + cc -o hello.o -c -MD -MF hello.d -I. hello.c + cc -o hello hello.o + % scons -Q --debug=explain + scons: rebuilding `hello.o' because `foo.h' is a new dependency + cc -o hello.o -c -MD -MF hello.d -I. hello.c + % scons -Q + scons: `.' is up to date. + + + + + 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 foo.h. In the second pass, + the object file is regenerated because foo.h + is detected as a new dependency. + + + + + + &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. + + + +
+ +
+ Ignoring Dependencies: the &Ignore; Function + + + + 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: + + + + + + hello = Program('hello.c') + Ignore(hello, 'hello.h') + + + #include "hello.h" + int main() { printf("Hello, %s!\n", string); } + + + #define string "world" + + + + + + + + + % scons -Q hello + cc -c -o hello.o hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + % edit hello.h + [CHANGE THE CONTENTS OF hello.h] + % scons -Q hello + scons: `hello' is up to date. + + + + + 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: + + + + + hello = Program('hello.c', CPPPATH=['/usr/include']) + Ignore(hello, '/usr/include/stdio.h') + + + + &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. + + + + + hello_obj=Object('hello.c') + hello = Program(hello_obj) + Ignore('.',[hello,hello_obj]) + + + #include "stdio.h" + int main() { printf("Hello!\n"); } + + + + + scons -Q + scons -Q hello + scons -Q hello + +
+ +
+ Order-Only Dependencies: the &Requires; Function + + + + 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 not + require that the target itself be rebuilt. + Such a relationship is called an + order-only dependency + 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. + + + + + + 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 version.c 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 version.c in the sources: + + + + + + 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']) + + + extern char *date; + int main() { printf("Hello, %s! I was built: %s\n", date); } + + + + + + If we list version.c as an actual source file, + though, then version.o + will get rebuilt every time we run &SCons; + (because the &SConstruct; file itself changes + the contents of version.c) + and the hello executable + will get re-linked every time + (because the version.o file changes): + + + + + + + % scons -Q + gcc -o hello.o -c hello.c + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + + + + + One solution is to use the &Requires; function + to specify that the version.o + must be rebuilt before it is used by the link step, + but that changes to version.o + should not actually cause the hello + executable to be re-linked: + + + + + + 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) + + + extern char *date; + int main() { printf("Hello, %s! I was built: %s\n", date); } + + + + + + Notice that because we can no longer list version.c + as one of the sources for the hello 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 version_obj + 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. + + + + + + With these changes, + we get the desired behavior of + re-building the version.o file, + and therefore re-linking the hello executable, + only when the hello.c has changed: + + + + + scons -Q + scons -Q + edit hello.c + scons -Q + scons -Q + + +
+ +
+ The &AlwaysBuild; Function + + + + How &SCons; handles dependencies can also be affected + by the &AlwaysBuild; method. + When a file is passed to the &AlwaysBuild; method, + like so: + + + + + + hello = Program('hello.c') + AlwaysBuild(hello) + + + int main() { printf("Hello, %s!\n", string); } + + + + + + 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: + + + + + scons -Q + scons -Q + + + + + 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 not + itself depend on the &AlwaysBuild; target, + will still be rebuilt only if it's out-of-date + with respect to its dependencies: + + + + + scons -Q + scons -Q hello.o + + + + +
+ + diff --git a/doc/user/depends.xml b/doc/user/depends.xml new file mode 100644 index 0000000..b72f41f --- /dev/null +++ b/doc/user/depends.xml @@ -0,0 +1,1776 @@ + + + + + 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 not + 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: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + scons: `.' is up to date. + + + + + 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: + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + Note that &SCons; reports "...is up to date" + only for target files named explicitly on the command line, + to avoid cluttering the output. + + + +
+ Deciding When an Input File Has Changed: the &Decider; Function + + + + Another aspect of avoiding unnecessary rebuilds + is the fundamental build tool behavior + of rebuilding + 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. + + + +
+ Using MD5 Signatures to Decide if a File Has Changed + + + + 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): + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch hello.c + % scons -Q hello + scons: `hello' is up to date. + + + + + Even though the file's modification time has changed, + &SCons; realizes that the contents of the + &hello_c; file have not 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: + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % edit hello.c + [CHANGE THE CONTENTS OF hello.c] + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + Note that you can, if you wish, + specify this default behavior + (MD5 signatures) explicitly + using the &Decider; function as follows: + + + + + Program('hello.c') + Decider('MD5') + + + + + You can also use the string 'content' + as a synonym for 'MD5' + when calling the &Decider; function. + + + +
+ Ramifications of Using MD5 Signatures + + + + 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. + + + + + + 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: + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % edit hello.c + [CHANGE A COMMENT IN hello.c] + % scons -Q hello + cc -o hello.o -c hello.c + scons: `hello' is up to date. + + + + + 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. + + + +
+ +
+ +
+ Using Time Stamps to Decide If a File Has Changed + + + + 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. + + + + + + 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 + newer + than the target file. + To do this, call the &Decider; + function as follows: + + + + + Program('hello.c') + Decider('timestamp-newer') + + + + + This makes &SCons; act like &Make; + when a file's modification time is updated + (using the &touch; command, for example): + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch hello.c + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + And, in fact, because this behavior is the same + as the behavior of &Make;, + you can also use the string 'make' + as a synonym for 'timestamp-newer' + when calling the &Decider; function: + + + + + Program('hello.c') + Decider('make') + + + + + One drawback to using times stamps exactly like &Make; + is that if an input file's modification time suddenly + becomes older 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. + + + + + + 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 + 'timestamp-match' + when calling the &Decider; function: + + + + + Program('hello.c') + Decider('timestamp-match') + + + + + When configured this way, + &SCons; will rebuild a target whenever + a source file's modification time has changed. + So if we use the touch -t + option to change the modification time of + &hello_c; to an old date (January 1, 1989), + &SCons; will still rebuild the target file: + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch -t 198901010000 hello.c + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + In general, the only reason to prefer + timestamp-newer + instead of + timestamp-match, + 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. + + + +
+ +
+ Deciding If a File Has Changed Using Both MD Signatures and Time Stamps + + + + 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 'MD5-timestamp' + argument as follows: + + + + + Program('hello.c') + Decider('MD5-timestamp') + + + + + So configured, &SCons; will still behave like + it does when using Decider('MD5'): + + + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch hello.c + % scons -Q hello + scons: `hello' is up to date. + % edit hello.c + [CHANGE THE CONTENTS OF hello.c] + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + 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. + + + + + + The only drawback to using + Decider('MD5-timestamp') + is that &SCons; will not + 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 + Decider('MD5-timestamp') + may not be appropriate. + + + +
+ +
+ Writing Your Own Custom &Decider; Function + + + + 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. + + + + + + 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 done with a custom + &Decider; function that might look something like this: + + + + + 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) + + + + + Note that in the function definition, + the dependency + (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 + str(). + + + + + + The third argument, prev_ni, + is an object that holds the + signature or timestamp information + that was recorded about the dependency + the last time the target was built. + A prev_ni object can hold + different information, + depending on the type of thing that the + dependency argument represents. + For normal files, + the prev_ni object + has the following attributes: + + + + + + + .csig + + + + The content signature, + or MD5 checksum, of the contents of the + dependency + file the list time the ⌖ was built. + + + + + + + .size + + + + The size in bytes of the dependency + file the list time the target was built. + + + + + + + .timestamp + + + + The modification time of the dependency + file the list time the ⌖ was built. + + + + + + + + + + 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. + + + +
+ +
+ Mixing Different Ways of Deciding If a File Has Changed + + + + 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 + env.Decider + method to affect only the configuration + decisions for targets built with a + specific construction environment. + + + + + + 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: + + + + + env1 = Environment(CPPPATH = ['.']) + env2 = env1.Clone() + env2.Decider('timestamp-match') + env1.Program('prog-MD5', 'program1.c') + env2.Program('prog-timestamp', 'program2.c') + + + + + If both of the programs include the same + inc.h file, + then updating the modification time of + inc.h + (using the &touch; command) + will cause only prog-timestamp + to be rebuilt: + + + + + % scons -Q + cc -o program1.o -c -I. program1.c + cc -o prog-MD5 program1.o + cc -o program2.o -c -I. program2.c + cc -o prog-timestamp program2.o + % touch inc.h + % scons -Q + cc -o program2.o -c -I. program2.c + cc -o prog-timestamp program2.o + + +
+ +
+ +
+ Older Functions for Deciding When an Input File Has Changed + + + + &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. + Although they're not officially deprecated yet, + 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 existing &SConscript; files. + + + +
+ The &SourceSignatures; Function + + + + The &SourceSignatures; function is fairly straightforward, + and supports two different argument values + to configure whether source file changes should be decided + using MD5 signatures: + + + + + Program('hello.c') + SourceSignatures('MD5') + + + + + Or using time stamps: + + + + + Program('hello.c') + SourceSignatures('timestamp') + + + + + These are roughly equivalent to specifying + Decider('MD5') + or + Decider('timestamp-match'), + respectively, + although it only affects how SCons makes + decisions about dependencies on + source files--that is, + files that are not built from any other files. + + + +
+ +
+ The &TargetSignatures; Function + + + + The &TargetSignatures; function + specifies how &SCons; decides + when a target file has changed + when it is used as a + dependency of (input to) another target--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. + + 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. + + + + + + + The &TargetSignatures; function supports the same + 'MD5' and 'timestamp' + argument values that are supported by the &SourceSignatures;, + with the same meanings, but applied to target files. + That is, in the example: + + + + + Program('hello.c') + TargetSignatures('MD5') + + + + + 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: + + + + + Program('hello.c') + TargetSignatures('timestamp') + + + + + 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. + + + + + + The &TargetSignatures; function supports + two additional argument values: + 'source' and 'build'. + The 'source' 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: + + + + + Program('hello.c') + TargetSignatures('source') + SourceSignatures('timestamp') + + + + + 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. + + + + + + Lastly, the 'build' 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. + + + + + + This mimics the behavior of + build signatures + 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 + Decider('MD5-timestamp'). + + + +
+ +
+ +
+ Implicit Dependencies: The &cv-CPPPATH; Construction Variable + + + + Now suppose that our "Hello, World!" program + actually has an #include line + to include the &hello_h; file in the compilation: + + + + + #include <hello.h> + int + main() + { + printf("Hello, %s!\n", string); + } + + + + + And, for completeness, the &hello_h; file looks like this: + + + + + + #define string "world" + + + + + 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: + + + + + + Program('hello.c', CPPPATH = '.') + + + + + The &cv-link-CPPPATH; value + tells &SCons; to look in the current directory + ('.') + for any files included by C source files + (.c or .h files). + With this assignment in the &SConstruct; file: + + + + + % scons -Q hello + cc -o hello.o -c -I. hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + % edit hello.h + [CHANGE THE CONTENTS OF hello.h] + % scons -Q hello + cc -o hello.o -c -I. hello.c + cc -o hello hello.o + + + + + First, notice that &SCons; + added the -I. argument + from the &cv-CPPPATH; variable + so that the compilation would find the + &hello_h; file in the local directory. + + + + + + Second, realize that &SCons; knows that the &hello; + program must be rebuilt + because it scans the contents of + the &hello_c; file + for the #include lines that indicate + another file is being included in the compilation. + &SCons; records these as + implicit dependencies + 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. + + + + + + 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: + + + + + Program('hello.c', CPPPATH = ['include', '/home/project/inc']) + + + + + Will look like this on POSIX or Linux: + + + + + % scons -Q hello + cc -o hello.o -c -Iinclude -I/home/project/inc hello.c + cc -o hello hello.o + + + + + And like this on Windows: + + + + + C:\>scons -Q hello.exe + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fohello.obj /c hello.c /nologo /Iinclude /I\home\project\inc + link /nologo /OUT:hello.exe hello.obj + + +
+ +
+ Caching Implicit Dependencies + + + + Scanning each file for #include 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 rebuild + 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). + + + + + + + + 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: + + + + + % scons -Q --implicit-cache hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + 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: + + + + + SetOption('implicit_cache', 1) + + + + + &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 not rebuild "correctly" in the + following cases: + + + + + + + + + + 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. + + + + + + + + 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. + + + + + + +
+ The &implicit-deps-changed; Option + + + + 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: + + + + + % scons -Q --implicit-deps-changed hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + In this case, &SCons; will re-scan all of the implicit dependencies + and cache updated copies of the information. + + + +
+ +
+ The &implicit-deps-unchanged; Option + + + + 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 #include lines. + In this case, + you can use the &implicit-deps-unchanged; option: + + + + + % scons -Q --implicit-deps-unchanged hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + 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. + + + +
+ + + +
+ +
+ Explicit Dependencies: the &Depends; Function + + + + 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: + + + + + hello = Program('hello.c') + Depends(hello, 'other_file') + + + + + + % scons -Q hello + cc -c hello.c -o hello.o + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + % edit other_file + [CHANGE THE CONTENTS OF other_file] + % scons -Q hello + cc -c hello.c -o hello.o + cc -o hello hello.o + + + + + 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): + + + + + hello = Program('hello.c') + goodbye = Program('goodbye.c') + Depends(hello, goodbye) + + + + + in which case the dependency or dependencies + will be built before the target(s): + + + + + % scons -Q hello + cc -c goodbye.c -o goodbye.o + cc -o goodbye goodbye.o + cc -c hello.c -o hello.o + cc -o hello hello.o + + +
+ +
+ Dependencies From External Files: the &ParseDepends; + Function + + + + &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. + + + + + + The following example illustrates a case where the built-in C + scanner is unable to extract the implicit dependency on a header + file. + + + + + #define FOO_HEADER <foo.h> + #include FOO_HEADER + + int main() { + return FOO; + } + + + + % scons -Q + cc -o hello.o -c -I. hello.c + cc -o hello hello.o + % edit foo.h + [CHANGE CONTENTS OF foo.h] + % scons -Q + scons: `.' is up to date. + + + + + Apparently, the scanner does not know about the header dependency. + Being not a full-fledged C preprocessor, the scanner does not + expand the macro. + + + + + + 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. + + + + + + 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: + + + + + + + obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') + SideEffect('hello.d', obj) + ParseDepends('hello.d') + Program('hello', obj) + + + + % scons -Q + cc -o hello.o -c -MD -MF hello.d -I. hello.c + cc -o hello hello.o + % edit foo.h + [CHANGE CONTENTS OF foo.h] + % scons -Q + cc -o hello.o -c -MD -MF hello.d -I. hello.c + + + + + Parsing dependencies from a compiler-generated + .d file has a chicken-and-egg problem, that + causes unnecessary rebuilds: + + + + + + + + + % scons -Q + cc -o hello.o -c -MD -MF hello.d -I. hello.c + cc -o hello hello.o + % scons -Q --debug=explain + scons: rebuilding `hello.o' because `foo.h' is a new dependency + cc -o hello.o -c -MD -MF hello.d -I. hello.c + % scons -Q + scons: `.' is up to date. + + + + + 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 foo.h. In the second pass, + the object file is regenerated because foo.h + is detected as a new dependency. + + + + + + &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. + + + +
+ +
+ Ignoring Dependencies: the &Ignore; Function + + + + 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: + + + + + hello = Program('hello.c') + Ignore(hello, 'hello.h') + + + + + + + + % scons -Q hello + cc -c -o hello.o hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + % edit hello.h + [CHANGE THE CONTENTS OF hello.h] + % scons -Q hello + scons: `hello' is up to date. + + + + + 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: + + + + + hello = Program('hello.c', CPPPATH=['/usr/include']) + Ignore(hello, '/usr/include/stdio.h') + + + + &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. + + + + hello_obj=Object('hello.c') + hello = Program(hello_obj) + Ignore('.',[hello,hello_obj]) + + + + % scons -Q + scons: `.' is up to date. + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + +
+ +
+ Order-Only Dependencies: the &Requires; Function + + + + 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 not + require that the target itself be rebuilt. + Such a relationship is called an + order-only dependency + 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. + + + + + + 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 version.c 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 version.c in the sources: + + + + + 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']) + + + + + If we list version.c as an actual source file, + though, then version.o + will get rebuilt every time we run &SCons; + (because the &SConstruct; file itself changes + the contents of version.c) + and the hello executable + will get re-linked every time + (because the version.o file changes): + + + + + + + % scons -Q + gcc -o hello.o -c hello.c + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + + + + + One solution is to use the &Requires; function + to specify that the version.o + must be rebuilt before it is used by the link step, + but that changes to version.o + should not actually cause the hello + executable to be re-linked: + + + + + 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) + + + + + Notice that because we can no longer list version.c + as one of the sources for the hello 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 version_obj + 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. + + + + + + With these changes, + we get the desired behavior of + re-building the version.o file, + and therefore re-linking the hello executable, + only when the hello.c has changed: + + + + + % scons -Q + cc -o version.o -c version.c + cc -o hello.o -c hello.c + cc -o hello version.o hello.o + % scons -Q + scons: `.' is up to date. + % edit hello.c + [CHANGE THE CONTENTS OF hello.c] + % scons -Q + cc -o version.o -c version.c + cc -o hello.o -c hello.c + cc -o hello version.o hello.o + % scons -Q + scons: `.' is up to date. + + +
+ +
+ The &AlwaysBuild; Function + + + + How &SCons; handles dependencies can also be affected + by the &AlwaysBuild; method. + When a file is passed to the &AlwaysBuild; method, + like so: + + + + + hello = Program('hello.c') + AlwaysBuild(hello) + + + + + 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: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + cc -o hello hello.o + + + + + 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 not + itself depend on the &AlwaysBuild; target, + will still be rebuilt only if it's out-of-date + with respect to its dependencies: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello.o + scons: `hello.o' is up to date. + + + + +
+ + diff --git a/doc/user/environments.in b/doc/user/environments.in new file mode 100644 index 0000000..124aaaa --- /dev/null +++ b/doc/user/environments.in @@ -0,0 +1,1678 @@ + + + + + + + An environment + is a collection of values that + can affect how a program executes. + &SCons; distinguishes between three + different types of environments + that can affect the behavior of &SCons; itself + (subject to the configuration in the &SConscript; files), + as well as the compilers and other tools it executes: + + + + + + + External Environment + + + + + The external environment + is the set of variables in the user's environment + at the time the user runs &SCons. + These variables are available within the &SConscript; files + through the Python os.environ dictionary. + See , below. + + + + + + + &ConsEnv; + + + + + A &consenv; + is a distinct object creating within + a &SConscript; file and + and which contains values that + affect how &SCons; decides + what action to use to build a target, + and even to define which targets + should be built from which sources. + One of the most powerful features of &SCons; + is the ability to create multiple &consenvs;, + including the ability to clone a new, customized + &consenv; from an existing &consenv;. + See , below. + + + + + + + Execution Environment + + + + + An execution environment + is the values that &SCons; sets + when executing an external + command (such as a compiler or linker) + to build one or more targets. + Note that this is not the same as + the external environment + (see above). + See , below. + + + + + + + + + + Unlike &Make;, &SCons; does not automatically + copy or import values between different environments + (with the exception of explicit clones of &consenvs, + which inherit values from their parent). + This is a deliberate design choice + to make sure that builds are, + by default, repeatable regardless of + the values in the user's external environment. + This avoids a whole class of problems with builds + where a developer's local build works + because a custom variable setting + causes a different compiler or build option to be used, + but the checked-in change breaks the official build + because it uses different environment variable settings. + + + + + + Note that the &SConscript; writer can + easily arrange for variables to be + copied or imported between environments, + and this is often very useful + (or even downright necessary) + to make it easy for developers + to customize the build in appropriate ways. + The point is not + that copying variables between different environments + is evil and must always be avoided. + Instead, it should be up to the + implementer of the build system + to make conscious choices + about how and when to import + a variable from one environment to another, + making informed decisions about + striking the right balance + between making the build + repeatable on the one hand + and convenient to use on the other. + + + +
+ Using Values From the External Environment + + + + The external environment + variable settings that + the user has in force + when executing &SCons; + are available through the normal Python + os.environ + dictionary. + This means that you must add an + import os statement + to any &SConscript; file + in which you want to use + values from the user's external environment. + + + + + + import os + + + int main() { } + + + + + + More usefully, you can use the + os.environ + dictionary in your &SConscript; + files to initialize &consenvs; + with values from the user's external environment. + See the next section, + , + for information on how to do this. + + + +
+ +
+ Construction Environments + + + + It is rare that all of the software in a large, + complicated system needs to be built the same way. + For example, different source files may need different options + enabled on the command line, + or different executable programs need to be linked + with different libraries. + &SCons; accommodates these different build + requirements by allowing you to create and + configure multiple &consenvs; + that control how the software is built. + A &consenv; is an object + that has a number of associated + &consvars;, each with a name and a value. + (A construction environment also has an attached + set of &Builder; methods, + about which we'll learn more later.) + + + +
+ Creating a &ConsEnv;: the &Environment; Function + + + + A &consenv; is created by the &Environment; method: + + + + + env = Environment() + + + + + By default, &SCons; initializes every + new construction environment + with a set of &consvars; + based on the tools that it finds on your system, + plus the default set of builder methods + necessary for using those tools. + The construction variables + are initialized with values describing + the C compiler, + the Fortran compiler, + the linker, + etc., + as well as the command lines to invoke them. + + + + + + When you initialize a construction environment + you can set the values of the + environment's &consvars; + to control how a program is built. + For example: + + + + + + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') + + env.Program('foo.c') + + + int main() { } + + + + + + The construction environment in this example + is still initialized with the same default + construction variable values, + except that the user has explicitly specified use of the + GNU C compiler &gcc;, + and further specifies that the -O2 + (optimization level two) + flag should be used when compiling the object file. + In other words, the explicit initializations of + &cv-link-CC; and &cv-link-CCFLAGS; + override the default values in the newly-created + construction environment. + So a run from this example would look like: + + + + + scons -Q + + +
+ +
+ Fetching Values From a &ConsEnv; + + + + You can fetch individual construction variables + using the normal syntax + for accessing individual named items in a Python dictionary: + + + + + + env = Environment() + print "CC is:", env['CC'] + + + + + + This example &SConstruct; file doesn't build anything, + but because it's actually a Python script, + it will print the value of &cv-link-CC; for us: + + + + + scons -Q + + + + + A construction environment, however, + is actually an object with associated methods, etc. + If you want to have direct access to only the + dictionary of construction variables, + you can fetch this using the &Dictionary; method: + + + + + + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: + print "key = %s, value = %s" % (key, dict[key]) + + + + + + This &SConstruct; file + will print the specified dictionary items for us on POSIX + systems as follows: + + + + + scons -Q + + + + + And on Windows: + + + + + scons -Q + + + + + If you want to loop and print the values of + all of the construction variables in a construction environment, + the Python code to do that in sorted order might look something like: + + + + + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + + +
+ +
+ Expanding Values From a &ConsEnv;: the &subst; Method + + + + Another way to get information from + a construction environment. + is to use the &subst; method + on a string containing $ expansions + of construction variable names. + As a simple example, + the example from the previous + section that used + env['CC'] + to fetch the value of &cv-link-CC; + could also be written as: + + + + + env = Environment() + print "CC is:", env.subst('$CC') + + + + + One advantage of using + &subst; to expand strings is + that construction variables + in the result get re-expanded until + there are no expansions left in the string. + So a simple fetch of a value like + &cv-link-CCCOM;: + + + + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + + + + + Will print the unexpanded value of &cv-CCCOM;, + showing us the construction + variables that still need to be expanded: + + + + + % scons -Q + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + scons: `.' is up to date. + + + + + Calling the &subst; method on $CCOM, + however: + + + + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + + + + + Will recursively expand all of + the construction variables prefixed + with $ (dollar signs), + showing us the final output: + + + + + % scons -Q + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + + + + + Note that because we're not expanding this + in the context of building something + there are no target or source files + for &cv-link-TARGET; and &cv-link-SOURCES; to expand. + + + +
+ +
+ Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function + + + + All of the &Builder; functions that we've introduced so far, + like &Program; and &Library;, + actually use a default &consenv; + that contains settings + for the various compilers + and other tools that + &SCons; configures by default, + or otherwise knows about + and has discovered on your system. + The goal of the default construction environment + is to make many configurations to "just work" + to build software using + readily available tools + with a minimum of configuration changes. + + + + + + You can, however, control the settings + in the default contstruction environment + by using the &DefaultEnvironment; function + to initialize various settings: + + + + + + DefaultEnvironment(CC = '/usr/local/bin/gcc') + + + + + + When configured as above, + all calls to the &Program; + or &Object; Builder + will build object files with the + /usr/local/bin/gcc + compiler. + + + + + + Note that the &DefaultEnvironment; function + returns the initialized + default construction environment object, + which can then be manipulated like any + other construction environment. + So the following + would be equivalent to the + previous example, + setting the &cv-CC; + variable to /usr/local/bin/gcc + but as a separate step after + the default construction environment has been initialized: + + + + + + env = DefaultEnvironment() + env['CC'] = '/usr/local/bin/gcc' + + + + + + One very common use of the &DefaultEnvironment; function + is to speed up &SCons; initialization. + As part of trying to make most default + configurations "just work," + &SCons; will actually + search the local system for installed + compilers and other utilities. + This search can take time, + especially on systems with + slow or networked file systems. + If you know which compiler(s) and/or + other utilities you want to configure, + you can control the search + that &SCons; performs + by specifying some specific + tool modules with which to + initialize the default construction environment: + + + + + + env = DefaultEnvironment(tools = ['gcc', 'gnulink'], + CC = '/usr/local/bin/gcc') + + + + + + So the above example would tell &SCons; + to explicitly configure the default environment + to use its normal GNU Compiler and GNU Linker settings + (without having to search for them, + or any other utilities for that matter), + and specifically to use the compiler found at + /usr/local/bin/gcc. + + + +
+ +
+ Multiple &ConsEnvs; + + + + The real advantage of construction environments + is that you can create as many different construction + environments as you need, + each tailored to a different way to build + some piece of software or other file. + If, for example, we need to build + one program with the -O2 flag + and another with the -g (debug) flag, + we would do this like so: + + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('bar', 'bar.c') + + + int main() { } + + + int main() { } + + + + + scons -Q + + + + + We can even use multiple construction environments to build + multiple versions of a single program. + If you do this by simply trying to use the + &b-link-Program; builder with both environments, though, + like this: + + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('foo', 'foo.c') + + + int main() { } + + + + + + Then &SCons; generates the following error: + + + + + scons -Q + + + + + This is because the two &b-Program; calls have + each implicitly told &SCons; to generate an object file named + foo.o, + one with a &cv-link-CCFLAGS; value of + -O2 + and one with a &cv-link-CCFLAGS; value of + -g. + &SCons; can't just decide that one of them + should take precedence over the other, + so it generates the error. + To avoid this problem, + we must explicitly specify + that each environment compile + foo.c + to a separately-named object file + using the &b-link-Object; builder, like so: + + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + + + int main() { } + + + + + + Notice that each call to the &b-Object; builder + returns a value, + an internal &SCons; object that + represents the object file that will be built. + We then use that object + as input to the &b-Program; builder. + This avoids having to specify explicitly + the object file name in multiple places, + and makes for a compact, readable + &SConstruct; file. + Our &SCons; output then looks like: + + + + + scons -Q + + +
+ +
+ Making Copies of &ConsEnvs;: the &Clone; Method + + + + Sometimes you want more than one construction environment + to share the same values for one or more variables. + Rather than always having to repeat all of the common + variables when you create each construction environment, + you can use the &Clone; method + to create a copy of a construction environment. + + + + + + Like the &Environment; call that creates a construction environment, + the &Clone; method takes &consvar; assignments, + which will override the values in the copied construction environment. + For example, suppose we want to use &gcc; + to create three versions of a program, + one optimized, one debug, and one with neither. + We could do this by creating a "base" construction environment + that sets &cv-link-CC; to &gcc;, + and then creating two copies, + one which sets &cv-link-CCFLAGS; for optimization + and the other which sets &cv-CCFLAGS; for debugging: + + + + + + env = Environment(CC = 'gcc') + opt = env.Clone(CCFLAGS = '-O2') + dbg = env.Clone(CCFLAGS = '-g') + + env.Program('foo', 'foo.c') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + + + int main() { } + + + + + + Then our output would look like: + + + + + scons -Q + + +
+ +
+ Replacing Values: the &Replace; Method + + + + You can replace existing construction variable values + using the &Replace; method: + + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('foo.c') + + + int main() { } + + + + + + The replacing value + (-DDEFINE2 in the above example) + completely replaces the value in the + construction environment: + + + + + scons -Q + + + + + You can safely call &Replace; + for construction variables that + don't exist in the construction environment: + + + + + + env = Environment() + env.Replace(NEW_VARIABLE = 'xyzzy') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + + In this case, + the construction variable simply + gets added to the construction environment: + + + + + scons -Q + + + + + Because the variables + aren't expanded until the construction environment + is actually used to build the targets, + and because &SCons; function and method calls + are order-independent, + the last replacement "wins" + and is used to build all targets, + regardless of the order in which + the calls to Replace() are + interspersed with calls to + builder methods: + + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('foo.c') + + env.Replace(CCFLAGS = '-DDEFINE2') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('bar.c') + + + int main() { } + + + int main() { } + + + + + + The timing of when the replacement + actually occurs relative + to when the targets get built + becomes apparent + if we run &scons; without the -Q + option: + + + + + scons + + + + + Because the replacement occurs while + the &SConscript; files are being read, + the &cv-link-CCFLAGS; + variable has already been set to + -DDEFINE2 + by the time the &foo_o; target is built, + even though the call to the &Replace; + method does not occur until later in + the &SConscript; file. + + + +
+ +
+ Setting Values Only If They're Not Already Defined: the &SetDefault; Method + + + + Sometimes it's useful to be able to specify + that a construction variable should be + set to a value only if the construction environment + does not already have that variable defined + You can do this with the &SetDefault; method, + which behaves similarly to the set_default + method of Python dictionary objects: + + + + + env.SetDefault(SPECIAL_FLAG = '-extra-option') + + + + + This is especially useful + when writing your own Tool modules + to apply variables to construction environments. + + + + +
+ +
+ Appending to the End of Values: the &Append; Method + + + + You can append a value to + an existing construction variable + using the &Append; method: + + + + + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Append(CCFLAGS = ['-DLAST']) + env.Program('foo.c') + + + int main() { } + + + + + + &SCons; then supplies both the -DMY_VALUE and + -DLAST flags when compiling the object file: + + + + + scons -Q + + + + + If the construction variable doesn't already exist, + the &Append; method will create it: + + + + + + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + + Which yields: + + + + + scons -Q + + + + + Note that the &Append; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. + + + +
+ +
+ Appending Unique Values: the &AppendUnique; Method + + + + Some times it's useful to add a new value + only if the existing construction variable + doesn't already contain the value. + This can be done using the &AppendUnique; method: + + + + + env.AppendUnique(CCFLAGS=['-g']) + + + + + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. + + + +
+ +
+ Appending to the Beginning of Values: the &Prepend; Method + + + + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: + + + + + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Prepend(CCFLAGS = ['-DFIRST']) + env.Program('foo.c') + + + int main() { } + + + + + + &SCons; then supplies both the -DFIRST and + -DMY_VALUE flags when compiling the object file: + + + + + scons -Q + + + + + If the construction variable doesn't already exist, + the &Prepend; method will create it: + + + + + + env = Environment() + env.Prepend(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + + Which yields: + + + + + scons -Q + + + + + Like the &Append; function, + the &Prepend; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. + + + +
+ +
+ Prepending Unique Values: the &PrependUnique; Method + + + + Some times it's useful to add a new value + to the beginning of a construction variable + only if the existing value + doesn't already contain the to-be-added value. + This can be done using the &PrependUnique; method: + + + + + env.PrependUnique(CCFLAGS=['-g']) + + + + + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. + + + +
+ +
+ +
+ Controlling the Execution Environment for Issued Commands + + + + When &SCons; builds a target file, + it does not execute the commands with + the same external environment + that you used to execute &SCons;. + Instead, it uses the dictionary + stored in the &cv-link-ENV; construction variable + as the external environment + for executing commands. + + + + + + The most important ramification of this behavior + is that the &PATH; environment variable, + which controls where the operating system + will look for commands and utilities, + is not the same as in the external environment + from which you called &SCons;. + This means that &SCons; will not, by default, + necessarily find all of the tools + that you can execute from the command line. + + + + + + The default value of the &PATH; environment variable + on a POSIX system + is /usr/local/bin:/bin:/usr/bin. + The default value of the &PATH; environment variable + on a Windows system comes from the Windows registry + value for the command interpreter. + If you want to execute any commands--compilers, linkers, etc.--that + are not in these default locations, + you need to set the &PATH; value + in the &cv-ENV; dictionary + in your construction environment. + + + + + + The simplest way to do this is to initialize explicitly + the value when you create the construction environment; + this is one way to do that: + + + + + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + + + + + Assign a dictionary to the &cv-ENV; + construction variable in this way + completely resets the external environment + so that the only variable that will be + set when external commands are executed + will be the &PATH; value. + If you want to use the rest of + the values in &cv-ENV; and only + set the value of &PATH;, + the most straightforward way is probably: + + + + + env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] + + + + + Note that &SCons; does allow you to define + the directories in the &PATH; in a string, + separated by the pathname-separator character + for your system (':' on POSIX systems, ';' on Windows): + + + + + env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' + + + + + But doing so makes your &SConscript; file less portable, + (although in this case that may not be a huge concern + since the directories you list are likley system-specific, anyway). + + + + + +
+ Propagating &PATH; From the External Environment + + + + You may want to propagate the external &PATH; + to the execution environment for commands. + You do this by initializing the &PATH; + variable with the &PATH; value from + the os.environ + dictionary, + which is Python's way of letting you + get at the external environment: + + + + + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + + + + + Alternatively, you may find it easier + to just propagate the entire external + environment to the execution environment + for commands. + This is simpler to code than explicity + selecting the &PATH; value: + + + + + import os + env = Environment(ENV = os.environ) + + + + + Either of these will guarantee that + &SCons; will be able to execute + any command that you can execute from the command line. + The drawback is that the build can behave + differently if it's run by people with + different &PATH; values in their environment--for example, + if both the /bin and + /usr/local/bin directories + have different &cc; commands, + then which one will be used to compile programs + will depend on which directory is listed + first in the user's &PATH; variable. + + + +
+ +
+ Adding to <varname>PATH</varname> Values in the Execution Environment + + + + One of the most common requirements + for manipulating a variable in the execution environment + is to add one or more custom directories to a search + like the $PATH variable on Linux or POSIX systems, + or the %PATH% variable on Windows, + so that a locally-installed compiler or other utility + can be found when &SCons; tries to execute it to update a target. + &SCons; provides &PrependENVPath; and &AppendENVPath; functions + to make adding things to execution variables convenient. + You call these functions by specifying the variable + to which you want the value added, + and then value itself. + So to add some /usr/local directories + to the $PATH and $LIB variables, + you might: + + + + + env = Environment(ENV = os.environ) + env.PrependENVPath('PATH', '/usr/local/bin') + env.AppendENVPath('LIB', '/usr/local/lib') + + + + + Note that the added values are strings, + and if you want to add multiple directories to + a variable like $PATH, + you must include the path separate character + (: on Linux or POSIX, + ; on Windows) + in the string. + + + +
+ +
diff --git a/doc/user/environments.xml b/doc/user/environments.xml new file mode 100644 index 0000000..b9a60b9 --- /dev/null +++ b/doc/user/environments.xml @@ -0,0 +1,1684 @@ + + + + + + + An environment + is a collection of values that + can affect how a program executes. + &SCons; distinguishes between three + different types of environments + that can affect the behavior of &SCons; itself + (subject to the configuration in the &SConscript; files), + as well as the compilers and other tools it executes: + + + + + + + External Environment + + + + + The external environment + is the set of variables in the user's environment + at the time the user runs &SCons;. + These variables are available within the &SConscript; files + through the Python os.environ dictionary. + See , below. + + + + + + + &ConsEnv; + + + + + A &consenv; + is a distinct object creating within + a &SConscript; file and + and which contains values that + affect how &SCons; decides + what action to use to build a target, + and even to define which targets + should be built from which sources. + One of the most powerful features of &SCons; + is the ability to create multiple &consenvs;, + including the ability to clone a new, customized + &consenv; from an existing &consenv;. + See , below. + + + + + + + Execution Environment + + + + + An execution environment + is the values that &SCons; sets + when executing an external + command (such as a compiler or linker) + to build one or more targets. + Note that this is not the same as + the external environment + (see above). + See , below. + + + + + + + + + + Unlike &Make;, &SCons; does not automatically + copy or import values between different environments + (with the exception of explicit clones of &consenvs;, + which inherit values from their parent). + This is a deliberate design choice + to make sure that builds are, + by default, repeatable regardless of + the values in the user's external environment. + This avoids a whole class of problems with builds + where a developer's local build works + because a custom variable setting + causes a different compiler or build option to be used, + but the checked-in change breaks the official build + because it uses different environment variable settings. + + + + + + Note that the &SConscript; writer can + easily arrange for variables to be + copied or imported between environments, + and this is often very useful + (or even downright necessary) + to make it easy for developers + to customize the build in appropriate ways. + The point is not + that copying variables between different environments + is evil and must always be avoided. + Instead, it should be up to the + implementer of the build system + to make conscious choices + about how and when to import + a variable from one environment to another, + making informed decisions about + striking the right balance + between making the build + repeatable on the one hand + and convenient to use on the other. + + + +
+ Using Values From the External Environment + + + + The external environment + variable settings that + the user has in force + when executing &SCons; + are available through the normal Python + os.environ + dictionary. + This means that you must add an + import os statement + to any &SConscript; file + in which you want to use + values from the user's external environment. + + + + + import os + + + + + More usefully, you can use the + os.environ + dictionary in your &SConscript; + files to initialize &consenvs; + with values from the user's external environment. + See the next section, + , + for information on how to do this. + + + +
+ +
+ Construction Environments + + + + It is rare that all of the software in a large, + complicated system needs to be built the same way. + For example, different source files may need different options + enabled on the command line, + or different executable programs need to be linked + with different libraries. + &SCons; accommodates these different build + requirements by allowing you to create and + configure multiple &consenvs; + that control how the software is built. + A &consenv; is an object + that has a number of associated + &consvars;, each with a name and a value. + (A construction environment also has an attached + set of &Builder; methods, + about which we'll learn more later.) + + + +
+ Creating a &ConsEnv;: the &Environment; Function + + + + A &consenv; is created by the &Environment; method: + + + + + env = Environment() + + + + + By default, &SCons; initializes every + new construction environment + with a set of &consvars; + based on the tools that it finds on your system, + plus the default set of builder methods + necessary for using those tools. + The construction variables + are initialized with values describing + the C compiler, + the Fortran compiler, + the linker, + etc., + as well as the command lines to invoke them. + + + + + + When you initialize a construction environment + you can set the values of the + environment's &consvars; + to control how a program is built. + For example: + + + + + import os + + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') + + env.Program('foo.c') + + + + + The construction environment in this example + is still initialized with the same default + construction variable values, + except that the user has explicitly specified use of the + GNU C compiler &gcc;, + and further specifies that the -O2 + (optimization level two) + flag should be used when compiling the object file. + In other words, the explicit initializations of + &cv-link-CC; and &cv-link-CCFLAGS; + override the default values in the newly-created + construction environment. + So a run from this example would look like: + + + + + % scons -Q + gcc -o foo.o -c -O2 foo.c + gcc -o foo foo.o + + +
+ +
+ Fetching Values From a &ConsEnv; + + + + You can fetch individual construction variables + using the normal syntax + for accessing individual named items in a Python dictionary: + + + + + env = Environment() + print "CC is:", env['CC'] + + + + + This example &SConstruct; file doesn't build anything, + but because it's actually a Python script, + it will print the value of &cv-link-CC; for us: + + + + + % scons -Q + CC is: cc + scons: `.' is up to date. + + + + + A construction environment, however, + is actually an object with associated methods, etc. + If you want to have direct access to only the + dictionary of construction variables, + you can fetch this using the &Dictionary; method: + + + + + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: + print "key = %s, value = %s" % (key, dict[key]) + + + + + This &SConstruct; file + will print the specified dictionary items for us on POSIX + systems as follows: + + + + + % scons -Q + key = OBJSUFFIX, value = .o + key = LIBSUFFIX, value = .a + key = PROGSUFFIX, value = + scons: `.' is up to date. + + + + + And on Windows: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + key = OBJSUFFIX, value = .obj + key = LIBSUFFIX, value = .lib + key = PROGSUFFIX, value = .exe + scons: `.' is up to date. + + + + + If you want to loop and print the values of + all of the construction variables in a construction environment, + the Python code to do that in sorted order might look something like: + + + + + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + + +
+ +
+ Expanding Values From a &ConsEnv;: the &subst; Method + + + + Another way to get information from + a construction environment. + is to use the &subst; method + on a string containing $ expansions + of construction variable names. + As a simple example, + the example from the previous + section that used + env['CC'] + to fetch the value of &cv-link-CC; + could also be written as: + + + + + env = Environment() + print "CC is:", env.subst('$CC') + + + + + One advantage of using + &subst; to expand strings is + that construction variables + in the result get re-expanded until + there are no expansions left in the string. + So a simple fetch of a value like + &cv-link-CCCOM;: + + + + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + + + + + Will print the unexpanded value of &cv-CCCOM;, + showing us the construction + variables that still need to be expanded: + + + + + % scons -Q + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + scons: `.' is up to date. + + + + + Calling the &subst; method on $CCOM, + however: + + + + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + + + + + Will recursively expand all of + the construction variables prefixed + with $ (dollar signs), + showing us the final output: + + + + + % scons -Q + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + + + + + Note that because we're not expanding this + in the context of building something + there are no target or source files + for &cv-link-TARGET; and &cv-link-SOURCES; to expand. + + + +
+ +
+ Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function + + + + All of the &Builder; functions that we've introduced so far, + like &Program; and &Library;, + actually use a default &consenv; + that contains settings + for the various compilers + and other tools that + &SCons; configures by default, + or otherwise knows about + and has discovered on your system. + The goal of the default construction environment + is to make many configurations to "just work" + to build software using + readily available tools + with a minimum of configuration changes. + + + + + + You can, however, control the settings + in the default contstruction environment + by using the &DefaultEnvironment; function + to initialize various settings: + + + + + + DefaultEnvironment(CC = '/usr/local/bin/gcc') + + + + + + When configured as above, + all calls to the &Program; + or &Object; Builder + will build object files with the + /usr/local/bin/gcc + compiler. + + + + + + Note that the &DefaultEnvironment; function + returns the initialized + default construction environment object, + which can then be manipulated like any + other construction environment. + So the following + would be equivalent to the + previous example, + setting the &cv-CC; + variable to /usr/local/bin/gcc + but as a separate step after + the default construction environment has been initialized: + + + + + + env = DefaultEnvironment() + env['CC'] = '/usr/local/bin/gcc' + + + + + + One very common use of the &DefaultEnvironment; function + is to speed up &SCons; initialization. + As part of trying to make most default + configurations "just work," + &SCons; will actually + search the local system for installed + compilers and other utilities. + This search can take time, + especially on systems with + slow or networked file systems. + If you know which compiler(s) and/or + other utilities you want to configure, + you can control the search + that &SCons; performs + by specifying some specific + tool modules with which to + initialize the default construction environment: + + + + + + env = DefaultEnvironment(tools = ['gcc', 'gnulink'], + CC = '/usr/local/bin/gcc') + + + + + + So the above example would tell &SCons; + to explicitly configure the default environment + to use its normal GNU Compiler and GNU Linker settings + (without having to search for them, + or any other utilities for that matter), + and specifically to use the compiler found at + /usr/local/bin/gcc. + + + +
+ +
+ Multiple &ConsEnvs; + + + + The real advantage of construction environments + is that you can create as many different construction + environments as you need, + each tailored to a different way to build + some piece of software or other file. + If, for example, we need to build + one program with the -O2 flag + and another with the -g (debug) flag, + we would do this like so: + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('bar', 'bar.c') + + + + % scons -Q + cc -o bar.o -c -g bar.c + cc -o bar bar.o + cc -o foo.o -c -O2 foo.c + cc -o foo foo.o + + + + + We can even use multiple construction environments to build + multiple versions of a single program. + If you do this by simply trying to use the + &b-link-Program; builder with both environments, though, + like this: + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('foo', 'foo.c') + + + + + Then &SCons; generates the following error: + + + + + % scons -Q + + scons: warning: Two different environments were specified for target foo.o, + but they appear to have the same action: CCCom(target, source, env) + File "/home/my/project/SConstruct", line 6, in ? + + scons: warning: Two different environments were specified for target foo, + but they appear to have the same action: Cat(target, source, env) + File "/home/my/project/SConstruct", line 6, in ? + cc -o foo.o -c -g foo.c + cc -o foo foo.o + + + + + This is because the two &b-Program; calls have + each implicitly told &SCons; to generate an object file named + foo.o, + one with a &cv-link-CCFLAGS; value of + -O2 + and one with a &cv-link-CCFLAGS; value of + -g. + &SCons; can't just decide that one of them + should take precedence over the other, + so it generates the error. + To avoid this problem, + we must explicitly specify + that each environment compile + foo.c + to a separately-named object file + using the &b-link-Object; builder, like so: + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + + + + + Notice that each call to the &b-Object; builder + returns a value, + an internal &SCons; object that + represents the object file that will be built. + We then use that object + as input to the &b-Program; builder. + This avoids having to specify explicitly + the object file name in multiple places, + and makes for a compact, readable + &SConstruct; file. + Our &SCons; output then looks like: + + + + + % scons -Q + cc -o foo-dbg.o -c -g foo.c + cc -o foo-dbg foo-dbg.o + cc -o foo-opt.o -c -O2 foo.c + cc -o foo-opt foo-opt.o + + +
+ +
+ Making Copies of &ConsEnvs;: the &Clone; Method + + + + Sometimes you want more than one construction environment + to share the same values for one or more variables. + Rather than always having to repeat all of the common + variables when you create each construction environment, + you can use the &Clone; method + to create a copy of a construction environment. + + + + + + Like the &Environment; call that creates a construction environment, + the &Clone; method takes &consvar; assignments, + which will override the values in the copied construction environment. + For example, suppose we want to use &gcc; + to create three versions of a program, + one optimized, one debug, and one with neither. + We could do this by creating a "base" construction environment + that sets &cv-link-CC; to &gcc;, + and then creating two copies, + one which sets &cv-link-CCFLAGS; for optimization + and the other which sets &cv-CCFLAGS; for debugging: + + + + + env = Environment(CC = 'gcc') + opt = env.Clone(CCFLAGS = '-O2') + dbg = env.Clone(CCFLAGS = '-g') + + env.Program('foo', 'foo.c') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + + + + + Then our output would look like: + + + + + % scons -Q + gcc -o foo.o -c foo.c + gcc -o foo foo.o + gcc -o foo-dbg.o -c -g foo.c + gcc -o foo-dbg foo-dbg.o + gcc -o foo-opt.o -c -O2 foo.c + gcc -o foo-opt foo-opt.o + + +
+ +
+ Replacing Values: the &Replace; Method + + + + You can replace existing construction variable values + using the &Replace; method: + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('foo.c') + + + + + The replacing value + (-DDEFINE2 in the above example) + completely replaces the value in the + construction environment: + + + + + % scons -Q + cc -o foo.o -c -DDEFINE2 foo.c + cc -o foo foo.o + + + + + You can safely call &Replace; + for construction variables that + don't exist in the construction environment: + + + + + env = Environment() + env.Replace(NEW_VARIABLE = 'xyzzy') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + In this case, + the construction variable simply + gets added to the construction environment: + + + + + % scons -Q + NEW_VARIABLE = xyzzy + scons: `.' is up to date. + + + + + Because the variables + aren't expanded until the construction environment + is actually used to build the targets, + and because &SCons; function and method calls + are order-independent, + the last replacement "wins" + and is used to build all targets, + regardless of the order in which + the calls to Replace() are + interspersed with calls to + builder methods: + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('foo.c') + + env.Replace(CCFLAGS = '-DDEFINE2') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('bar.c') + + + + + The timing of when the replacement + actually occurs relative + to when the targets get built + becomes apparent + if we run &scons; without the -Q + option: + + + + + % scons + scons: Reading SConscript files ... + CCFLAGS = -DDEFINE1 + CCFLAGS = -DDEFINE2 + scons: done reading SConscript files. + scons: Building targets ... + cc -o bar.o -c -DDEFINE2 bar.c + cc -o bar bar.o + cc -o foo.o -c -DDEFINE2 foo.c + cc -o foo foo.o + scons: done building targets. + + + + + Because the replacement occurs while + the &SConscript; files are being read, + the &cv-link-CCFLAGS; + variable has already been set to + -DDEFINE2 + by the time the &foo_o; target is built, + even though the call to the &Replace; + method does not occur until later in + the &SConscript; file. + + + +
+ +
+ Setting Values Only If They're Not Already Defined: the &SetDefault; Method + + + + Sometimes it's useful to be able to specify + that a construction variable should be + set to a value only if the construction environment + does not already have that variable defined + You can do this with the &SetDefault; method, + which behaves similarly to the set_default + method of Python dictionary objects: + + + + + env.SetDefault(SPECIAL_FLAG = '-extra-option') + + + + + This is especially useful + when writing your own Tool modules + to apply variables to construction environments. + + + + +
+ +
+ Appending to the End of Values: the &Append; Method + + + + You can append a value to + an existing construction variable + using the &Append; method: + + + + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Append(CCFLAGS = ['-DLAST']) + env.Program('foo.c') + + + + + &SCons; then supplies both the -DMY_VALUE and + -DLAST flags when compiling the object file: + + + + + % scons -Q + cc -o foo.o -c -DMY_VALUE -DLAST foo.c + cc -o foo foo.o + + + + + If the construction variable doesn't already exist, + the &Append; method will create it: + + + + + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + Which yields: + + + + + % scons -Q + NEW_VARIABLE = added + scons: `.' is up to date. + + + + + Note that the &Append; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. + + + +
+ +
+ Appending Unique Values: the &AppendUnique; Method + + + + Some times it's useful to add a new value + only if the existing construction variable + doesn't already contain the value. + This can be done using the &AppendUnique; method: + + + + + env.AppendUnique(CCFLAGS=['-g']) + + + + + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. + + + +
+ +
+ Appending to the Beginning of Values: the &Prepend; Method + + + + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: + + + + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Prepend(CCFLAGS = ['-DFIRST']) + env.Program('foo.c') + + + + + &SCons; then supplies both the -DFIRST and + -DMY_VALUE flags when compiling the object file: + + + + + % scons -Q + cc -o foo.o -c -DFIRST -DMY_VALUE foo.c + cc -o foo foo.o + + + + + If the construction variable doesn't already exist, + the &Prepend; method will create it: + + + + + env = Environment() + env.Prepend(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + Which yields: + + + + + % scons -Q + NEW_VARIABLE = added + scons: `.' is up to date. + + + + + Like the &Append; function, + the &Prepend; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. + + + +
+ +
+ Prepending Unique Values: the &PrependUnique; Method + + + + Some times it's useful to add a new value + to the beginning of a construction variable + only if the existing value + doesn't already contain the to-be-added value. + This can be done using the &PrependUnique; method: + + + + + env.PrependUnique(CCFLAGS=['-g']) + + + + + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. + + + +
+ +
+ +
+ Controlling the Execution Environment for Issued Commands + + + + When &SCons; builds a target file, + it does not execute the commands with + the same external environment + that you used to execute &SCons;. + Instead, it uses the dictionary + stored in the &cv-link-ENV; construction variable + as the external environment + for executing commands. + + + + + + The most important ramification of this behavior + is that the &PATH; environment variable, + which controls where the operating system + will look for commands and utilities, + is not the same as in the external environment + from which you called &SCons;. + This means that &SCons; will not, by default, + necessarily find all of the tools + that you can execute from the command line. + + + + + + The default value of the &PATH; environment variable + on a POSIX system + is /usr/local/bin:/bin:/usr/bin. + The default value of the &PATH; environment variable + on a Windows system comes from the Windows registry + value for the command interpreter. + If you want to execute any commands--compilers, linkers, etc.--that + are not in these default locations, + you need to set the &PATH; value + in the &cv-ENV; dictionary + in your construction environment. + + + + + + The simplest way to do this is to initialize explicitly + the value when you create the construction environment; + this is one way to do that: + + + + + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + + + + + Assign a dictionary to the &cv-ENV; + construction variable in this way + completely resets the external environment + so that the only variable that will be + set when external commands are executed + will be the &PATH; value. + If you want to use the rest of + the values in &cv-ENV; and only + set the value of &PATH;, + the most straightforward way is probably: + + + + + env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] + + + + + Note that &SCons; does allow you to define + the directories in the &PATH; in a string, + separated by the pathname-separator character + for your system (':' on POSIX systems, ';' on Windows): + + + + + env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' + + + + + But doing so makes your &SConscript; file less portable, + (although in this case that may not be a huge concern + since the directories you list are likley system-specific, anyway). + + + + + +
+ Propagating &PATH; From the External Environment + + + + You may want to propagate the external &PATH; + to the execution environment for commands. + You do this by initializing the &PATH; + variable with the &PATH; value from + the os.environ + dictionary, + which is Python's way of letting you + get at the external environment: + + + + + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + + + + + Alternatively, you may find it easier + to just propagate the entire external + environment to the execution environment + for commands. + This is simpler to code than explicity + selecting the &PATH; value: + + + + + import os + env = Environment(ENV = os.environ) + + + + + Either of these will guarantee that + &SCons; will be able to execute + any command that you can execute from the command line. + The drawback is that the build can behave + differently if it's run by people with + different &PATH; values in their environment--for example, + if both the /bin and + /usr/local/bin directories + have different &cc; commands, + then which one will be used to compile programs + will depend on which directory is listed + first in the user's &PATH; variable. + + + +
+ +
+ Adding to <varname>PATH</varname> Values in the Execution Environment + + + + One of the most common requirements + for manipulating a variable in the execution environment + is to add one or more custom directories to a search + like the $PATH variable on Linux or POSIX systems, + or the %PATH% variable on Windows, + so that a locally-installed compiler or other utility + can be found when &SCons; tries to execute it to update a target. + &SCons; provides &PrependENVPath; and &AppendENVPath; functions + to make adding things to execution variables convenient. + You call these functions by specifying the variable + to which you want the value added, + and then value itself. + So to add some /usr/local directories + to the $PATH and $LIB variables, + you might: + + + + + env = Environment(ENV = os.environ) + env.PrependENVPath('PATH', '/usr/local/bin') + env.AppendENVPath('LIB', '/usr/local/lib') + + + + + Note that the added values are strings, + and if you want to add multiple directories to + a variable like $PATH, + you must include the path separate character + (: on Linux or POSIX, + ; on Windows) + in the string. + + + +
+ +
diff --git a/doc/user/errors.in b/doc/user/errors.in new file mode 100644 index 0000000..0051953 --- /dev/null +++ b/doc/user/errors.in @@ -0,0 +1,41 @@ + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/errors.xml b/doc/user/errors.xml new file mode 100644 index 0000000..0051953 --- /dev/null +++ b/doc/user/errors.xml @@ -0,0 +1,41 @@ + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/example.in b/doc/user/example.in new file mode 100644 index 0000000..0051953 --- /dev/null +++ b/doc/user/example.in @@ -0,0 +1,41 @@ + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/example.xml b/doc/user/example.xml new file mode 100644 index 0000000..0051953 --- /dev/null +++ b/doc/user/example.xml @@ -0,0 +1,41 @@ + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/factories.in b/doc/user/factories.in new file mode 100644 index 0000000..0cea6c4 --- /dev/null +++ b/doc/user/factories.in @@ -0,0 +1,507 @@ + + + + + &SCons; provides a number of platform-independent functions, + called factories, + that perform common file system manipulations + like copying, moving or deleting files and directories, + or making directories. + These functions are factories + because they don't perform the action + at the time they're called, + they each return an &Action; object + that can be executed at the appropriate time. + + + +
+ Copying Files or Directories: The &Copy; Factory + + + + Suppose you want to arrange to make a copy of a file, + and don't have a suitable pre-existing builder. + + + Unfortunately, in the early days of SCons design, + we used the name &Copy; for the function that + returns a copy of the environment, + otherwise that would be the logical choice for + a Builder that copies a file or directory tree + to a target location. + + + One way would be to use the &Copy; action factory + in conjunction with the &Command; builder: + + + + + + Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) + + file.in + + + + + Notice that the action returned by the &Copy; factory + will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings + at the time &file_out; is built, + and that the order of the arguments + is the same as that of a builder itself--that is, + target first, followed by source: + + + + + scons -Q + + + + + You can, of course, name a file explicitly + instead of using &cv-TARGET; or &cv-SOURCE;: + + + + + + Command("file.out", [], Copy("$TARGET", "file.in")) + + file.in + + + + + Which executes as: + + + + + scons -Q + + + + + The usefulness of the &Copy; factory + becomes more apparent when + you use it in a list of actions + passed to the &Command; builder. + For example, suppose you needed to run a + file through a utility that only modifies files in-place, + and can't "pipe" input to output. + One solution is to copy the source file + to a temporary file name, + run the utility, + and then copy the modified temporary file to the target, + which the &Copy; factory makes extremely easy: + + + + + + Command("file.out", "file.in", + [ + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Copy("$TARGET", "tempfile"), + ]) + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + file.in + + touch $* + + + + + + The output then looks like: + + + + + scons -Q + + +
+ +
+ Deleting Files or Directories: The &Delete; Factory + + + + If you need to delete a file, + then the &Delete; factory + can be used in much the same way as + the &Copy; factory. + For example, if we want to make sure that + the temporary file + in our last example doesn't exist before + we copy to it, + we could add &Delete; to the beginning + of the command list: + + + + + + Command("file.out", "file.in", + [ + Delete("tempfile"), + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Copy("$TARGET", "tempfile"), + ]) + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + file.in + + touch $* + + + + + + Which then executes as follows: + + + + + scons -Q + + + + + Of course, like all of these &Action; factories, + the &Delete factory also expands + &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. + For example: + + + + + + Command("file.out", "file.in", + [ + Delete("$TARGET"), + Copy("$TARGET", "$SOURCE") + ]) + + file.in + + + + + Executes as: + + + + + scons -Q + + + + + Note, however, that you typically don't need to + call the &Delete; factory explicitly in this way; + by default, &SCons; deletes its target(s) + for you before executing any action. + + + + + + One word of caution about using the &Delete; factory: + it has the same variable expansions available + as any other factory, including the &cv-SOURCE; variable. + Specifying Delete("$SOURCE") + is not something you usually want to do! + + + +
+ +
+ Moving (Renaming) Files or Directories: The &Move; Factory + + + + The &Move; factory + allows you to rename a file or directory. + For example, if we don't want to copy the temporary file, + we could use: + + + + + + Command("file.out", "file.in", + [ + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Move("$TARGET", "tempfile"), + ]) + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + file.in + + touch $* + + + + + + Which would execute as: + + + + + scons -Q + + +
+ +
+ Updating the Modification Time of a File: The &Touch; Factory + + + + If you just need to update the + recorded modification time for a file, + use the &Touch; factory: + + + + + + Command("file.out", "file.in", + [ + Copy("$TARGET", "$SOURCE"), + Touch("$TARGET"), + ]) + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + file.in + + + + + Which executes as: + + + + + scons -Q + + +
+ +
+ Creating a Directory: The &Mkdir; Factory + + + + If you need to create a directory, + use the &Mkdir; factory. + For example, if we need to process + a file in a temporary directory + in which the processing tool + will create other files that we don't care about, + you could use: + + + + + + Command("file.out", "file.in", + [ + Delete("tempdir"), + Mkdir("tempdir"), + Copy("tempdir/${SOURCE.file}", "$SOURCE"), + "process tempdir", + Move("$TARGET", "tempdir/output_file"), + Delete("tempdir"), + ]) + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + file.in + + touch $* + + + + + + Which executes as: + + + + + scons -Q + + +
+ +
+ Changing File or Directory Permissions: The &Chmod; Factory + + + + To change permissions on a file or directory, + use the &Chmod; factory. + The permission argument uses POSIX-style + permission bits and should typically + be expressed as an octal, + not decimal, number: + + + + + + Command("file.out", "file.in", + [ + Copy("$TARGET", "$SOURCE"), + Chmod("$TARGET", 0755), + ]) + + file.in + + + + + Which executes: + + + + + scons -Q + + +
+ +
+ Executing an action immediately: the &Execute; Function + + + + We've been showing you how to use &Action; factories + in the &Command; function. + You can also execute an &Action; returned by a factory + (or actually, any &Action;) + at the time the &SConscript; file is read + by using the &Execute; function. + For example, if we need to make sure that + a directory exists before we build any targets, + + + + + + Execute(Mkdir('__ROOT__/tmp/my_temp_directory')) + + + + + + Notice that this will + create the directory while + the &SConscript; file is being read: + + + + + scons + + + + + If you're familiar with Python, + you may wonder why you would want to use this + instead of just calling the native Python + os.mkdir() function. + The advantage here is that the &Mkdir; + action will behave appropriately if the user + specifies the &SCons; or + options--that is, + it will print the action but not actually + make the directory when is specified, + or make the directory but not print the action + when is specified. + + + + + + The &Execute; function returns the exit status + or return value of the underlying action being executed. + It will also print an error message if the action + fails and returns a non-zero value. + &SCons; will not, however, + actually stop the build if the action fails. + If you want the build to stop + in response to a failure in an action called by &Execute;, + you must do so by explicitly + checking the return value + and calling the &Exit; function + (or a Python equivalent): + + + + + if Execute(Mkdir('__ROOT__/tmp/my_temp_directory')): + # A problem occurred while making the temp directory. + Exit(1) + + +
diff --git a/doc/user/factories.xml b/doc/user/factories.xml new file mode 100644 index 0000000..17e52bd --- /dev/null +++ b/doc/user/factories.xml @@ -0,0 +1,466 @@ + + + + + &SCons; provides a number of platform-independent functions, + called factories, + that perform common file system manipulations + like copying, moving or deleting files and directories, + or making directories. + These functions are factories + because they don't perform the action + at the time they're called, + they each return an &Action; object + that can be executed at the appropriate time. + + + +
+ Copying Files or Directories: The &Copy; Factory + + + + Suppose you want to arrange to make a copy of a file, + and don't have a suitable pre-existing builder. + + + Unfortunately, in the early days of SCons design, + we used the name &Copy; for the function that + returns a copy of the environment, + otherwise that would be the logical choice for + a Builder that copies a file or directory tree + to a target location. + + + One way would be to use the &Copy; action factory + in conjunction with the &Command; builder: + + + + + Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) + + + + + Notice that the action returned by the &Copy; factory + will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings + at the time &file_out; is built, + and that the order of the arguments + is the same as that of a builder itself--that is, + target first, followed by source: + + + + + % scons -Q + Copy("file.out", "file.in") + + + + + You can, of course, name a file explicitly + instead of using &cv-TARGET; or &cv-SOURCE;: + + + + + Command("file.out", [], Copy("$TARGET", "file.in")) + + + + + Which executes as: + + + + + % scons -Q + Copy("file.out", "file.in") + + + + + The usefulness of the &Copy; factory + becomes more apparent when + you use it in a list of actions + passed to the &Command; builder. + For example, suppose you needed to run a + file through a utility that only modifies files in-place, + and can't "pipe" input to output. + One solution is to copy the source file + to a temporary file name, + run the utility, + and then copy the modified temporary file to the target, + which the &Copy; factory makes extremely easy: + + + + + Command("file.out", "file.in", + [ + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Copy("$TARGET", "tempfile"), + ]) + + + + + The output then looks like: + + + + + % scons -Q + Copy("tempfile", "file.in") + modify tempfile + Copy("file.out", "tempfile") + + +
+ +
+ Deleting Files or Directories: The &Delete; Factory + + + + If you need to delete a file, + then the &Delete; factory + can be used in much the same way as + the &Copy; factory. + For example, if we want to make sure that + the temporary file + in our last example doesn't exist before + we copy to it, + we could add &Delete; to the beginning + of the command list: + + + + + Command("file.out", "file.in", + [ + Delete("tempfile"), + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Copy("$TARGET", "tempfile"), + ]) + + + + + Which then executes as follows: + + + + + % scons -Q + Delete("tempfile") + Copy("tempfile", "file.in") + modify tempfile + Copy("file.out", "tempfile") + + + + + Of course, like all of these &Action; factories, + the &Delete; factory also expands + &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. + For example: + + + + + Command("file.out", "file.in", + [ + Delete("$TARGET"), + Copy("$TARGET", "$SOURCE") + ]) + + + + + Executes as: + + + + + % scons -Q + Delete("file.out") + Copy("file.out", "file.in") + + + + + Note, however, that you typically don't need to + call the &Delete; factory explicitly in this way; + by default, &SCons; deletes its target(s) + for you before executing any action. + + + + + + One word of caution about using the &Delete; factory: + it has the same variable expansions available + as any other factory, including the &cv-SOURCE; variable. + Specifying Delete("$SOURCE") + is not something you usually want to do! + + + +
+ +
+ Moving (Renaming) Files or Directories: The &Move; Factory + + + + The &Move; factory + allows you to rename a file or directory. + For example, if we don't want to copy the temporary file, + we could use: + + + + + Command("file.out", "file.in", + [ + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Move("$TARGET", "tempfile"), + ]) + + + + + Which would execute as: + + + + + % scons -Q + Copy("tempfile", "file.in") + modify tempfile + Move("file.out", "tempfile") + + +
+ +
+ Updating the Modification Time of a File: The &Touch; Factory + + + + If you just need to update the + recorded modification time for a file, + use the &Touch; factory: + + + + + Command("file.out", "file.in", + [ + Copy("$TARGET", "$SOURCE"), + Touch("$TARGET"), + ]) + + + + + Which executes as: + + + + + % scons -Q + Copy("file.out", "file.in") + Touch("file.out") + + +
+ +
+ Creating a Directory: The &Mkdir; Factory + + + + If you need to create a directory, + use the &Mkdir; factory. + For example, if we need to process + a file in a temporary directory + in which the processing tool + will create other files that we don't care about, + you could use: + + + + + Command("file.out", "file.in", + [ + Delete("tempdir"), + Mkdir("tempdir"), + Copy("tempdir/${SOURCE.file}", "$SOURCE"), + "process tempdir", + Move("$TARGET", "tempdir/output_file"), + Delete("tempdir"), + ]) + + + + + Which executes as: + + + + + % scons -Q + Delete("tempdir") + Mkdir("tempdir") + Copy("tempdir/file.in", "file.in") + process tempdir + Move("file.out", "tempdir/output_file") + scons: *** [file.out] tempdir/output_file: No such file or directory + + +
+ +
+ Changing File or Directory Permissions: The &Chmod; Factory + + + + To change permissions on a file or directory, + use the &Chmod; factory. + The permission argument uses POSIX-style + permission bits and should typically + be expressed as an octal, + not decimal, number: + + + + + Command("file.out", "file.in", + [ + Copy("$TARGET", "$SOURCE"), + Chmod("$TARGET", 0755), + ]) + + + + + Which executes: + + + + + % scons -Q + Copy("file.out", "file.in") + Chmod("file.out", 0755) + + +
+ +
+ Executing an action immediately: the &Execute; Function + + + + We've been showing you how to use &Action; factories + in the &Command; function. + You can also execute an &Action; returned by a factory + (or actually, any &Action;) + at the time the &SConscript; file is read + by using the &Execute; function. + For example, if we need to make sure that + a directory exists before we build any targets, + + + + + Execute(Mkdir('/tmp/my_temp_directory')) + + + + + Notice that this will + create the directory while + the &SConscript; file is being read: + + + + + % scons + scons: Reading SConscript files ... + Mkdir("/tmp/my_temp_directory") + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + If you're familiar with Python, + you may wonder why you would want to use this + instead of just calling the native Python + os.mkdir() function. + The advantage here is that the &Mkdir; + action will behave appropriately if the user + specifies the &SCons; or + options--that is, + it will print the action but not actually + make the directory when is specified, + or make the directory but not print the action + when is specified. + + + + + + The &Execute; function returns the exit status + or return value of the underlying action being executed. + It will also print an error message if the action + fails and returns a non-zero value. + &SCons; will not, however, + actually stop the build if the action fails. + If you want the build to stop + in response to a failure in an action called by &Execute;, + you must do so by explicitly + checking the return value + and calling the &Exit; function + (or a Python equivalent): + + + + + if Execute(Mkdir('/tmp/my_temp_directory')): + # A problem occurred while making the temp directory. + Exit(1) + + +
diff --git a/doc/user/file-removal.in b/doc/user/file-removal.in new file mode 100644 index 0000000..c26f020 --- /dev/null +++ b/doc/user/file-removal.in @@ -0,0 +1,223 @@ + + + + + There are two occasions when &SCons; will, + by default, remove target files. + The first is when &SCons; determines that + an target file needs to be rebuilt + and removes the existing version of the target + before executing + The second is when &SCons; is invoked with the + -c option to "clean" + a tree of its built targets. + + These behaviours can be suppressed with the + &Precious; and &NoClean; functions, respectively. + + + +
+ Preventing target removal during build: the &Precious; Function + + + + By default, &SCons; removes targets before building them. + Sometimes, however, this is not what you want. + For example, you may want to update a library incrementally, + not by having it deleted and then rebuilt from all + of the constituent object files. + In such cases, you can use the + &Precious; method to prevent + &SCons; from removing the target before it is built: + + + + + + env = Environment(RANLIBCOM='') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Precious(lib) + + + int f1() { } + + + int f2() { } + + + int f3() { } + + + + + + Although the output doesn't look any different, + &SCons; does not, in fact, + delete the target library before rebuilding it: + + + + + scons -Q + + + + + &SCons; will, however, still delete files marked as &Precious; + when the -c option is used. + + + +
+ +
+ Preventing target removal during clean: the &NoClean; Function + + + + By default, &SCons; removes all built targets when invoked + with the -c option to clean a source tree + of built targets. + Sometimes, however, this is not what you want. + For example, you may want to remove only intermediate generated files + (such as object files), + but leave the final targets + (the libraries) + untouched. + + In such cases, you can use the &NoClean; method to prevent &SCons; + from removing a target during a clean: + + + + + + env = Environment(RANLIBCOM='') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.NoClean(lib) + + + int f1() { } + + + int f2() { } + + + int f3() { } + + + + + + Notice that the libfoo.a + is not listed as a removed file: + + + + + scons -Q + scons -c + + +
+ +
+ Removing additional files during clean: the &Clean; Function + + + + There may be additional files that you want removed + when the -c option is used, + but which &SCons; doesn't know about + because they're not normal target files. + For example, perhaps a command you invoke + creates a log file as + part of building the target file you want. + You would like the log file cleaned, + but you don't want to have to teach + SCons that the command + "builds" two files. + + + + + + You can use the &Clean; function to arrange for additional files + to be removed when the -c option is used. + Notice, however, that the &Clean; function takes two arguments, + and the second argument + is the name of the additional file you want cleaned + (foo.log in this example): + + + + + + t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') + Clean(t, 'foo.log') + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + + foo.in + + + foo.log + + + cat $3 > $2 + + + + + + The first argument is the target with which you want + the cleaning of this additional file associated. + In the above example, + we've used the return value from the + &Command; function, + which represents the + foo.out + target. + Now whenever the + foo.out target is cleaned + by the -c option, + the foo.log file + will be removed as well: + + + + + scons -Q + scons -Q -c + + +
diff --git a/doc/user/file-removal.xml b/doc/user/file-removal.xml new file mode 100644 index 0000000..542fd38 --- /dev/null +++ b/doc/user/file-removal.xml @@ -0,0 +1,202 @@ + + + + + There are two occasions when &SCons; will, + by default, remove target files. + The first is when &SCons; determines that + an target file needs to be rebuilt + and removes the existing version of the target + before executing + The second is when &SCons; is invoked with the + -c option to "clean" + a tree of its built targets. + + These behaviours can be suppressed with the + &Precious; and &NoClean; functions, respectively. + + + +
+ Preventing target removal during build: the &Precious; Function + + + + By default, &SCons; removes targets before building them. + Sometimes, however, this is not what you want. + For example, you may want to update a library incrementally, + not by having it deleted and then rebuilt from all + of the constituent object files. + In such cases, you can use the + &Precious; method to prevent + &SCons; from removing the target before it is built: + + + + + env = Environment(RANLIBCOM='') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Precious(lib) + + + + + Although the output doesn't look any different, + &SCons; does not, in fact, + delete the target library before rebuilding it: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o + + + + + &SCons; will, however, still delete files marked as &Precious; + when the -c option is used. + + + +
+ +
+ Preventing target removal during clean: the &NoClean; Function + + + + By default, &SCons; removes all built targets when invoked + with the -c option to clean a source tree + of built targets. + Sometimes, however, this is not what you want. + For example, you may want to remove only intermediate generated files + (such as object files), + but leave the final targets + (the libraries) + untouched. + + In such cases, you can use the &NoClean; method to prevent &SCons; + from removing a target during a clean: + + + + + env = Environment(RANLIBCOM='') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.NoClean(lib) + + + + + Notice that the libfoo.a + is not listed as a removed file: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o + % scons -c + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Cleaning targets ... + Removed f1.o + Removed f2.o + Removed f3.o + scons: done cleaning targets. + + +
+ +
+ Removing additional files during clean: the &Clean; Function + + + + There may be additional files that you want removed + when the -c option is used, + but which &SCons; doesn't know about + because they're not normal target files. + For example, perhaps a command you invoke + creates a log file as + part of building the target file you want. + You would like the log file cleaned, + but you don't want to have to teach + SCons that the command + "builds" two files. + + + + + + You can use the &Clean; function to arrange for additional files + to be removed when the -c option is used. + Notice, however, that the &Clean; function takes two arguments, + and the second argument + is the name of the additional file you want cleaned + (foo.log in this example): + + + + + t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') + Clean(t, 'foo.log') + + + + + The first argument is the target with which you want + the cleaning of this additional file associated. + In the above example, + we've used the return value from the + &Command; function, + which represents the + foo.out + target. + Now whenever the + foo.out target is cleaned + by the -c option, + the foo.log file + will be removed as well: + + + + + % scons -Q + build -o foo.out foo.in + % scons -Q -c + Removed foo.out + Removed foo.log + + +
diff --git a/doc/user/hierarchy.in b/doc/user/hierarchy.in new file mode 100644 index 0000000..a917d10 --- /dev/null +++ b/doc/user/hierarchy.in @@ -0,0 +1,794 @@ + + + + + + + The source code for large software projects + rarely stays in a single directory, + but is nearly always divided into a + hierarchy of directories. + Organizing a large software build using &SCons; + involves creating a hierarchy of build scripts + using the &SConscript; function. + + + +
+ &SConscript; Files + + + + As we've already seen, + the build script at the top of the tree is called &SConstruct;. + The top-level &SConstruct; file can + use the &SConscript; function to + include other subsidiary scripts in the build. + These subsidiary scripts can, in turn, + use the &SConscript; function + to include still other scripts in the build. + By convention, these subsidiary scripts are usually + named &SConscript;. + For example, a top-level &SConstruct; file might + arrange for four subsidiary scripts to be included + in the build as follows: + + + + + SConscript(['drivers/display/SConscript', + 'drivers/mouse/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + + + + + In this case, the &SConstruct; file + lists all of the &SConscript; files in the build explicitly. + (Note, however, that not every directory in the tree + necessarily has an &SConscript; file.) + Alternatively, the drivers + subdirectory might contain an intermediate + &SConscript; file, + in which case the &SConscript; call in + the top-level &SConstruct; file + would look like: + + + + + SConscript(['drivers/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + + + + + And the subsidiary &SConscript; file in the + drivers subdirectory + would look like: + + + + + SConscript(['display/SConscript', + 'mouse/SConscript']) + + + + + Whether you list all of the &SConscript; files in the + top-level &SConstruct; file, + or place a subsidiary &SConscript; file in + intervening directories, + or use some mix of the two schemes, + is up to you and the needs of your software. + + + +
+ +
+ Path Names Are Relative to the &SConscript; Directory + + + + Subsidiary &SConscript; files make it easy to create a build + hierarchy because all of the file and directory names + in a subsidiary &SConscript; files are interpreted + relative to the directory in which the &SConscript; file lives. + Typically, this allows the &SConscript; file containing the + instructions to build a target file + to live in the same directory as the source files + from which the target will be built, + making it easy to update how the software is built + whenever files are added or deleted + (or other changes are made). + + + + + + For example, suppose we want to build two programs + &prog1; and &prog2; in two separate directories + with the same names as the programs. + One typical way to do this would be + with a top-level &SConstruct; file like this: + + + + + + SConscript(['prog1/SConscript', + 'prog2/SConscript']) + + + env = Environment() + env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) + + + env = Environment() + env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) + + + + x + + + x + + + x + + + + x + + + x + + + x + + + + + + And subsidiary &SConscript; files that look like this: + + + + + + + + + And this: + + + + + + + + + Then, when we run &SCons; in the top-level directory, + our build looks like: + + + + + scons -Q + + + + + Notice the following: + + First, you can have files with the same names + in multiple directories, like main.c in the above example. + + Second, unlike standard recursive use of &Make;, + &SCons; stays in the top-level directory + (where the &SConstruct; file lives) + and issues commands that use the path names + from the top-level directory to the + target and source files within the hierarchy. + + + +
+ +
+ Top-Level Path Names in Subsidiary &SConscript; Files + + + + If you need to use a file from another directory, + it's sometimes more convenient to specify + the path to a file in another directory + from the top-level &SConstruct; directory, + even when you're using that file in + a subsidiary &SConscript; file in a subdirectory. + You can tell &SCons; to interpret a path name + as relative to the top-level &SConstruct; directory, + not the local directory of the &SConscript; file, + by appending a &hash; (hash mark) + to the beginning of the path name: + + + + + + SConscript('src/prog/SConscript') + + + env = Environment() + env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) + + + x + + + x + + + x + + + + + + In this example, + the lib directory is + directly underneath the top-level &SConstruct; directory. + If the above &SConscript; file is in a subdirectory + named src/prog, + the output would look like: + + + + + scons -Q + + + + + (Notice that the lib/foo1.o object file + is built in the same directory as its source file. + See , below, + for information about + how to build the object file in a different subdirectory.) + + + +
+ +
+ Absolute Path Names + + + + Of course, you can always specify + an absolute path name for a file--for example: + + + + + + SConscript('src/prog/SConscript') + + + env = Environment() + env.Program('prog', ['main.c', '__ROOT__/usr/joe/lib/foo1.c', 'foo2.c']) + + + x + + + x + + + x + + + + + + Which, when executed, would yield: + + + + + scons -Q + + + + + (As was the case with top-relative path names, + notice that the /usr/joe/lib/foo1.o object file + is built in the same directory as its source file. + See , below, + for information about + how to build the object file in a different subdirectory.) + + + +
+ +
+ Sharing Environments (and Other Variables) Between &SConscript; Files + + + + In the previous example, + each of the subsidiary &SConscript; files + created its own construction environment + by calling &Environment; separately. + This obviously works fine, + but if each program must be built + with the same construction variables, + it's cumbersome and error-prone to initialize + separate construction environments + in the same way over and over in each subsidiary + &SConscript; file. + + + + + + &SCons; supports the ability to export variables + from a parent &SConscript; file + to its subsidiary &SConscript; files, + which allows you to share common initialized + values throughout your build hierarchy. + + + +
+ Exporting Variables + + + + There are two ways to export a variable, + such as a construction environment, + from an &SConscript; file, + so that it may be used by other &SConscript; files. + First, you can call the &Export; + function with a list of variables, + or a string of white-space separated variable names. + Each call to &Export; adds one + or more variables to a global list + of variables that are available for import + by other &SConscript; files. + + + + + env = Environment() + Export('env') + + + + + You may export more than one variable name at a time: + + + + + env = Environment() + debug = ARGUMENTS['debug'] + Export('env', 'debug') + + + + + Because white space is not legal in Python variable names, + the &Export; function will even automatically split + a string into separate names for you: + + + + + Export('env debug') + + + + + Second, you can specify a list of + variables to export as a second argument + to the &SConscript; function call: + + + + + SConscript('src/SConscript', 'env') + + + + + Or as the &exports; keyword argument: + + + + + SConscript('src/SConscript', exports='env') + + + + + These calls export the specified variables + to only the listed &SConscript; files. + You may, however, specify more than one + &SConscript; file in a list: + + + + + SConscript(['src1/SConscript', + 'src2/SConscript'], exports='env') + + + + + This is functionally equivalent to + calling the &SConscript; function + multiple times with the same &exports; argument, + one per &SConscript; file. + + + +
+ +
+ Importing Variables + + + + Once a variable has been exported from a calling + &SConscript; file, + it may be used in other &SConscript; files + by calling the &Import; function: + + + + + Import('env') + env.Program('prog', ['prog.c']) + + + + + The &Import; call makes the env construction + environment available to the &SConscript; file, + after which the variable can be used to build + programs, libraries, etc. + + + + + + Like the &Export; function, + the &Import; function can be used + with multiple variable names: + + + + + Import('env', 'debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + And the &Import; function will similarly + split a string along white-space + into separate variable names: + + + + + Import('env debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + Lastly, as a special case, + you may import all of the variables that + have been exported by supplying an asterisk + to the &Import; function: + + + + + Import('*') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + If you're dealing with a lot of &SConscript; files, + this can be a lot simpler than keeping + arbitrary lists of imported variables in each file. + + + +
+ +
+ Returning Values From an &SConscript; File + + + + Sometimes, you would like to be able to + use information from a subsidiary + &SConscript file in some way. + For example, + suppose that you want to create one + library from source files + scattered throughout a number + of subsidiary &SConscript; files. + You can do this by using the &Return; + function to return values + from the subsidiary &SConscript; files + to the calling file. + + + + + + If, for example, we have two subdirectories + &foo; and &bar; + that should each contribute a source + file to a Library, + what we'd like to be able to do is + collect the object files + from the subsidiary &SConscript; calls + like this: + + + + + + env = Environment() + Export('env') + objs = [] + for subdir in ['foo', 'bar']: + o = SConscript('%s/SConscript' % subdir) + objs.append(o) + env.Library('prog', objs) + + + + + Import('env') + obj = env.Object('foo.c') + Return('obj') + + + Import('env') + obj = env.Object('bar.c') + Return('obj') + + + void foo(void) { printf("foo/foo.c\n"); } + + + void bar(void) { printf("bar/bar.c\n"); } + + + + + + We can do this by using the &Return; + function in the + foo/SConscript file like this: + + + + + + + + + (The corresponding + bar/SConscript + file should be pretty obvious.) + Then when we run &SCons;, + the object files from the subsidiary subdirectories + are all correctly archived in the desired library: + + + + + scons -Q + + + + +
+ +
+ + diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml new file mode 100644 index 0000000..3495c5f --- /dev/null +++ b/doc/user/hierarchy.xml @@ -0,0 +1,746 @@ + + + + + + + The source code for large software projects + rarely stays in a single directory, + but is nearly always divided into a + hierarchy of directories. + Organizing a large software build using &SCons; + involves creating a hierarchy of build scripts + using the &SConscript; function. + + + +
+ &SConscript; Files + + + + As we've already seen, + the build script at the top of the tree is called &SConstruct;. + The top-level &SConstruct; file can + use the &SConscript; function to + include other subsidiary scripts in the build. + These subsidiary scripts can, in turn, + use the &SConscript; function + to include still other scripts in the build. + By convention, these subsidiary scripts are usually + named &SConscript;. + For example, a top-level &SConstruct; file might + arrange for four subsidiary scripts to be included + in the build as follows: + + + + + SConscript(['drivers/display/SConscript', + 'drivers/mouse/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + + + + + In this case, the &SConstruct; file + lists all of the &SConscript; files in the build explicitly. + (Note, however, that not every directory in the tree + necessarily has an &SConscript; file.) + Alternatively, the drivers + subdirectory might contain an intermediate + &SConscript; file, + in which case the &SConscript; call in + the top-level &SConstruct; file + would look like: + + + + + SConscript(['drivers/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + + + + + And the subsidiary &SConscript; file in the + drivers subdirectory + would look like: + + + + + SConscript(['display/SConscript', + 'mouse/SConscript']) + + + + + Whether you list all of the &SConscript; files in the + top-level &SConstruct; file, + or place a subsidiary &SConscript; file in + intervening directories, + or use some mix of the two schemes, + is up to you and the needs of your software. + + + +
+ +
+ Path Names Are Relative to the &SConscript; Directory + + + + Subsidiary &SConscript; files make it easy to create a build + hierarchy because all of the file and directory names + in a subsidiary &SConscript; files are interpreted + relative to the directory in which the &SConscript; file lives. + Typically, this allows the &SConscript; file containing the + instructions to build a target file + to live in the same directory as the source files + from which the target will be built, + making it easy to update how the software is built + whenever files are added or deleted + (or other changes are made). + + + + + + For example, suppose we want to build two programs + &prog1; and &prog2; in two separate directories + with the same names as the programs. + One typical way to do this would be + with a top-level &SConstruct; file like this: + + + + + SConscript(['prog1/SConscript', + 'prog2/SConscript']) + + + + + And subsidiary &SConscript; files that look like this: + + + + + + env = Environment() + env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) + + + + + And this: + + + + + + env = Environment() + env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) + + + + + Then, when we run &SCons; in the top-level directory, + our build looks like: + + + + + % scons -Q + cc -o prog1/foo1.o -c prog1/foo1.c + cc -o prog1/foo2.o -c prog1/foo2.c + cc -o prog1/main.o -c prog1/main.c + cc -o prog1/prog1 prog1/main.o prog1/foo1.o prog1/foo2.o + cc -o prog2/bar1.o -c prog2/bar1.c + cc -o prog2/bar2.o -c prog2/bar2.c + cc -o prog2/main.o -c prog2/main.c + cc -o prog2/prog2 prog2/main.o prog2/bar1.o prog2/bar2.o + + + + + Notice the following: + + First, you can have files with the same names + in multiple directories, like main.c in the above example. + + Second, unlike standard recursive use of &Make;, + &SCons; stays in the top-level directory + (where the &SConstruct; file lives) + and issues commands that use the path names + from the top-level directory to the + target and source files within the hierarchy. + + + +
+ +
+ Top-Level Path Names in Subsidiary &SConscript; Files + + + + If you need to use a file from another directory, + it's sometimes more convenient to specify + the path to a file in another directory + from the top-level &SConstruct; directory, + even when you're using that file in + a subsidiary &SConscript; file in a subdirectory. + You can tell &SCons; to interpret a path name + as relative to the top-level &SConstruct; directory, + not the local directory of the &SConscript; file, + by appending a &hash; (hash mark) + to the beginning of the path name: + + + + + env = Environment() + env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) + + + + + In this example, + the lib directory is + directly underneath the top-level &SConstruct; directory. + If the above &SConscript; file is in a subdirectory + named src/prog, + the output would look like: + + + + + % scons -Q + cc -o lib/foo1.o -c lib/foo1.c + cc -o src/prog/foo2.o -c src/prog/foo2.c + cc -o src/prog/main.o -c src/prog/main.c + cc -o src/prog/prog src/prog/main.o lib/foo1.o src/prog/foo2.o + + + + + (Notice that the lib/foo1.o object file + is built in the same directory as its source file. + See , below, + for information about + how to build the object file in a different subdirectory.) + + + +
+ +
+ Absolute Path Names + + + + Of course, you can always specify + an absolute path name for a file--for example: + + + + + env = Environment() + env.Program('prog', ['main.c', '/usr/joe/lib/foo1.c', 'foo2.c']) + + + + + Which, when executed, would yield: + + + + + % scons -Q + cc -o src/prog/foo2.o -c src/prog/foo2.c + cc -o src/prog/main.o -c src/prog/main.c + cc -o /usr/joe/lib/foo1.o -c /usr/joe/lib/foo1.c + cc -o src/prog/prog src/prog/main.o /usr/joe/lib/foo1.o src/prog/foo2.o + + + + + (As was the case with top-relative path names, + notice that the /usr/joe/lib/foo1.o object file + is built in the same directory as its source file. + See , below, + for information about + how to build the object file in a different subdirectory.) + + + +
+ +
+ Sharing Environments (and Other Variables) Between &SConscript; Files + + + + In the previous example, + each of the subsidiary &SConscript; files + created its own construction environment + by calling &Environment; separately. + This obviously works fine, + but if each program must be built + with the same construction variables, + it's cumbersome and error-prone to initialize + separate construction environments + in the same way over and over in each subsidiary + &SConscript; file. + + + + + + &SCons; supports the ability to export variables + from a parent &SConscript; file + to its subsidiary &SConscript; files, + which allows you to share common initialized + values throughout your build hierarchy. + + + +
+ Exporting Variables + + + + There are two ways to export a variable, + such as a construction environment, + from an &SConscript; file, + so that it may be used by other &SConscript; files. + First, you can call the &Export; + function with a list of variables, + or a string of white-space separated variable names. + Each call to &Export; adds one + or more variables to a global list + of variables that are available for import + by other &SConscript; files. + + + + + env = Environment() + Export('env') + + + + + You may export more than one variable name at a time: + + + + + env = Environment() + debug = ARGUMENTS['debug'] + Export('env', 'debug') + + + + + Because white space is not legal in Python variable names, + the &Export; function will even automatically split + a string into separate names for you: + + + + + Export('env debug') + + + + + Second, you can specify a list of + variables to export as a second argument + to the &SConscript; function call: + + + + + SConscript('src/SConscript', 'env') + + + + + Or as the &exports; keyword argument: + + + + + SConscript('src/SConscript', exports='env') + + + + + These calls export the specified variables + to only the listed &SConscript; files. + You may, however, specify more than one + &SConscript; file in a list: + + + + + SConscript(['src1/SConscript', + 'src2/SConscript'], exports='env') + + + + + This is functionally equivalent to + calling the &SConscript; function + multiple times with the same &exports; argument, + one per &SConscript; file. + + + +
+ +
+ Importing Variables + + + + Once a variable has been exported from a calling + &SConscript; file, + it may be used in other &SConscript; files + by calling the &Import; function: + + + + + Import('env') + env.Program('prog', ['prog.c']) + + + + + The &Import; call makes the env construction + environment available to the &SConscript; file, + after which the variable can be used to build + programs, libraries, etc. + + + + + + Like the &Export; function, + the &Import; function can be used + with multiple variable names: + + + + + Import('env', 'debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + And the &Import; function will similarly + split a string along white-space + into separate variable names: + + + + + Import('env debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + Lastly, as a special case, + you may import all of the variables that + have been exported by supplying an asterisk + to the &Import; function: + + + + + Import('*') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + If you're dealing with a lot of &SConscript; files, + this can be a lot simpler than keeping + arbitrary lists of imported variables in each file. + + + +
+ +
+ Returning Values From an &SConscript; File + + + + Sometimes, you would like to be able to + use information from a subsidiary + &SConscript; file in some way. + For example, + suppose that you want to create one + library from source files + scattered throughout a number + of subsidiary &SConscript; files. + You can do this by using the &Return; + function to return values + from the subsidiary &SConscript; files + to the calling file. + + + + + + If, for example, we have two subdirectories + &foo; and &bar; + that should each contribute a source + file to a Library, + what we'd like to be able to do is + collect the object files + from the subsidiary &SConscript; calls + like this: + + + + + env = Environment() + Export('env') + objs = [] + for subdir in ['foo', 'bar']: + o = SConscript('%s/SConscript' % subdir) + objs.append(o) + env.Library('prog', objs) + + + + + We can do this by using the &Return; + function in the + foo/SConscript file like this: + + + + + + Import('env') + obj = env.Object('foo.c') + Return('obj') + + + + + (The corresponding + bar/SConscript + file should be pretty obvious.) + Then when we run &SCons;, + the object files from the subsidiary subdirectories + are all correctly archived in the desired library: + + + + + % scons -Q + cc -o bar/bar.o -c bar/bar.c + cc -o foo/foo.o -c foo/foo.c + ar rc libprog.a foo/foo.o bar/bar.o + ranlib libprog.a + + + + +
+ +
+ + diff --git a/doc/user/install.in b/doc/user/install.in new file mode 100644 index 0000000..cce2de3 --- /dev/null +++ b/doc/user/install.in @@ -0,0 +1,247 @@ + + + + + Once a program is built, + it is often appropriate to install it in another + directory for public use. + You use the &Install; method + to arrange for a program, or any other file, + to be copied into a destination directory: + + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('__ROOT__/usr/bin', hello) + + + int main() { printf("Hello, world!\n"); } + + + + + + Note, however, that installing a file is + still considered a type of file "build." + This is important when you remember that + the default behavior of &SCons; is + to build files in or below the current directory. + If, as in the example above, + you are installing files in a directory + outside of the top-level &SConstruct; file's directory tree, + you must specify that directory + (or a higher directory, such as /) + for it to install anything there: + + + + + scons -Q + scons -Q __ROOT__/usr/bin + + + + + It can, however, be cumbersome to remember + (and type) the specific destination directory + in which the program (or any other file) + should be installed. + This is an area where the &Alias; + function comes in handy, + allowing you, for example, + to create a pseudo-target named install + that can expand to the specified destination directory: + + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('__ROOT__/usr/bin', hello) + env.Alias('install', '__ROOT__/usr/bin') + + + int main() { printf("Hello, world!\n"); } + + + + + + This then yields the more natural + ability to install the program + in its destination as follows: + + + + + scons -Q + scons -Q install + + +
+ Installing Multiple Files in a Directory + + + + You can install multiple files into a directory + simply by calling the &Install; function multiple times: + + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('__ROOT__/usr/bin', hello) + env.Install('__ROOT__/usr/bin', goodbye) + env.Alias('install', '__ROOT__/usr/bin') + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Goodbye, world!\n"); } + + + + + + Or, more succinctly, listing the multiple input + files in a list + (just like you can do with any other builder): + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('__ROOT__/usr/bin', [hello, goodbye]) + env.Alias('install', '__ROOT__/usr/bin') + + + + + Either of these two examples yields: + + + + + scons -Q install + + +
+ +
+ Installing a File Under a Different Name + + + + The &Install; method preserves the name + of the file when it is copied into the + destination directory. + If you need to change the name of the file + when you copy it, use the &InstallAs; function: + + + + + + env = Environment() + hello = env.Program('hello.c') + env.InstallAs('__ROOT__/usr/bin/hello-new', hello) + env.Alias('install', '__ROOT__/usr/bin') + + + int main() { printf("Hello, world!\n"); } + + + + + + This installs the hello + program with the name hello-new + as follows: + + + + + scons -Q install + + +
+ +
+ Installing Multiple Files Under Different Names + + + + Lastly, if you have multiple files that all + need to be installed with different file names, + you can either call the &InstallAs; function + multiple times, or as a shorthand, + you can supply same-length lists + for both the target and source arguments: + + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.InstallAs(['__ROOT__/usr/bin/hello-new', + '__ROOT__/usr/bin/goodbye-new'], + [hello, goodbye]) + env.Alias('install', '__ROOT__/usr/bin') + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Goodbye, world!\n"); } + + + + + + In this case, the &InstallAs; function + loops through both lists simultaneously, + and copies each source file into its corresponding + target file name: + + + + + scons -Q install + + +
diff --git a/doc/user/install.xml b/doc/user/install.xml new file mode 100644 index 0000000..e011986 --- /dev/null +++ b/doc/user/install.xml @@ -0,0 +1,237 @@ + + + + + Once a program is built, + it is often appropriate to install it in another + directory for public use. + You use the &Install; method + to arrange for a program, or any other file, + to be copied into a destination directory: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('/usr/bin', hello) + + + + + Note, however, that installing a file is + still considered a type of file "build." + This is important when you remember that + the default behavior of &SCons; is + to build files in or below the current directory. + If, as in the example above, + you are installing files in a directory + outside of the top-level &SConstruct; file's directory tree, + you must specify that directory + (or a higher directory, such as /) + for it to install anything there: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q /usr/bin + Install file: "hello" as "/usr/bin/hello" + + + + + It can, however, be cumbersome to remember + (and type) the specific destination directory + in which the program (or any other file) + should be installed. + This is an area where the &Alias; + function comes in handy, + allowing you, for example, + to create a pseudo-target named install + that can expand to the specified destination directory: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('/usr/bin', hello) + env.Alias('install', '/usr/bin') + + + + + This then yields the more natural + ability to install the program + in its destination as follows: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q install + Install file: "hello" as "/usr/bin/hello" + + +
+ Installing Multiple Files in a Directory + + + + You can install multiple files into a directory + simply by calling the &Install; function multiple times: + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('/usr/bin', hello) + env.Install('/usr/bin', goodbye) + env.Alias('install', '/usr/bin') + + + + + Or, more succinctly, listing the multiple input + files in a list + (just like you can do with any other builder): + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('/usr/bin', [hello, goodbye]) + env.Alias('install', '/usr/bin') + + + + + Either of these two examples yields: + + + + + % scons -Q install + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + Install file: "goodbye" as "/usr/bin/goodbye" + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello" + + +
+ +
+ Installing a File Under a Different Name + + + + The &Install; method preserves the name + of the file when it is copied into the + destination directory. + If you need to change the name of the file + when you copy it, use the &InstallAs; function: + + + + + env = Environment() + hello = env.Program('hello.c') + env.InstallAs('/usr/bin/hello-new', hello) + env.Alias('install', '/usr/bin') + + + + + This installs the hello + program with the name hello-new + as follows: + + + + + % scons -Q install + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello-new" + + +
+ +
+ Installing Multiple Files Under Different Names + + + + Lastly, if you have multiple files that all + need to be installed with different file names, + you can either call the &InstallAs; function + multiple times, or as a shorthand, + you can supply same-length lists + for both the target and source arguments: + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.InstallAs(['/usr/bin/hello-new', + '/usr/bin/goodbye-new'], + [hello, goodbye]) + env.Alias('install', '/usr/bin') + + + + + In this case, the &InstallAs; function + loops through both lists simultaneously, + and copies each source file into its corresponding + target file name: + + + + + % scons -Q install + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + Install file: "goodbye" as "/usr/bin/goodbye-new" + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello-new" + + +
diff --git a/doc/user/java.in b/doc/user/java.in new file mode 100644 index 0000000..358d79b --- /dev/null +++ b/doc/user/java.in @@ -0,0 +1,657 @@ + + + + + So far, we've been using examples of + building C and C++ programs + to demonstrate the features of &SCons;. + &SCons; also supports building Java programs, + but Java builds are handled slightly differently, + which reflects the ways in which + the Java compiler and tools + build programs differently than + other languages' tool chains. + + + +
+ Building Java Class Files: the &b-Java; Builder + + + + The basic activity when programming in Java, + of course, is to take one or more .java files + containing Java source code + and to call the Java compiler + to turn them into one or more + .class files. + In &SCons;, you do this + by giving the &b-link-Java; Builder + a target directory in which + to put the .class files, + and a source directory that contains + the .java files: + + + + + + Java('classes', 'src') + + + public class Example1 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example2 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example3 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + + + + If the src directory contains + three .java source files, + then running &SCons; might look like this: + + + + + scons -Q + + + + + &SCons; will actually search the src + directory tree for all of the .java files. + The Java compiler will then create the + necessary class files in the classes subdirectory, + based on the class names found in the .java files. + + + +
+ +
+ How &SCons; Handles Java Dependencies + + + + In addition to searching the source directory for + .java files, + &SCons; actually runs the .java files + through a stripped-down Java parser that figures out + what classes are defined. + In other words, &SCons; knows, + without you having to tell it, + what .class files + will be produced by the &javac; call. + So our one-liner example from the preceding section: + + + + + + Java('classes', 'src') + + + public class Example1 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + public class AdditionalClass1 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example2 + { + class Inner2 { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + } + + + public class Example3 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + public class AdditionalClass3 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + + + + Will not only tell you reliably + that the .class files + in the classes subdirectory + are up-to-date: + + + + + scons -Q + scons -Q classes + + + + + But it will also remove all of the generated + .class files, + even for inner classes, + without you having to specify them manually. + For example, if our + Example1.java + and + Example3.java + files both define additional classes, + and the class defined in Example2.java + has an inner class, + running scons -c + will clean up all of those .class files + as well: + + + + + scons -Q + scons -Q -c classes + + + + + To ensure correct handling of .class + dependencies in all cases, you need to tell &SCons; which Java + version is being used. This is needed because Java 1.5 changed + the .class file names for nested anonymous + inner classes. Use the JAVAVERSION construction + variable to specify the version in use. With Java 1.6, the + one-liner example can then be defined like this: + + + + + Java('classes', 'src', JAVAVERSION='1.6') + + + + See JAVAVERSION in the man page for more information. + + +
+ +
+ Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder + + + + After building the class files, + it's common to collect them into + a Java archive (.jar) file, + which you do by calling the &b-link-Jar; Builder method. + If you want to just collect all of the + class files within a subdirectory, + you can just specify that subdirectory + as the &b-Jar; source: + + + + + + Java(target = 'classes', source = 'src') + Jar(target = 'test.jar', source = 'classes') + + + public class Example1 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example2 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example3 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + + + + &SCons; will then pass that directory + to the &jar; command, + which will collect all of the underlying + .class files: + + + + + scons -Q + + + + + If you want to keep all of the + .class files + for multiple programs in one location, + and only archive some of them in + each .jar file, + you can pass the &b-Jar; builder a + list of files as its source. + It's extremely simple to create multiple + .jar files this way, + using the lists of target class files created + by calls to the &b-link-Java; builder + as sources to the various &b-Jar; calls: + + + + + + prog1_class_files = Java(target = 'classes', source = 'prog1') + prog2_class_files = Java(target = 'classes', source = 'prog2') + Jar(target = 'prog1.jar', source = prog1_class_files) + Jar(target = 'prog2.jar', source = prog2_class_files) + + + public class Example1 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example2 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example3 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + public class Example4 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + + + + This will then create + prog1.jar + and prog2.jar + next to the subdirectories + that contain their .java files: + + + + + scons -Q + + +
+ +
+ Building C Header and Stub Files: the &b-JavaH; Builder + + + + You can generate C header and source files + for implementing native methods, + by using the &b-link-JavaH; Builder. + There are several ways of using the &JavaH Builder. + One typical invocation might look like: + + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + JavaH(target = 'native', source = classes) + + + package pkg.sub; + public class Example1 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example2 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example3 + { + public static void main(String[] args) + { + } + } + + + + + + The source is a list of class files generated by the + call to the &b-link-Java; Builder, + and the target is the output directory in + which we want the C header files placed. + The target + gets converted into the + when &SCons; runs &javah;: + + + + + scons -Q + + + + + In this case, + the call to &javah; + will generate the header files + native/pkg_sub_Example1.h, + native/pkg_sub_Example2.h + and + native/pkg_sub_Example3.h. + Notice that &SCons; remembered that the class + files were generated with a target directory of + classes, + and that it then specified that target directory + as the option + to the call to &javah;. + + + + + + Although it's more convenient to use + the list of class files returned by + the &b-Java; Builder + as the source of a call to the &b-JavaH; Builder, + you can + specify the list of class files + by hand, if you prefer. + If you do, + you need to set the + &cv-link-JAVACLASSDIR; construction variable + when calling &b-JavaH;: + + + + + + Java(target = 'classes', source = 'src/pkg/sub') + class_file_list = ['classes/pkg/sub/Example1.class', + 'classes/pkg/sub/Example2.class', + 'classes/pkg/sub/Example3.class'] + JavaH(target = 'native', source = class_file_list, JAVACLASSDIR = 'classes') + + + package pkg.sub; + public class Example1 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example2 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example3 + { + public static void main(String[] args) + { + } + } + + + + + + The &cv-JAVACLASSDIR; value then + gets converted into the + when &SCons; runs &javah;: + + + + + scons -Q + + + + + Lastly, if you don't want a separate header file + generated for each source file, + you can specify an explicit File Node + as the target of the &b-JavaH; Builder: + + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + JavaH(target = File('native.h'), source = classes) + + + package pkg.sub; + public class Example1 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example2 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example3 + { + public static void main(String[] args) + { + } + } + + + + + + Because &SCons; assumes by default + that the target of the &b-JavaH; builder is a directory, + you need to use the &File; function + to make sure that &SCons; doesn't + create a directory named native.h. + When a file is used, though, + &SCons; correctly converts the file name + into the &javah; option: + + + + + scons -Q + + +
+ +
+ Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder + + + + You can generate Remote Method Invocation stubs + by using the &b-link-RMIC; Builder. + The source is a list of directories, + typically returned by a call to the &b-link-Java; Builder, + and the target is an output directory + where the _Stub.class + and _Skel.class files will + be placed: + + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + RMIC(target = 'outdir', source = classes) + + + package pkg.sub; + public class Example1 + { + public static void main(String[] args) + { + } + } + + + package pkg.sub; + public class Example2 + { + public static void main(String[] args) + { + } + } + + + + + + As it did with the &b-link-JavaH; Builder, + &SCons; remembers the class directory + and passes it as the option + to &rmic: + + + + + scons -Q + + + + + This example would generate the files + outdir/pkg/sub/Example1_Skel.class, + outdir/pkg/sub/Example1_Stub.class, + outdir/pkg/sub/Example2_Skel.class and + outdir/pkg/sub/Example2_Stub.class. + + + +
diff --git a/doc/user/java.xml b/doc/user/java.xml new file mode 100644 index 0000000..8198b65 --- /dev/null +++ b/doc/user/java.xml @@ -0,0 +1,433 @@ + + + + + So far, we've been using examples of + building C and C++ programs + to demonstrate the features of &SCons;. + &SCons; also supports building Java programs, + but Java builds are handled slightly differently, + which reflects the ways in which + the Java compiler and tools + build programs differently than + other languages' tool chains. + + + +
+ Building Java Class Files: the &b-Java; Builder + + + + The basic activity when programming in Java, + of course, is to take one or more .java files + containing Java source code + and to call the Java compiler + to turn them into one or more + .class files. + In &SCons;, you do this + by giving the &b-link-Java; Builder + a target directory in which + to put the .class files, + and a source directory that contains + the .java files: + + + + + Java('classes', 'src') + + + + + If the src directory contains + three .java source files, + then running &SCons; might look like this: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + + + + + &SCons; will actually search the src + directory tree for all of the .java files. + The Java compiler will then create the + necessary class files in the classes subdirectory, + based on the class names found in the .java files. + + + +
+ +
+ How &SCons; Handles Java Dependencies + + + + In addition to searching the source directory for + .java files, + &SCons; actually runs the .java files + through a stripped-down Java parser that figures out + what classes are defined. + In other words, &SCons; knows, + without you having to tell it, + what .class files + will be produced by the &javac; call. + So our one-liner example from the preceding section: + + + + + Java('classes', 'src') + + + + + Will not only tell you reliably + that the .class files + in the classes subdirectory + are up-to-date: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + % scons -Q classes + scons: `classes' is up to date. + + + + + But it will also remove all of the generated + .class files, + even for inner classes, + without you having to specify them manually. + For example, if our + Example1.java + and + Example3.java + files both define additional classes, + and the class defined in Example2.java + has an inner class, + running scons -c + will clean up all of those .class files + as well: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + % scons -Q -c classes + Removed classes/Example1.class + Removed classes/AdditionalClass1.class + Removed classes/Example2$Inner2.class + Removed classes/Example2.class + Removed classes/Example3.class + Removed classes/AdditionalClass3.class + + + + + To ensure correct handling of .class + dependencies in all cases, you need to tell &SCons; which Java + version is being used. This is needed because Java 1.5 changed + the .class file names for nested anonymous + inner classes. Use the JAVAVERSION construction + variable to specify the version in use. With Java 1.6, the + one-liner example can then be defined like this: + + + + + Java('classes', 'src', JAVAVERSION='1.6') + + + + See JAVAVERSION in the man page for more information. + + +
+ +
+ Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder + + + + After building the class files, + it's common to collect them into + a Java archive (.jar) file, + which you do by calling the &b-link-Jar; Builder method. + If you want to just collect all of the + class files within a subdirectory, + you can just specify that subdirectory + as the &b-Jar; source: + + + + + Java(target = 'classes', source = 'src') + Jar(target = 'test.jar', source = 'classes') + + + + + &SCons; will then pass that directory + to the &jar; command, + which will collect all of the underlying + .class files: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + jar cf test.jar classes + + + + + If you want to keep all of the + .class files + for multiple programs in one location, + and only archive some of them in + each .jar file, + you can pass the &b-Jar; builder a + list of files as its source. + It's extremely simple to create multiple + .jar files this way, + using the lists of target class files created + by calls to the &b-link-Java; builder + as sources to the various &b-Jar; calls: + + + + + prog1_class_files = Java(target = 'classes', source = 'prog1') + prog2_class_files = Java(target = 'classes', source = 'prog2') + Jar(target = 'prog1.jar', source = prog1_class_files) + Jar(target = 'prog2.jar', source = prog2_class_files) + + + + + This will then create + prog1.jar + and prog2.jar + next to the subdirectories + that contain their .java files: + + + + + % scons -Q + javac -d classes -sourcepath prog1 prog1/Example1.java prog1/Example2.java + javac -d classes -sourcepath prog2 prog2/Example3.java prog2/Example4.java + jar cf prog1.jar -C classes Example1.class -C classes Example2.class + jar cf prog2.jar -C classes Example3.class -C classes Example4.class + + +
+ +
+ Building C Header and Stub Files: the &b-JavaH; Builder + + + + You can generate C header and source files + for implementing native methods, + by using the &b-link-JavaH; Builder. + There are several ways of using the &JavaH; Builder. + One typical invocation might look like: + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + JavaH(target = 'native', source = classes) + + + + + The source is a list of class files generated by the + call to the &b-link-Java; Builder, + and the target is the output directory in + which we want the C header files placed. + The target + gets converted into the + when &SCons; runs &javah;: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java + javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 + + + + + In this case, + the call to &javah; + will generate the header files + native/pkg_sub_Example1.h, + native/pkg_sub_Example2.h + and + native/pkg_sub_Example3.h. + Notice that &SCons; remembered that the class + files were generated with a target directory of + classes, + and that it then specified that target directory + as the option + to the call to &javah;. + + + + + + Although it's more convenient to use + the list of class files returned by + the &b-Java; Builder + as the source of a call to the &b-JavaH; Builder, + you can + specify the list of class files + by hand, if you prefer. + If you do, + you need to set the + &cv-link-JAVACLASSDIR; construction variable + when calling &b-JavaH;: + + + + + Java(target = 'classes', source = 'src/pkg/sub') + class_file_list = ['classes/pkg/sub/Example1.class', + 'classes/pkg/sub/Example2.class', + 'classes/pkg/sub/Example3.class'] + JavaH(target = 'native', source = class_file_list, JAVACLASSDIR = 'classes') + + + + + The &cv-JAVACLASSDIR; value then + gets converted into the + when &SCons; runs &javah;: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java + javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 + + + + + Lastly, if you don't want a separate header file + generated for each source file, + you can specify an explicit File Node + as the target of the &b-JavaH; Builder: + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + JavaH(target = File('native.h'), source = classes) + + + + + Because &SCons; assumes by default + that the target of the &b-JavaH; builder is a directory, + you need to use the &File; function + to make sure that &SCons; doesn't + create a directory named native.h. + When a file is used, though, + &SCons; correctly converts the file name + into the &javah; option: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java + javah -o native.h -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 + + +
+ +
+ Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder + + + + You can generate Remote Method Invocation stubs + by using the &b-link-RMIC; Builder. + The source is a list of directories, + typically returned by a call to the &b-link-Java; Builder, + and the target is an output directory + where the _Stub.class + and _Skel.class files will + be placed: + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + RMIC(target = 'outdir', source = classes) + + + + + As it did with the &b-link-JavaH; Builder, + &SCons; remembers the class directory + and passes it as the option + to &rmic;: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java + rmic -d outdir -classpath classes pkg.sub.Example1 pkg.sub.Example2 + + + + + This example would generate the files + outdir/pkg/sub/Example1_Skel.class, + outdir/pkg/sub/Example1_Stub.class, + outdir/pkg/sub/Example2_Skel.class and + outdir/pkg/sub/Example2_Stub.class. + + + +
diff --git a/doc/user/less-simple.in b/doc/user/less-simple.in new file mode 100644 index 0000000..e19ba13 --- /dev/null +++ b/doc/user/less-simple.in @@ -0,0 +1,623 @@ + + + + + In this chapter, + you will see several examples of + very simple build configurations using &SCons;, + which will demonstrate how easy + it is to use &SCons; to + build programs from several different programming languages + on different types of systems. + + + +
+ Specifying the Name of the Target (Output) File + + + + You've seen that when you call the &b-link-Program; builder method, + it builds the resulting program with the same + base name as the source file. + That is, the following call to build an + executable program from the &hello_c; source file + will build an executable program named &hello; on POSIX systems, + and an executable program named &hello_exe; on Windows systems: + + + + + Program('hello.c') + + + + + If you want to build a program with + a different name than the base of the source file name, + you simply put the target file name + to the left of the source file name: + + + + + + Program('new_hello', 'hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + (&SCons; requires the target file name first, + followed by the source file name, + so that the order mimics that of an + assignment statement in most programming languages, + including Python: + "program = source files".) + + + + + + Now &SCons; will build an executable program + named &new_hello; when run on a POSIX system: + + + + + scons -Q + + + + + And &SCons; will build an executable program + named &new_hello_exe; when run on a Windows system: + + + + + scons -Q + + +
+ +
+ Compiling Multiple Source Files + + + + You've just seen how to configure &SCons; + to compile a program from a single source file. + It's more common, of course, + that you'll need to build a program from + many input source files, not just one. + To do this, you need to put the + source files in a Python list + (enclosed in square brackets), + like so: + + + + + + Program(['prog.c', 'file1.c', 'file2.c']) + + + int main() { printf("prog.c\n"); } + + + void file1() { printf("file1.c\n"); } + + + void file2() { printf("file2.c\n"); } + + + + + + A build of the above example would look like: + + + + + scons -Q + + + + + Notice that &SCons; + deduces the output program name + from the first source file specified + in the list--that is, + because the first source file was &prog_c;, + &SCons; will name the resulting program &prog; + (or &prog_exe; on a Windows system). + If you want to specify a different program name, + then (as we've seen in the previous section) + you slide the list of source files + over to the right + to make room for the output program file name. + (&SCons; puts the output file name to the left + of the source file names + so that the order mimics that of an + assignment statement: "program = source files".) + This makes our example: + + + + + + Program('program', ['prog.c', 'file1.c', 'file2.c']) + + + int main() { printf("prog.c\n"); } + + + void file1() { printf("file1.c\n"); } + + + void file2() { printf("file2.c\n"); } + + + + + + On Linux, a build of this example would look like: + + + + + scons -Q + + + + + Or on Windows: + + + + + scons -Q + + +
+ +
+ Making a list of files with &Glob; + + + + You can also use the &Glob; function to find all files matching a + certain template, using the standard shell pattern matching + characters *, ? + and [abc] to match any of + a, b or c. + [!abc] is also supported, + to match any character except + a, b or c. + This makes many multi-source-file builds quite easy: + + + + + Program('program', Glob('*.c')) + + + + + The SCons man page has more details on using &Glob; + with variant directories + (see , below) + and repositories + (see , below), + and returning strings rather than Nodes. + + + +
+ +
+ Specifying Single Files Vs. Lists of Files + + + + We've now shown you two ways to specify + the source for a program, + one with a list of files: + + + + + Program('hello', ['file1.c', 'file2.c']) + + + + + And one with a single file: + + + + + Program('hello', 'hello.c') + + + + + You could actually put a single file name in a list, too, + which you might prefer just for the sake of consistency: + + + + + Program('hello', ['hello.c']) + + + + + &SCons; functions will accept a single file name in either form. + In fact, internally, &SCons; treats all input as lists of files, + but allows you to omit the square brackets + to cut down a little on the typing + when there's only a single file name. + + + + + + + + Although &SCons; functions + are forgiving about whether or not you + use a string vs. a list for a single file name, + Python itself is more strict about + treating lists and strings differently. + So where &SCons; allows either + a string or list: + + + + + # The following two calls both work correctly: + Program('program1', 'program1.c') + Program('program2', ['program2.c']) + + + + + Trying to do "Python things" that mix strings and + lists will cause errors or lead to incorrect results: + + + + + common_sources = ['file1.c', 'file2.c'] + + # THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR + # BECAUSE IT TRIES TO ADD A STRING TO A LIST: + Program('program1', common_sources + 'program1.c') + + # The following works correctly, because it's adding two + # lists together to make another list. + Program('program2', common_sources + ['program2.c']) + + + + +
+ +
+ Making Lists of Files Easier to Read + + + + One drawback to the use of a Python list + for source files is that + each file name must be enclosed in quotes + (either single quotes or double quotes). + This can get cumbersome and difficult to read + when the list of file names is long. + Fortunately, &SCons; and Python provide a number of ways + to make sure that + the &SConstruct; file stays easy to read. + + + + + + To make long lists of file names + easier to deal with, &SCons; provides a + &Split; function + that takes a quoted list of file names, + with the names separated by spaces or other white-space characters, + and turns it into a list of separate file names. + Using the &Split; function turns the + previous example into: + + + + + Program('program', Split('main.c file1.c file2.c')) + + + + + (If you're already familiar with Python, + you'll have realized that this is similar to the + split() method + in the Python standard string module. + Unlike the string.split() method, + however, the &Split; function + does not require a string as input + and will wrap up a single non-string object in a list, + or return its argument untouched if it's already a list. + This comes in handy as a way to make sure + arbitrary values can be passed to &SCons; functions + without having to check the type of the variable by hand.) + + + + + + Putting the call to the &Split; function + inside the &b-Program; call + can also be a little unwieldy. + A more readable alternative is to + assign the output from the &Split; call + to a variable name, + and then use the variable when calling the + &b-Program; function: + + + + + src_files = Split('main.c file1.c file2.c') + Program('program', src_files) + + + + + Lastly, the &Split; function + doesn't care how much white space separates + the file names in the quoted string. + This allows you to create lists of file + names that span multiple lines, + which often makes for easier editing: + + + + + src_files = Split("""main.c + file1.c + file2.c""") + Program('program', src_files) + + + + + (Note in this example that we used + the Python "triple-quote" syntax, + which allows a string to contain + multiple lines. + The three quotes can be either + single or double quotes.) + + + +
+ +
+ Keyword Arguments + + + + &SCons; also allows you to identify + the output file and input source files + using Python keyword arguments. + The output file is known as the + target, + and the source file(s) are known (logically enough) as the + source. + The Python syntax for this is: + + + + + src_files = Split('main.c file1.c file2.c') + Program(target = 'program', source = src_files) + + + + + Because the keywords explicitly identify + what each argument is, + you can actually reverse the order if you prefer: + + + + + src_files = Split('main.c file1.c file2.c') + Program(source = src_files, target = 'program') + + + + + Whether or not you choose to use keyword arguments + to identify the target and source files, + and the order in which you specify them + when using keywords, + are purely personal choices; + &SCons; functions the same regardless. + + + +
+ +
+ Compiling Multiple Programs + + + + In order to compile multiple programs + within the same &SConstruct; file, + simply call the &Program; method + multiple times, + once for each program you need to build: + + + + + + Program('foo.c') + Program('bar', ['bar1.c', 'bar2.c']) + + + int main() { printf("foo.c\n"); } + + + int main() { printf("bar1.c\n"); } + + + void bar2() { printf("bar2.c\n"); } + + + + + + &SCons; would then build the programs as follows: + + + + + scons -Q + + + + + Notice that &SCons; does not necessarily build the + programs in the same order in which you specify + them in the &SConstruct; file. + &SCons; does, however, recognize that + the individual object files must be built + before the resulting program can be built. + We'll discuss this in greater detail in + the "Dependencies" section, below. + + + +
+ +
+ Sharing Source Files Between Multiple Programs + + + + It's common to re-use code by sharing source files + between multiple programs. + One way to do this is to create a library + from the common source files, + which can then be linked into resulting programs. + (Creating libraries is discussed in + , below.) + + + + + + A more straightforward, but perhaps less convenient, + way to share source files between multiple programs + is simply to include the common files + in the lists of source files for each program: + + + + + + Program(Split('foo.c common1.c common2.c')) + Program('bar', Split('bar1.c bar2.c common1.c common2.c')) + + + int main() { printf("foo.c\n"); } + + + int main() { printf("bar1.c\n"); } + + + int bar2() { printf("bar2.c\n"); } + + + void common1() { printf("common1.c\n"); } + + + void common22() { printf("common2.c\n"); } + + + + + + &SCons; recognizes that the object files for + the &common1_c; and &common2_c; source files + each need to be built only once, + even though the resulting object files are + each linked in to both of the resulting executable programs: + + + + + scons -Q + + + + + If two or more programs + share a lot of common source files, + repeating the common files in the list for each program + can be a maintenance problem when you need to change the + list of common files. + You can simplify this by creating a separate Python list + to hold the common file names, + and concatenating it with other lists + using the Python + operator: + + + + + common = ['common1.c', 'common2.c'] + foo_files = ['foo.c'] + common + bar_files = ['bar1.c', 'bar2.c'] + common + Program('foo', foo_files) + Program('bar', bar_files) + + + + + This is functionally equivalent to the previous example. + + + +
diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml new file mode 100644 index 0000000..57c61e2 --- /dev/null +++ b/doc/user/less-simple.xml @@ -0,0 +1,608 @@ + + + + + In this chapter, + you will see several examples of + very simple build configurations using &SCons;, + which will demonstrate how easy + it is to use &SCons; to + build programs from several different programming languages + on different types of systems. + + + +
+ Specifying the Name of the Target (Output) File + + + + You've seen that when you call the &b-link-Program; builder method, + it builds the resulting program with the same + base name as the source file. + That is, the following call to build an + executable program from the &hello_c; source file + will build an executable program named &hello; on POSIX systems, + and an executable program named &hello_exe; on Windows systems: + + + + + Program('hello.c') + + + + + If you want to build a program with + a different name than the base of the source file name, + you simply put the target file name + to the left of the source file name: + + + + + Program('new_hello', 'hello.c') + + + + + (&SCons; requires the target file name first, + followed by the source file name, + so that the order mimics that of an + assignment statement in most programming languages, + including Python: + "program = source files".) + + + + + + Now &SCons; will build an executable program + named &new_hello; when run on a POSIX system: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o new_hello hello.o + + + + + And &SCons; will build an executable program + named &new_hello_exe; when run on a Windows system: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:new_hello.exe hello.obj + + +
+ +
+ Compiling Multiple Source Files + + + + You've just seen how to configure &SCons; + to compile a program from a single source file. + It's more common, of course, + that you'll need to build a program from + many input source files, not just one. + To do this, you need to put the + source files in a Python list + (enclosed in square brackets), + like so: + + + + + Program(['prog.c', 'file1.c', 'file2.c']) + + + + + A build of the above example would look like: + + + + + % scons -Q + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o prog.o -c prog.c + cc -o prog prog.o file1.o file2.o + + + + + Notice that &SCons; + deduces the output program name + from the first source file specified + in the list--that is, + because the first source file was &prog_c;, + &SCons; will name the resulting program &prog; + (or &prog_exe; on a Windows system). + If you want to specify a different program name, + then (as we've seen in the previous section) + you slide the list of source files + over to the right + to make room for the output program file name. + (&SCons; puts the output file name to the left + of the source file names + so that the order mimics that of an + assignment statement: "program = source files".) + This makes our example: + + + + + Program('program', ['prog.c', 'file1.c', 'file2.c']) + + + + + On Linux, a build of this example would look like: + + + + + % scons -Q + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o prog.o -c prog.c + cc -o program prog.o file1.o file2.o + + + + + Or on Windows: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fofile1.obj /c file1.c /nologo + cl /Fofile2.obj /c file2.c /nologo + cl /Foprog.obj /c prog.c /nologo + link /nologo /OUT:program.exe prog.obj file1.obj file2.obj + + +
+ +
+ Making a list of files with &Glob; + + + + You can also use the &Glob; function to find all files matching a + certain template, using the standard shell pattern matching + characters *, ? + and [abc] to match any of + a, b or c. + [!abc] is also supported, + to match any character except + a, b or c. + This makes many multi-source-file builds quite easy: + + + + + Program('program', Glob('*.c')) + + + + + The SCons man page has more details on using &Glob; + with variant directories + (see , below) + and repositories + (see , below), + and returning strings rather than Nodes. + + + +
+ +
+ Specifying Single Files Vs. Lists of Files + + + + We've now shown you two ways to specify + the source for a program, + one with a list of files: + + + + + Program('hello', ['file1.c', 'file2.c']) + + + + + And one with a single file: + + + + + Program('hello', 'hello.c') + + + + + You could actually put a single file name in a list, too, + which you might prefer just for the sake of consistency: + + + + + Program('hello', ['hello.c']) + + + + + &SCons; functions will accept a single file name in either form. + In fact, internally, &SCons; treats all input as lists of files, + but allows you to omit the square brackets + to cut down a little on the typing + when there's only a single file name. + + + + + + + + Although &SCons; functions + are forgiving about whether or not you + use a string vs. a list for a single file name, + Python itself is more strict about + treating lists and strings differently. + So where &SCons; allows either + a string or list: + + + + + # The following two calls both work correctly: + Program('program1', 'program1.c') + Program('program2', ['program2.c']) + + + + + Trying to do "Python things" that mix strings and + lists will cause errors or lead to incorrect results: + + + + + common_sources = ['file1.c', 'file2.c'] + + # THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR + # BECAUSE IT TRIES TO ADD A STRING TO A LIST: + Program('program1', common_sources + 'program1.c') + + # The following works correctly, because it's adding two + # lists together to make another list. + Program('program2', common_sources + ['program2.c']) + + + + +
+ +
+ Making Lists of Files Easier to Read + + + + One drawback to the use of a Python list + for source files is that + each file name must be enclosed in quotes + (either single quotes or double quotes). + This can get cumbersome and difficult to read + when the list of file names is long. + Fortunately, &SCons; and Python provide a number of ways + to make sure that + the &SConstruct; file stays easy to read. + + + + + + To make long lists of file names + easier to deal with, &SCons; provides a + &Split; function + that takes a quoted list of file names, + with the names separated by spaces or other white-space characters, + and turns it into a list of separate file names. + Using the &Split; function turns the + previous example into: + + + + + Program('program', Split('main.c file1.c file2.c')) + + + + + (If you're already familiar with Python, + you'll have realized that this is similar to the + split() method + in the Python standard string module. + Unlike the string.split() method, + however, the &Split; function + does not require a string as input + and will wrap up a single non-string object in a list, + or return its argument untouched if it's already a list. + This comes in handy as a way to make sure + arbitrary values can be passed to &SCons; functions + without having to check the type of the variable by hand.) + + + + + + Putting the call to the &Split; function + inside the &b-Program; call + can also be a little unwieldy. + A more readable alternative is to + assign the output from the &Split; call + to a variable name, + and then use the variable when calling the + &b-Program; function: + + + + + src_files = Split('main.c file1.c file2.c') + Program('program', src_files) + + + + + Lastly, the &Split; function + doesn't care how much white space separates + the file names in the quoted string. + This allows you to create lists of file + names that span multiple lines, + which often makes for easier editing: + + + + + src_files = Split("""main.c + file1.c + file2.c""") + Program('program', src_files) + + + + + (Note in this example that we used + the Python "triple-quote" syntax, + which allows a string to contain + multiple lines. + The three quotes can be either + single or double quotes.) + + + +
+ +
+ Keyword Arguments + + + + &SCons; also allows you to identify + the output file and input source files + using Python keyword arguments. + The output file is known as the + target, + and the source file(s) are known (logically enough) as the + source. + The Python syntax for this is: + + + + + src_files = Split('main.c file1.c file2.c') + Program(target = 'program', source = src_files) + + + + + Because the keywords explicitly identify + what each argument is, + you can actually reverse the order if you prefer: + + + + + src_files = Split('main.c file1.c file2.c') + Program(source = src_files, target = 'program') + + + + + Whether or not you choose to use keyword arguments + to identify the target and source files, + and the order in which you specify them + when using keywords, + are purely personal choices; + &SCons; functions the same regardless. + + + +
+ +
+ Compiling Multiple Programs + + + + In order to compile multiple programs + within the same &SConstruct; file, + simply call the &Program; method + multiple times, + once for each program you need to build: + + + + + Program('foo.c') + Program('bar', ['bar1.c', 'bar2.c']) + + + + + &SCons; would then build the programs as follows: + + + + + % scons -Q + cc -o bar1.o -c bar1.c + cc -o bar2.o -c bar2.c + cc -o bar bar1.o bar2.o + cc -o foo.o -c foo.c + cc -o foo foo.o + + + + + Notice that &SCons; does not necessarily build the + programs in the same order in which you specify + them in the &SConstruct; file. + &SCons; does, however, recognize that + the individual object files must be built + before the resulting program can be built. + We'll discuss this in greater detail in + the "Dependencies" section, below. + + + +
+ +
+ Sharing Source Files Between Multiple Programs + + + + It's common to re-use code by sharing source files + between multiple programs. + One way to do this is to create a library + from the common source files, + which can then be linked into resulting programs. + (Creating libraries is discussed in + , below.) + + + + + + A more straightforward, but perhaps less convenient, + way to share source files between multiple programs + is simply to include the common files + in the lists of source files for each program: + + + + + Program(Split('foo.c common1.c common2.c')) + Program('bar', Split('bar1.c bar2.c common1.c common2.c')) + + + + + &SCons; recognizes that the object files for + the &common1_c; and &common2_c; source files + each need to be built only once, + even though the resulting object files are + each linked in to both of the resulting executable programs: + + + + + % scons -Q + cc -o bar1.o -c bar1.c + cc -o bar2.o -c bar2.c + cc -o common1.o -c common1.c + cc -o common2.o -c common2.c + cc -o bar bar1.o bar2.o common1.o common2.o + cc -o foo.o -c foo.c + cc -o foo foo.o common1.o common2.o + + + + + If two or more programs + share a lot of common source files, + repeating the common files in the list for each program + can be a maintenance problem when you need to change the + list of common files. + You can simplify this by creating a separate Python list + to hold the common file names, + and concatenating it with other lists + using the Python + operator: + + + + + common = ['common1.c', 'common2.c'] + foo_files = ['foo.c'] + common + bar_files = ['bar1.c', 'bar2.c'] + common + Program('foo', foo_files) + Program('bar', bar_files) + + + + + This is functionally equivalent to the previous example. + + + +
diff --git a/doc/user/libraries.in b/doc/user/libraries.in new file mode 100644 index 0000000..4cce091 --- /dev/null +++ b/doc/user/libraries.in @@ -0,0 +1,445 @@ + + + + + It's often useful to organize large software projects + by collecting parts of the software into one or more libraries. + &SCons; makes it easy to create libraries + and to use them in the programs. + + + +
+ Building Libraries + + + + You build your own libraries by specifying &b-link-Library; + instead of &b-link-Program;: + + + + + + Library('foo', ['f1.c', 'f2.c', 'f3.c']) + + + void f1() { printf("f1.c\n"); } + + + void f2() { printf("f2.c\n"); } + + + void f3() { printf("f3.c\n"); } + + + + + + &SCons; uses the appropriate library prefix and suffix for your system. + So on POSIX or Linux systems, + the above example would build as follows + (although &ranlib; may not be called on all systems): + + + + + scons -Q + + + + + On a Windows system, + a build of the above example would look like: + + + + + scons -Q + + + + + The rules for the target name of the library + are similar to those for programs: + if you don't explicitly specify a target library name, + &SCons; will deduce one from the + name of the first source file specified, + and &SCons; will add an appropriate + file prefix and suffix if you leave them off. + + + +
+ Building Libraries From Source Code or Object Files + + + + The previous example shows building a library from a + list of source files. + You can, however, also give the &b-link-Library; call + object files, + and it will correctly realize + In fact, you can arbitrarily mix source code files + and object files in the source list: + + + + + + Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) + + + void f1() { printf("f1.c\n"); } + + + object file + + + void f3() { printf("f3.c\n"); } + + + object file + + + + + + And SCons realizes that only the source code files + must be compiled into object files + before creating the final library: + + + + + scons -Q + + + + + Of course, in this example, the object files + must already exist for the build to succeed. + See , below, + for information about how you can + build object files explicitly + and include the built files in a library. + + + +
+ +
+ Building Static Libraries Explicitly: the &b-StaticLibrary; Builder + + + + The &b-link-Library; function builds a traditional static library. + If you want to be explicit about the type of library being built, + you can use the synonym &b-link-StaticLibrary; function + instead of &b-Library;: + + + + + + StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + + There is no functional difference between the + &b-link-StaticLibrary; and &b-Library; functions. + + + +
+ +
+ Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder + + + + If you want to build a shared library (on POSIX systems) + or a DLL file (on Windows systems), + you use the &b-link-SharedLibrary; function: + + + + + + SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) + + + void f1() { printf("f1.c\n"); } + + + void f2() { printf("f2.c\n"); } + + + void f3() { printf("f3.c\n"); } + + + + + + The output on POSIX: + + + + + scons -Q + + + + + And the output on Windows: + + + + + scons -Q + + + + + Notice again that &SCons; takes care of + building the output file correctly, + adding the -shared option + for a POSIX compilation, + and the /dll option on Windows. + + + +
+ +
+ +
+ Linking with Libraries + + + + Usually, you build a library + because you want to link it with one or more programs. + You link libraries with a program by specifying + the libraries in the &cv-link-LIBS; construction variable, + and by specifying the directory in which + the library will be found in the + &cv-link-LIBPATH; construction variable: + + + + + + + + Library('foo', ['f1.c', 'f2.c', 'f3.c']) + Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Hello, world!\n"); } + + + + + + Notice, of course, that you don't need to specify a library + prefix (like lib) + or suffix (like .a or .lib). + &SCons; uses the correct prefix or suffix for the current system. + + + + + + On a POSIX or Linux system, + a build of the above example would look like: + + + + + scons -Q + + + + + On a Windows system, + a build of the above example would look like: + + + + + scons -Q + + + + + As usual, notice that &SCons; has taken care + of constructing the correct command lines + to link with the specified library on each system. + + + + + + Note also that, + if you only have a single library to link with, + you can specify the library name in single string, + instead of a Python list, + so that: + + + + + Program('prog.c', LIBS='foo', LIBPATH='.') + + + + + is equivalent to: + + + + + Program('prog.c', LIBS=['foo'], LIBPATH='.') + + + + + This is similar to the way that &SCons; + handles either a string or a list to + specify a single source file. + + + +
+ +
+ Finding Libraries: the &cv-LIBPATH; Construction Variable + + + + By default, the linker will only look in + certain system-defined directories for libraries. + &SCons; knows how to look for libraries + in directories that you specify with the + &cv-link-LIBPATH; construction variable. + &cv-LIBPATH; consists of a list of + directory names, like so: + + + + + + Program('prog.c', LIBS = 'm', + LIBPATH = ['/usr/lib', '/usr/local/lib']) + + + int main() { printf("prog.c\n"); } + + + + + + Using a Python list is preferred because it's portable + across systems. Alternatively, you could put all of + the directory names in a single string, separated by the + system-specific path separator character: + a colon on POSIX systems: + + + + + LIBPATH = '/usr/lib:/usr/local/lib' + + + + + or a semi-colon on Windows systems: + + + + + LIBPATH = 'C:\\lib;D:\\lib' + + + + + (Note that Python requires that the backslash + separators in a Windows path name + be escaped within strings.) + + + + + + When the linker is executed, + &SCons; will create appropriate flags + so that the linker will look for + libraries in the same directories as &SCons;. + So on a POSIX or Linux system, + a build of the above example would look like: + + + + + scons -Q + + + + + On a Windows system, + a build of the above example would look like: + + + + + scons -Q + + + + + + Note again that &SCons; has taken care of + the system-specific details of creating + the right command-line options. + + + +
diff --git a/doc/user/libraries.xml b/doc/user/libraries.xml new file mode 100644 index 0000000..e3821ce --- /dev/null +++ b/doc/user/libraries.xml @@ -0,0 +1,451 @@ + + + + + It's often useful to organize large software projects + by collecting parts of the software into one or more libraries. + &SCons; makes it easy to create libraries + and to use them in the programs. + + + +
+ Building Libraries + + + + You build your own libraries by specifying &b-link-Library; + instead of &b-link-Program;: + + + + + Library('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + &SCons; uses the appropriate library prefix and suffix for your system. + So on POSIX or Linux systems, + the above example would build as follows + (although &ranlib; may not be called on all systems): + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + + + + + On a Windows system, + a build of the above example would look like: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fof1.obj /c f1.c /nologo + cl /Fof2.obj /c f2.c /nologo + cl /Fof3.obj /c f3.c /nologo + lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj + + + + + The rules for the target name of the library + are similar to those for programs: + if you don't explicitly specify a target library name, + &SCons; will deduce one from the + name of the first source file specified, + and &SCons; will add an appropriate + file prefix and suffix if you leave them off. + + + +
+ Building Libraries From Source Code or Object Files + + + + The previous example shows building a library from a + list of source files. + You can, however, also give the &b-link-Library; call + object files, + and it will correctly realize + In fact, you can arbitrarily mix source code files + and object files in the source list: + + + + + Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) + + + + + And SCons realizes that only the source code files + must be compiled into object files + before creating the final library: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o f4.o + ranlib libfoo.a + + + + + Of course, in this example, the object files + must already exist for the build to succeed. + See , below, + for information about how you can + build object files explicitly + and include the built files in a library. + + + +
+ +
+ Building Static Libraries Explicitly: the &b-StaticLibrary; Builder + + + + The &b-link-Library; function builds a traditional static library. + If you want to be explicit about the type of library being built, + you can use the synonym &b-link-StaticLibrary; function + instead of &b-Library;: + + + + + StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + There is no functional difference between the + &b-link-StaticLibrary; and &b-Library; functions. + + + +
+ +
+ Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder + + + + If you want to build a shared library (on POSIX systems) + or a DLL file (on Windows systems), + you use the &b-link-SharedLibrary; function: + + + + + SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + The output on POSIX: + + + + + % scons -Q + cc -o f1.os -c f1.c + cc -o f2.os -c f2.c + cc -o f3.os -c f3.c + cc -o libfoo.so -shared f1.os f2.os f3.os + + + + + And the output on Windows: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fof1.obj /c f1.c /nologo + cl /Fof2.obj /c f2.c /nologo + cl /Fof3.obj /c f3.c /nologo + link /nologo /dll /out:foo.dll /implib:foo.lib f1.obj f2.obj f3.obj + RegServerFunc(target, source, env) + + + + + Notice again that &SCons; takes care of + building the output file correctly, + adding the -shared option + for a POSIX compilation, + and the /dll option on Windows. + + + +
+ +
+ +
+ Linking with Libraries + + + + Usually, you build a library + because you want to link it with one or more programs. + You link libraries with a program by specifying + the libraries in the &cv-link-LIBS; construction variable, + and by specifying the directory in which + the library will be found in the + &cv-link-LIBPATH; construction variable: + + + + + + + Library('foo', ['f1.c', 'f2.c', 'f3.c']) + Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') + + + + + Notice, of course, that you don't need to specify a library + prefix (like lib) + or suffix (like .a or .lib). + &SCons; uses the correct prefix or suffix for the current system. + + + + + + On a POSIX or Linux system, + a build of the above example would look like: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog.o -c prog.c + cc -o prog prog.o -L. -lfoo -lbar + + + + + On a Windows system, + a build of the above example would look like: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fof1.obj /c f1.c /nologo + cl /Fof2.obj /c f2.c /nologo + cl /Fof3.obj /c f3.c /nologo + lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj + cl /Foprog.obj /c prog.c /nologo + link /nologo /OUT:prog.exe /LIBPATH:. foo.lib bar.lib prog.obj + + + + + As usual, notice that &SCons; has taken care + of constructing the correct command lines + to link with the specified library on each system. + + + + + + Note also that, + if you only have a single library to link with, + you can specify the library name in single string, + instead of a Python list, + so that: + + + + + Program('prog.c', LIBS='foo', LIBPATH='.') + + + + + is equivalent to: + + + + + Program('prog.c', LIBS=['foo'], LIBPATH='.') + + + + + This is similar to the way that &SCons; + handles either a string or a list to + specify a single source file. + + + +
+ +
+ Finding Libraries: the &cv-LIBPATH; Construction Variable + + + + By default, the linker will only look in + certain system-defined directories for libraries. + &SCons; knows how to look for libraries + in directories that you specify with the + &cv-link-LIBPATH; construction variable. + &cv-LIBPATH; consists of a list of + directory names, like so: + + + + + Program('prog.c', LIBS = 'm', + LIBPATH = ['/usr/lib', '/usr/local/lib']) + + + + + Using a Python list is preferred because it's portable + across systems. Alternatively, you could put all of + the directory names in a single string, separated by the + system-specific path separator character: + a colon on POSIX systems: + + + + + LIBPATH = '/usr/lib:/usr/local/lib' + + + + + or a semi-colon on Windows systems: + + + + + LIBPATH = 'C:\\lib;D:\\lib' + + + + + (Note that Python requires that the backslash + separators in a Windows path name + be escaped within strings.) + + + + + + When the linker is executed, + &SCons; will create appropriate flags + so that the linker will look for + libraries in the same directories as &SCons;. + So on a POSIX or Linux system, + a build of the above example would look like: + + + + + % scons -Q + cc -o prog.o -c prog.c + cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm + + + + + On a Windows system, + a build of the above example would look like: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Foprog.obj /c prog.c /nologo + link /nologo /OUT:prog.exe /LIBPATH:\usr\lib /LIBPATH:\usr\local\lib m.lib prog.obj + + + + + + Note again that &SCons; has taken care of + the system-specific details of creating + the right command-line options. + + + +
diff --git a/doc/user/main.in b/doc/user/main.in new file mode 100644 index 0000000..949a90a --- /dev/null +++ b/doc/user/main.in @@ -0,0 +1,385 @@ + + + + + %version; + + + %scons; + + + %builders-mod; + + + %tools-mod; + + + %variables-mod; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + SCons User Guide &buildversion; + + + Steven + Knight + + + Revision &buildrevision; (&builddate;) + + 2004, 2005, 2006, 2007, 2008 + + + 2004, 2005, 2006, 2007, 2008 + Steven Knight + + + + ©right; + + + version &buildversion; + + + + + Preface + &preface; + + + + Building and Installing &SCons; + &build-install; + + + + Simple Builds + &simple; + + + + Less Simple Things to Do With Builds + &less-simple; + + + + Building and Linking with Libraries + &libraries; + + + + Node Objects + &nodes; + + + + Dependencies + &depends; + + + + Environments + &environments; + + + + + Merging Options into the Environment: the &MergeFlags; Function + &mergeflags; + + + Separating Compile Arguments into their Variables: the &ParseFlags; Function + &parseflags; + + + Finding Installed Library Information: the &ParseConfig; Function + &parseconfig; + + + + Controlling Build Output + &output; + + + + Controlling a Build From the Command Line + &command-line; + + + + Installing Files in Other Directories: the &Install; Builder + &install; + + + + Platform-Independent File System Manipulation + &factories; + + + + Controlling Removal of Targets + &file-removal; + + + + Hierarchical Builds + &hierarchy; + + + + Separating Source and Build Directories + &separate; + + + + Variant Builds + &variants; + + + + + + Writing Your Own Builders + &builders-writing; + + + + Not Writing a Builder: the &Command; Builder + &builders-commands; + + + + Pseudo-Builders: the AddMethod function + &add-method; + + + + + + Writing Scanners + &scanners; + + + + Building From Code Repositories + &repositories; + + + + Multi-Platform Configuration (&Autoconf; Functionality) + &sconf; + + + + + + Caching Built Files + &caching; + + + + Alias Targets + &alias; + + + + Java Builds + &java; + + + + + + Miscellaneous Functionality + &misc; + + + + Troubleshooting + &troubleshoot; + + + + Construction Variables + &variables-xml; + + + + Builders + &builders; + + + + Tools + &tools; + + + + Handling Common Tasks + &tasks; + + + + + diff --git a/doc/user/main.xml b/doc/user/main.xml new file mode 100644 index 0000000..949a90a --- /dev/null +++ b/doc/user/main.xml @@ -0,0 +1,385 @@ + + + + + %version; + + + %scons; + + + %builders-mod; + + + %tools-mod; + + + %variables-mod; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + SCons User Guide &buildversion; + + + Steven + Knight + + + Revision &buildrevision; (&builddate;) + + 2004, 2005, 2006, 2007, 2008 + + + 2004, 2005, 2006, 2007, 2008 + Steven Knight + + + + ©right; + + + version &buildversion; + + + + + Preface + &preface; + + + + Building and Installing &SCons; + &build-install; + + + + Simple Builds + &simple; + + + + Less Simple Things to Do With Builds + &less-simple; + + + + Building and Linking with Libraries + &libraries; + + + + Node Objects + &nodes; + + + + Dependencies + &depends; + + + + Environments + &environments; + + + + + Merging Options into the Environment: the &MergeFlags; Function + &mergeflags; + + + Separating Compile Arguments into their Variables: the &ParseFlags; Function + &parseflags; + + + Finding Installed Library Information: the &ParseConfig; Function + &parseconfig; + + + + Controlling Build Output + &output; + + + + Controlling a Build From the Command Line + &command-line; + + + + Installing Files in Other Directories: the &Install; Builder + &install; + + + + Platform-Independent File System Manipulation + &factories; + + + + Controlling Removal of Targets + &file-removal; + + + + Hierarchical Builds + &hierarchy; + + + + Separating Source and Build Directories + &separate; + + + + Variant Builds + &variants; + + + + + + Writing Your Own Builders + &builders-writing; + + + + Not Writing a Builder: the &Command; Builder + &builders-commands; + + + + Pseudo-Builders: the AddMethod function + &add-method; + + + + + + Writing Scanners + &scanners; + + + + Building From Code Repositories + &repositories; + + + + Multi-Platform Configuration (&Autoconf; Functionality) + &sconf; + + + + + + Caching Built Files + &caching; + + + + Alias Targets + &alias; + + + + Java Builds + &java; + + + + + + Miscellaneous Functionality + &misc; + + + + Troubleshooting + &troubleshoot; + + + + Construction Variables + &variables-xml; + + + + Builders + &builders; + + + + Tools + &tools; + + + + Handling Common Tasks + &tasks; + + + + + diff --git a/doc/user/make.in b/doc/user/make.in new file mode 100644 index 0000000..e778c61 --- /dev/null +++ b/doc/user/make.in @@ -0,0 +1,121 @@ + + + + + + + XXX + + + +
+ Differences Between &Make; and &SCons; + + + + XXX + + + +
+ +
+ Advantages of &SCons; Over &Make; + + + + XXX + + + +
diff --git a/doc/user/make.xml b/doc/user/make.xml new file mode 100644 index 0000000..e778c61 --- /dev/null +++ b/doc/user/make.xml @@ -0,0 +1,121 @@ + + + + + + + XXX + + + +
+ Differences Between &Make; and &SCons; + + + + XXX + + + +
+ +
+ Advantages of &SCons; Over &Make; + + + + XXX + + + +
diff --git a/doc/user/mergeflags.in b/doc/user/mergeflags.in new file mode 100644 index 0000000..b50992b --- /dev/null +++ b/doc/user/mergeflags.in @@ -0,0 +1,137 @@ + + + + + &SCons; construction environments have a &MergeFlags; method + that merges a dictionary of values into the construction environment. + &MergeFlags; treats each value in the dictionary + as a list of options such as one might pass to a command + (such as a compiler or linker). + &MergeFlags; will not duplicate an option + if it already exists in the construction environment variable. + + + + + + &MergeFlags; tries to be intelligent about merging options. + When merging options to any variable + whose name ends in PATH, + &MergeFlags; keeps the leftmost occurrence of the option, + because in typical lists of directory paths, + the first occurrence "wins." + When merging options to any other variable name, + &MergeFlags; keeps the rightmost occurrence of the option, + because in a list of typical command-line options, + the last occurrence "wins." + + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + flags = { 'CCFLAGS' : '-whatever -O3' } + env.MergeFlags(flags) + print env['CCFLAGS'] + + + + + scons -Q + + + + + Note that the default value for &cv-link-CCFLAGS; + + is an internal &SCons; object + which automatically converts + the options we specified as a string into a list. + + + + + + env = Environment() + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + flags = { 'CPPPATH' : ['/usr/opt/include', '/usr/local/include'] } + env.MergeFlags(flags) + print env['CPPPATH'] + + + + + scons -Q + + + + + Note that the default value for &cv-link-CPPPATH; + + is a normal Python list, + so we must specify its values as a list + in the dictionary we pass to the &MergeFlags; function. + + + + + + If &MergeFlags; is passed anything other than a dictionary, + it calls the &ParseFlags; method to convert it into a dictionary. + + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include') + print env['CCFLAGS'] + print env['CPPPATH'] + + + + + scons -Q + + + + + In the combined example above, + &ParseFlags; has sorted the options into their corresponding variables + and returned a dictionary for &MergeFlags; to apply + to the construction variables + in the specified construction environment. + + diff --git a/doc/user/mergeflags.xml b/doc/user/mergeflags.xml new file mode 100644 index 0000000..7edc986 --- /dev/null +++ b/doc/user/mergeflags.xml @@ -0,0 +1,138 @@ + + + + + &SCons; construction environments have a &MergeFlags; method + that merges a dictionary of values into the construction environment. + &MergeFlags; treats each value in the dictionary + as a list of options such as one might pass to a command + (such as a compiler or linker). + &MergeFlags; will not duplicate an option + if it already exists in the construction environment variable. + + + + + + &MergeFlags; tries to be intelligent about merging options. + When merging options to any variable + whose name ends in PATH, + &MergeFlags; keeps the leftmost occurrence of the option, + because in typical lists of directory paths, + the first occurrence "wins." + When merging options to any other variable name, + &MergeFlags; keeps the rightmost occurrence of the option, + because in a list of typical command-line options, + the last occurrence "wins." + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + flags = { 'CCFLAGS' : '-whatever -O3' } + env.MergeFlags(flags) + print env['CCFLAGS'] + + + + % scons -Q + ['-option', '-O1', '-whatever', '-O3'] + scons: `.' is up to date. + + + + + Note that the default value for &cv-link-CCFLAGS; + + is an internal &SCons; object + which automatically converts + the options we specified as a string into a list. + + + + + env = Environment() + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + flags = { 'CPPPATH' : ['/usr/opt/include', '/usr/local/include'] } + env.MergeFlags(flags) + print env['CPPPATH'] + + + + % scons -Q + ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] + scons: `.' is up to date. + + + + + Note that the default value for &cv-link-CPPPATH; + + is a normal Python list, + so we must specify its values as a list + in the dictionary we pass to the &MergeFlags; function. + + + + + + If &MergeFlags; is passed anything other than a dictionary, + it calls the &ParseFlags; method to convert it into a dictionary. + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include') + print env['CCFLAGS'] + print env['CPPPATH'] + + + + % scons -Q + ['-option', '-O1', '-whatever', '-O3'] + ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] + scons: `.' is up to date. + + + + + In the combined example above, + &ParseFlags; has sorted the options into their corresponding variables + and returned a dictionary for &MergeFlags; to apply + to the construction variables + in the specified construction environment. + + diff --git a/doc/user/misc.in b/doc/user/misc.in new file mode 100644 index 0000000..6679134 --- /dev/null +++ b/doc/user/misc.in @@ -0,0 +1,606 @@ + + + + + &SCons; supports a lot of additional functionality + that doesn't readily fit into the other chapters. + + + +
+ Verifying the Python Version: the &EnsurePythonVersion; Function + + + + Although the &SCons; code itself will run + on any Python version 1.5.2 or later, + you are perfectly free to make use of + Python syntax and modules from more modern versions + (for example, Python 2.4 or 2.5) + when writing your &SConscript; files + or your own local modules. + If you do this, it's usually helpful to + configure &SCons; to exit gracefully with an error message + if it's being run with a version of Python + that simply won't work with your code. + This is especially true if you're going to use &SCons; + to build source code that you plan to distribute publicly, + where you can't be sure of the Python version + that an anonymous remote user might use + to try to build your software. + + + + + + &SCons; provides an &EnsurePythonVersion; function for this. + You simply pass it the major and minor versions + numbers of the version of Python you require: + + + + + + + EnsurePythonVersion(2, 5) + + + + + And then &SCons will exit with the following error + message when a user runs it with an unsupported + earlier version of Python: + + + + + + + % scons -Q + Python 2.5 or greater required, but you have Python 2.3.6 + + +
+ +
+ Verifying the SCons Version: the &EnsureSConsVersion; Function + + + + You may, of course, write your &SConscript; files + to use features that were only added in + recent versions of &SCons;. + When you publicly distribute software that is built using &SCons;, + it's helpful to have &SCons; + verify the version being used and + exit gracefully with an error message + if the user's version of &SCons; won't work + with your &SConscript; files. + &SCons; provides an &EnsureSConsVersion; function + that verifies the version of &SCons; + in the same + the &EnsurePythonVersion; function + verifies the version of Python, + by passing in the major and minor versions + numbers of the version of SCons you require: + + + + + + + EnsureSConsVersion(1, 0) + + + + + And then &SCons will exit with the following error + message when a user runs it with an unsupported + earlier version of &SCons;: + + + + + + + % scons -Q + SCons 1.0 or greater required, but you have SCons 0.98.5 + + +
+ +
+ Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function + + + + &SCons; supports an &Exit; function + which can be used to terminate &SCons; + while reading the &SConscript; files, + usually because you've detected a condition + under which it doesn't make sense to proceed: + + + + + + if ARGUMENTS.get('FUTURE'): + print "The FUTURE option is not supported yet!" + Exit(2) + env = Environment() + env.Program('hello.c') + + + hello.c + + + + + scons -Q FUTURE=1 + scons -Q + + + + + The &Exit; function takes as an argument + the (numeric) exit status that you want &SCons; to exit with. + If you don't specify a value, + the default is to exit with 0, + which indicates successful execution. + + + + + + Note that the &Exit; function + is equivalent to calling the Python + sys.exit function + (which the it actually calls), + but because &Exit; is a &SCons; function, + you don't have to import the Python + sys module to use it. + + + +
+ +
+ Searching for Files: the &FindFile; Function + + + + The &FindFile; function searches for a file in a list of directories. + If there is only one directory, it can be given as a simple string. + The function returns a File node if a matching file exists, + or None if no file is found. + (See the documentation for the &Glob; function for an alternative way + of searching for entries in a directory.) + + + + + + # one directory + print FindFile('missing', '.') + t = FindFile('exists', '.') + print t.__class__, t + + + exists + + + + + scons -Q + + + + + # several directories + includes = [ '.', 'include', 'src/include'] + headers = [ 'nonesuch.h', 'config.h', 'private.h', 'dist.h'] + for hdr in headers: + print '%-12s' % ('%s:' % hdr), FindFile(hdr, includes) + + + exists + + + + + + exists + + + + exists + + + + scons -Q + + + + + + + If the file exists in more than one directory, + only the first occurrence is returned. + + + + + + print FindFile('multiple', ['sub1', 'sub2', 'sub3']) + print FindFile('multiple', ['sub2', 'sub3', 'sub1']) + print FindFile('multiple', ['sub3', 'sub1', 'sub2']) + + + + exists + + + + exists + + + + exists + + + + + scons -Q + + + + + + + In addition to existing files, &FindFile; will also find derived files + (that is, non-leaf files) that haven't been built yet. + (Leaf files should already exist, or the build will fail!) + + + + + + # Neither file exists, so build will fail + Command('derived', 'leaf', 'cat >$TARGET $SOURCE') + print FindFile('leaf', '.') + print FindFile('derived', '.') + + + + + scons -Q + + + + + # Only 'leaf' exists + Command('derived', 'leaf', 'cat >$TARGET $SOURCE') + print FindFile('leaf', '.') + print FindFile('derived', '.') + + + leaf + + + + + scons -Q + + + + + If a source file exists, &FindFile; will correctly return the name + in the build directory. + + + + + + # Only 'src/leaf' exists + VariantDir('build', 'src') + print FindFile('leaf', 'build') + + + + leaf + + + + + scons -Q + + +
+ +
+ Handling Nested Lists: the &Flatten; Function + + + + &SCons; supports a &Flatten; function + which takes an input Python sequence + (list or tuple) + and returns a flattened list + containing just the individual elements of + the sequence. + This can be handy when trying to examine + a list composed of the lists + returned by calls to various Builders. + For example, you might collect + object files built in different ways + into one call to the &Program; Builder + by just enclosing them in a list, as follows: + + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + + prog1.c + + + prog2.c + + + + + + Because the Builder calls in &SCons; + flatten their input lists, + this works just fine to build the program: + + + + + scons -Q + + + + + But if you were debugging your build + and wanted to print the absolute path + of each object file in the + objects list, + you might try the following simple approach, + trying to print each Node's + abspath + attribute: + + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in objects: + print object_file.abspath + + + prog1.c + + + prog2.c + + + + + + This does not work as expected + because each call to str + is operating an embedded list returned by + each &Object; call, + not on the underlying Nodes within those lists: + + + + + scons -Q + + + + + The solution is to use the &Flatten; function + so that you can pass each Node to + the str separately: + + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in Flatten(objects): + print object_file.abspath + + + prog1.c + + + prog2.c + + + + + + + % scons -Q + /home/me/project/prog1.o + /home/me/project/prog2.o + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + + +
+ +
+ Finding the Invocation Directory: the &GetLaunchDir; Function + + + + If you need to find the directory from + which the user invoked the &scons; command, + you can use the &GetLaunchDir; function: + + + + + env = Environment( + LAUNCHDIR = GetLaunchDir(), + ) + env.Command('directory_build_info', + '$LAUNCHDIR/build_info' + Copy('$TARGET', '$SOURCE')) + + + + + Because &SCons; is usually invoked from the top-level + directory in which the &SConstruct; file lives, + the Python os.getcwd() + is often equivalent. + However, the &SCons; + -u, + -U + and + -D + command-line options, + when invoked from a subdirectory, + will cause &SCons; to change to the directory + in which the &SConstruct; file is found. + When those options are used, + &GetLaunchDir; will still return the path to the + user's invoking subdirectory, + allowing the &SConscript; configuration + to still get at configuration (or other) files + from the originating directory. + + + +
diff --git a/doc/user/misc.xml b/doc/user/misc.xml new file mode 100644 index 0000000..a2305ee --- /dev/null +++ b/doc/user/misc.xml @@ -0,0 +1,565 @@ + + + + + &SCons; supports a lot of additional functionality + that doesn't readily fit into the other chapters. + + + +
+ Verifying the Python Version: the &EnsurePythonVersion; Function + + + + Although the &SCons; code itself will run + on any Python version 1.5.2 or later, + you are perfectly free to make use of + Python syntax and modules from more modern versions + (for example, Python 2.4 or 2.5) + when writing your &SConscript; files + or your own local modules. + If you do this, it's usually helpful to + configure &SCons; to exit gracefully with an error message + if it's being run with a version of Python + that simply won't work with your code. + This is especially true if you're going to use &SCons; + to build source code that you plan to distribute publicly, + where you can't be sure of the Python version + that an anonymous remote user might use + to try to build your software. + + + + + + &SCons; provides an &EnsurePythonVersion; function for this. + You simply pass it the major and minor versions + numbers of the version of Python you require: + + + + + + + EnsurePythonVersion(2, 5) + + + + + And then &SCons; will exit with the following error + message when a user runs it with an unsupported + earlier version of Python: + + + + + + + % scons -Q + Python 2.5 or greater required, but you have Python 2.3.6 + + +
+ +
+ Verifying the SCons Version: the &EnsureSConsVersion; Function + + + + You may, of course, write your &SConscript; files + to use features that were only added in + recent versions of &SCons;. + When you publicly distribute software that is built using &SCons;, + it's helpful to have &SCons; + verify the version being used and + exit gracefully with an error message + if the user's version of &SCons; won't work + with your &SConscript; files. + &SCons; provides an &EnsureSConsVersion; function + that verifies the version of &SCons; + in the same + the &EnsurePythonVersion; function + verifies the version of Python, + by passing in the major and minor versions + numbers of the version of SCons you require: + + + + + + + EnsureSConsVersion(1, 0) + + + + + And then &SCons; will exit with the following error + message when a user runs it with an unsupported + earlier version of &SCons;: + + + + + + + % scons -Q + SCons 1.0 or greater required, but you have SCons 0.98.5 + + +
+ +
+ Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function + + + + &SCons; supports an &Exit; function + which can be used to terminate &SCons; + while reading the &SConscript; files, + usually because you've detected a condition + under which it doesn't make sense to proceed: + + + + + if ARGUMENTS.get('FUTURE'): + print "The FUTURE option is not supported yet!" + Exit(2) + env = Environment() + env.Program('hello.c') + + + + % scons -Q FUTURE=1 + The FUTURE option is not supported yet! + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + The &Exit; function takes as an argument + the (numeric) exit status that you want &SCons; to exit with. + If you don't specify a value, + the default is to exit with 0, + which indicates successful execution. + + + + + + Note that the &Exit; function + is equivalent to calling the Python + sys.exit function + (which the it actually calls), + but because &Exit; is a &SCons; function, + you don't have to import the Python + sys module to use it. + + + +
+ +
+ Searching for Files: the &FindFile; Function + + + + The &FindFile; function searches for a file in a list of directories. + If there is only one directory, it can be given as a simple string. + The function returns a File node if a matching file exists, + or None if no file is found. + (See the documentation for the &Glob; function for an alternative way + of searching for entries in a directory.) + + + + + # one directory + print FindFile('missing', '.') + t = FindFile('exists', '.') + print t.__class__, t + + + + % scons -Q + None + SCons.Node.FS.File exists + scons: `.' is up to date. + + + + # several directories + includes = [ '.', 'include', 'src/include'] + headers = [ 'nonesuch.h', 'config.h', 'private.h', 'dist.h'] + for hdr in headers: + print '%-12s' % ('%s:' % hdr), FindFile(hdr, includes) + + + + % scons -Q + nonesuch.h: None + config.h: config.h + private.h: src/include/private.h + dist.h: include/dist.h + scons: `.' is up to date. + + + + + + + If the file exists in more than one directory, + only the first occurrence is returned. + + + + + print FindFile('multiple', ['sub1', 'sub2', 'sub3']) + print FindFile('multiple', ['sub2', 'sub3', 'sub1']) + print FindFile('multiple', ['sub3', 'sub1', 'sub2']) + + + + % scons -Q + sub1/multiple + sub2/multiple + sub3/multiple + scons: `.' is up to date. + + + + + + + In addition to existing files, &FindFile; will also find derived files + (that is, non-leaf files) that haven't been built yet. + (Leaf files should already exist, or the build will fail!) + + + + + # Neither file exists, so build will fail + Command('derived', 'leaf', 'cat >$TARGET $SOURCE') + print FindFile('leaf', '.') + print FindFile('derived', '.') + + + + % scons -Q + None + derived + scons: *** [derived] Source `leaf' not found, needed by target `derived'. + + + + # Neither file exists, so build will fail + Command('derived', 'leaf', 'cat >$TARGET $SOURCE') + print FindFile('leaf', '.') + print FindFile('derived', '.') + + # Only 'leaf' exists + Command('derived', 'leaf', 'cat >$TARGET $SOURCE') + print FindFile('leaf', '.') + print FindFile('derived', '.') + + + + % scons -Q + leaf + derived + cat > derived leaf + + + + + If a source file exists, &FindFile; will correctly return the name + in the build directory. + + + + + # Only 'src/leaf' exists + VariantDir('build', 'src') + print FindFile('leaf', 'build') + + + + % scons -Q + build/leaf + scons: `.' is up to date. + + +
+ +
+ Handling Nested Lists: the &Flatten; Function + + + + &SCons; supports a &Flatten; function + which takes an input Python sequence + (list or tuple) + and returns a flattened list + containing just the individual elements of + the sequence. + This can be handy when trying to examine + a list composed of the lists + returned by calls to various Builders. + For example, you might collect + object files built in different ways + into one call to the &Program; Builder + by just enclosing them in a list, as follows: + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + + + + Because the Builder calls in &SCons; + flatten their input lists, + this works just fine to build the program: + + + + + % scons -Q + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + + + + + But if you were debugging your build + and wanted to print the absolute path + of each object file in the + objects list, + you might try the following simple approach, + trying to print each Node's + abspath + attribute: + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in objects: + print object_file.abspath + + + + + This does not work as expected + because each call to str + is operating an embedded list returned by + each &Object; call, + not on the underlying Nodes within those lists: + + + + + % scons -Q + AttributeError: NodeList instance has no attribute 'abspath': + File "/home/my/project/SConstruct", line 8: + print object_file.abspath + + + + + The solution is to use the &Flatten; function + so that you can pass each Node to + the str separately: + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in Flatten(objects): + print object_file.abspath + + + + + + % scons -Q + /home/me/project/prog1.o + /home/me/project/prog2.o + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + + +
+ +
+ Finding the Invocation Directory: the &GetLaunchDir; Function + + + + If you need to find the directory from + which the user invoked the &scons; command, + you can use the &GetLaunchDir; function: + + + + + env = Environment( + LAUNCHDIR = GetLaunchDir(), + ) + env.Command('directory_build_info', + '$LAUNCHDIR/build_info' + Copy('$TARGET', '$SOURCE')) + + + + + Because &SCons; is usually invoked from the top-level + directory in which the &SConstruct; file lives, + the Python os.getcwd() + is often equivalent. + However, the &SCons; + -u, + -U + and + -D + command-line options, + when invoked from a subdirectory, + will cause &SCons; to change to the directory + in which the &SConstruct; file is found. + When those options are used, + &GetLaunchDir; will still return the path to the + user's invoking subdirectory, + allowing the &SConscript; configuration + to still get at configuration (or other) files + from the originating directory. + + + +
diff --git a/doc/user/nodes.in b/doc/user/nodes.in new file mode 100644 index 0000000..c914ce5 --- /dev/null +++ b/doc/user/nodes.in @@ -0,0 +1,386 @@ + + + + + Internally, &SCons; represents all of the files + and directories it knows about as &Nodes;. + These internal objects + (not object files) + can be used in a variety of ways + to make your &SConscript; + files portable and easy to read. + + + +
+ Builder Methods Return Lists of Target Nodes + + + + All builder methods return a list of + &Node; objects that identify the + target file or files that will be built. + These returned &Nodes; can be passed + as arguments to other builder methods. + + + + + + For example, suppose that we want to build + the two object files that make up a program with different options. + This would mean calling the &b-link-Object; + builder once for each object file, + specifying the desired options: + + + + + Object('hello.c', CCFLAGS='-DHELLO') + Object('goodbye.c', CCFLAGS='-DGOODBYE') + + + + + One way to combine these object files + into the resulting program + would be to call the &b-link-Program; + builder with the names of the object files + listed as sources: + + + + + Object('hello.c', CCFLAGS='-DHELLO') + Object('goodbye.c', CCFLAGS='-DGOODBYE') + Program(['hello.o', 'goodbye.o']) + + + + + The problem with specifying the names as strings + is that our &SConstruct; file is no longer portable + across operating systems. + It won't, for example, work on Windows + because the object files there would be + named &hello_obj; and &goodbye_obj;, + not &hello_o; and &goodbye_o;. + + + + + + A better solution is to assign the lists of targets + returned by the calls to the &b-Object; builder to variables, + which we can then concatenate in our + call to the &b-Program; builder: + + + + + + hello_list = Object('hello.c', CCFLAGS='-DHELLO') + goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE') + Program(hello_list + goodbye_list) + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Goodbye, world!\n"); } + + + + + + This makes our &SConstruct; file portable again, + the build output on Linux looking like: + + + + + scons -Q + + + + + And on Windows: + + + + + scons -Q + + + + + We'll see examples of using the list of nodes + returned by builder methods throughout + the rest of this guide. + + + +
+ +
+ Explicitly Creating File and Directory Nodes + + + + It's worth mentioning here that + &SCons; maintains a clear distinction + between Nodes that represent files + and Nodes that represent directories. + &SCons; supports &File; and &Dir; + functions that, respectively, + return a file or directory Node: + + + + + + hello_c = File('hello.c') + Program(hello_c) + + classes = Dir('classes') + Java(classes, 'src') + + + + + + Normally, you don't need to call + &File; or &Dir; directly, + because calling a builder method automatically + treats strings as the names of files or directories, + and translates them into + the Node objects for you. + The &File; and &Dir; functions can come in handy + in situations where you need to explicitly + instruct &SCons; about the type of Node being + passed to a builder or other function, + or unambiguously refer to a specific + file in a directory tree. + + + + + + + There are also times when you may need to + refer to an entry in a file system + without knowing in advance + whether it's a file or a directory. + For those situations, + &SCons; also supports an &Entry; function, + which returns a Node + that can represent either a file or a directory. + + + + + xyzzy = Entry('xyzzy') + + + + + The returned xyzzy Node + will be turned into a file or directory Node + the first time it is used by a builder method + or other function that + requires one vs. the other. + + + +
+ +
+ Printing &Node; File Names + + + + One of the most common things you can do + with a Node is use it to print the + file name that the node represents. + Keep in mind, though, that because the object + returned by a builder call + is a list of Nodes, + you must use Python subscripts + to fetch individual Nodes from the list. + For example, the following &SConstruct; file: + + + + + + object_list = Object('hello.c') + program_list = Program(object_list) + print "The object file is:", object_list[0] + print "The program file is:", program_list[0] + + + int main() { printf("Hello, world!\n"); } + + + + + + Would print the following file names on a POSIX system: + + + + + scons -Q + + + + + And the following file names on a Windows system: + + + + + scons -Q + + + + + Note that in the above example, + the object_list[0] + extracts an actual Node object + from the list, + and the Python print statement + converts the object to a string for printing. + + + +
+ +
+ Using a &Node;'s File Name as a String + + + + Printing a &Node;'s name + as described in the previous section + works because the string representation of a &Node; object + is the name of the file. + If you want to do something other than + print the name of the file, + you can fetch it by using the builtin Python + &str; function. + For example, if you want to use the Python + os.path.exists + to figure out whether a file + exists while the &SConstruct; file + is being read and executed, + you can fetch the string as follows: + + + + + + import os.path + program_list = Program('hello.c') + program_name = str(program_list[0]) + if not os.path.exists(program_name): + print program_name, "does not exist!" + + + int main() { printf("Hello, world!\n"); } + + + + + + Which executes as follows on a POSIX system: + + + + + scons -Q + + +
+ + + + diff --git a/doc/user/nodes.xml b/doc/user/nodes.xml new file mode 100644 index 0000000..46215c4 --- /dev/null +++ b/doc/user/nodes.xml @@ -0,0 +1,401 @@ + + + + + Internally, &SCons; represents all of the files + and directories it knows about as &Nodes;. + These internal objects + (not object files) + can be used in a variety of ways + to make your &SConscript; + files portable and easy to read. + + + +
+ Builder Methods Return Lists of Target Nodes + + + + All builder methods return a list of + &Node; objects that identify the + target file or files that will be built. + These returned &Nodes; can be passed + as arguments to other builder methods. + + + + + + For example, suppose that we want to build + the two object files that make up a program with different options. + This would mean calling the &b-link-Object; + builder once for each object file, + specifying the desired options: + + + + + Object('hello.c', CCFLAGS='-DHELLO') + Object('goodbye.c', CCFLAGS='-DGOODBYE') + + + + + One way to combine these object files + into the resulting program + would be to call the &b-link-Program; + builder with the names of the object files + listed as sources: + + + + + Object('hello.c', CCFLAGS='-DHELLO') + Object('goodbye.c', CCFLAGS='-DGOODBYE') + Program(['hello.o', 'goodbye.o']) + + + + + The problem with specifying the names as strings + is that our &SConstruct; file is no longer portable + across operating systems. + It won't, for example, work on Windows + because the object files there would be + named &hello_obj; and &goodbye_obj;, + not &hello_o; and &goodbye_o;. + + + + + + A better solution is to assign the lists of targets + returned by the calls to the &b-Object; builder to variables, + which we can then concatenate in our + call to the &b-Program; builder: + + + + + hello_list = Object('hello.c', CCFLAGS='-DHELLO') + goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE') + Program(hello_list + goodbye_list) + + + + + This makes our &SConstruct; file portable again, + the build output on Linux looking like: + + + + + % scons -Q + cc -o goodbye.o -c -DGOODBYE goodbye.c + cc -o hello.o -c -DHELLO hello.c + cc -o hello hello.o goodbye.o + + + + + And on Windows: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fogoodbye.obj /c goodbye.c -DGOODBYE + cl /Fohello.obj /c hello.c -DHELLO + link /nologo /OUT:hello.exe hello.obj goodbye.obj + + + + + We'll see examples of using the list of nodes + returned by builder methods throughout + the rest of this guide. + + + +
+ +
+ Explicitly Creating File and Directory Nodes + + + + It's worth mentioning here that + &SCons; maintains a clear distinction + between Nodes that represent files + and Nodes that represent directories. + &SCons; supports &File; and &Dir; + functions that, respectively, + return a file or directory Node: + + + + + hello_c = File('hello.c') + Program(hello_c) + + classes = Dir('classes') + Java(classes, 'src') + + + + + Normally, you don't need to call + &File; or &Dir; directly, + because calling a builder method automatically + treats strings as the names of files or directories, + and translates them into + the Node objects for you. + The &File; and &Dir; functions can come in handy + in situations where you need to explicitly + instruct &SCons; about the type of Node being + passed to a builder or other function, + or unambiguously refer to a specific + file in a directory tree. + + + + + + + There are also times when you may need to + refer to an entry in a file system + without knowing in advance + whether it's a file or a directory. + For those situations, + &SCons; also supports an &Entry; function, + which returns a Node + that can represent either a file or a directory. + + + + + xyzzy = Entry('xyzzy') + + + + + The returned xyzzy Node + will be turned into a file or directory Node + the first time it is used by a builder method + or other function that + requires one vs. the other. + + + +
+ +
+ Printing &Node; File Names + + + + One of the most common things you can do + with a Node is use it to print the + file name that the node represents. + Keep in mind, though, that because the object + returned by a builder call + is a list of Nodes, + you must use Python subscripts + to fetch individual Nodes from the list. + For example, the following &SConstruct; file: + + + + + hello_c = File('hello.c') + Program(hello_c) + + classes = Dir('classes') + Java(classes, 'src') + + object_list = Object('hello.c') + program_list = Program(object_list) + print "The object file is:", object_list[0] + print "The program file is:", program_list[0] + + + + + Would print the following file names on a POSIX system: + + + + + % scons -Q + The object file is: hello.o + The program file is: hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + And the following file names on a Windows system: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + The object file is: hello.obj + The program file is: hello.exe + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:hello.exe hello.obj + + + + + Note that in the above example, + the object_list[0] + extracts an actual Node object + from the list, + and the Python print statement + converts the object to a string for printing. + + + +
+ +
+ Using a &Node;'s File Name as a String + + + + Printing a &Node;'s name + as described in the previous section + works because the string representation of a &Node; object + is the name of the file. + If you want to do something other than + print the name of the file, + you can fetch it by using the builtin Python + &str; function. + For example, if you want to use the Python + os.path.exists + to figure out whether a file + exists while the &SConstruct; file + is being read and executed, + you can fetch the string as follows: + + + + + import os.path + program_list = Program('hello.c') + program_name = str(program_list[0]) + if not os.path.exists(program_name): + print program_name, "does not exist!" + + + + + Which executes as follows on a POSIX system: + + + + + % scons -Q + hello does not exist! + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ + + + diff --git a/doc/user/output.in b/doc/user/output.in new file mode 100644 index 0000000..1f600f3 --- /dev/null +++ b/doc/user/output.in @@ -0,0 +1,681 @@ + + + + + A key aspect of creating a usable build configuration + is providing good output from the build + so its users can readily understand + what the build is doing + and get information about how to control the build. + &SCons; provides several ways of + controlling output from the build configuration + to help make the build + more useful and understandable. + + + +
+ Providing Build Help: the &Help; Function + + + + It's often very useful to be able to give + users some help that describes the + specific targets, build options, etc., + that can be used for your build. + &SCons; provides the &Help; function + to allow you to specify this help text: + + + + + + Help(""" + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + """) + + + + + + (Note the above use of the Python triple-quote syntax, + which comes in very handy for + specifying multi-line strings like help text.) + + + + + + When the &SConstruct; or &SConscript; files + contain such a call to the &Help; function, + the specified help text will be displayed in response to + the &SCons; -h option: + + + + + scons -h + + + + + The &SConscript; files may contain + multiple calls to the &Help; function, + in which case the specified text(s) + will be concatenated when displayed. + This allows you to split up the + help text across multiple &SConscript; files. + In this situation, the order in + which the &SConscript; files are called + will determine the order in which the &Help; functions are called, + which will determine the order in which + the various bits of text will get concatenated. + + + + + + Another use would be to make the help text conditional + on some variable. + For example, suppose you only want to display + a line about building a Windows-only + version of a program when actually + run on Windows. + The following &SConstruct; file: + + + + + + env = Environment() + + Help("\nType: 'scons program' to build the production program.\n") + + if env['PLATFORM'] == 'win32': + Help("\nType: 'scons windebug' to build the Windows debug version.\n") + + + + + + Will display the complete help text on Windows: + + + + + scons -h + + + + + But only show the relevant option on a Linux or UNIX system: + + + + + scons -h + + + + + If there is no &Help; text in the &SConstruct; or + &SConscript; files, + &SCons; will revert to displaying its + standard list that describes the &SCons; command-line + options. + This list is also always displayed whenever + the -H option is used. + + + +
+ +
+ Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables + + + + Sometimes the commands executed + to compile object files or link programs + (or build other targets) + can get very long, + long enough to make it difficult for users + to distinguish error messages or + other important build output + from the commands themselves. + All of the default $*COM variables + that specify the command lines + used to build various types of target files + have a corresponding $*COMSTR variable + that can be set to an alternative + string that will be displayed + when the target is built. + + + + + + For example, suppose you want to + have &SCons; display a + "Compiling" + message whenever it's compiling an object file, + and a + "Linking" + when it's linking an executable. + You could write a &SConstruct; file + that looks like: + + + + + + env = Environment(CCCOMSTR = "Compiling $TARGET", + LINKCOMSTR = "Linking $TARGET") + env.Program('foo.c') + + + foo.c + + + + + + Which would then yield the output: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + + + + + &SCons; performs complete variable substitution + on $*COMSTR variables, + so they have access to all of the + standard variables like &cv-TARGET; &cv-SOURCES;, etc., + as well as any construction variables + that happen to be configured in + the construction environment + used to build a specific target. + + + + + + Of course, sometimes it's still important to + be able to see the exact command + that &SCons; will execute to build a target. + For example, you may simply need to verify + that &SCons; is configured to supply + the right options to the compiler, + or a developer may want to + cut-and-paste a compile command + to add a few options + for a custom test. + + + + + + One common way to give users + control over whether or not + &SCons; should print the actual command line + or a short, configured summary + is to add support for a + VERBOSE + command-line variable to your &SConstruct; file. + A simple configuration for this might look like: + + + + + + env = Environment() + if ARGUMENTS.get('VERBOSE') != "1': + env['CCCOMSTR'] = "Compiling $TARGET" + env['LINKCOMSTR'] = "Linking $TARGET" + env.Program('foo.c') + + + foo.c + + + + + + + By only setting the appropriate + $*COMSTR variables + if the user specifies + VERBOSE=1 + on the command line, + the user has control + over how &SCons; + displays these particular command lines: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + % scons -Q -c + Removed foo.o + Removed foo + % scons -Q VERBOSE=1 + cc -o foo.o -c foo.c + cc -o foo foo.o + + +
+ +
+ Providing Build Progress Output: the &Progress; Function + + + + Another aspect of providing good build output + is to give the user feedback + about what &SCons; is doing + even when nothing is being built at the moment. + This can be especially true for large builds + when most of the targets are already up-to-date. + Because &SCons; can take a long time + making absolutely sure that every + target is, in fact, up-to-date + with respect to a lot of dependency files, + it can be easy for users to mistakenly + conclude that &SCons; is hung + or that there is some other problem with the build. + + + + + + One way to deal with this perception + is to configure &SCons; to print something to + let the user know what it's "thinking about." + The &Progress; function + allows you to specify a string + that will be printed for every file + that &SCons; is "considering" + while it is traversing the dependency graph + to decide what targets are or are not up-to-date. + + + + + + Progress('Evaluating $TARGET\n') + Program('f1.c') + Program('f2.c') + + + f1.c + + + f2.c + + + + + + Note that the &Progress; function does not + arrange for a newline to be printed automatically + at the end of the string (as does the Python + print statement), + and we must specify the + \n + that we want printed at the end of the configured string. + This configuration, then, + will have &SCons; + print that it is Evaluating + each file that it encounters + in turn as it traverses the dependency graph: + + + + + scons -Q + + + + + Of course, normally you don't want to add + all of these additional lines to your build output, + as that can make it difficult for the user + to find errors or other important messages. + A more useful way to display + this progress might be + to have the file names printed + directly to the user's screen, + not to the same standard output + stream where build output is printed, + and to use a carriage return character + (\r) + so that each file name gets re-printed on the same line. + Such a configuration would look like: + + + + + Progress('$TARGET\r', + file=open('/dev/tty', 'w'), + overwrite=True) + Program('f1.c') + Program('f2.c') + + + + + Note that we also specified the + overwrite=True argument + to the &Progress; function, + which causes &SCons; to + "wipe out" the previous string with space characters + before printing the next &Progress; string. + Without the + overwrite=True argument, + a shorter file name would not overwrite + all of the charactes in a longer file name that + precedes it, + making it difficult to tell what the + actual file name is on the output. + Also note that we opened up the + /dev/tty file + for direct access (on POSIX) to + the user's screen. + On Windows, the equivalent would be to open + the con: file name. + + + + + + Also, it's important to know that although you can use + $TARGET to substitute the name of + the node in the string, + the &Progress; function does not + perform general variable substitution + (because there's not necessarily a construction + environment involved in evaluating a node + like a source file, for example). + + + + + + You can also specify a list of strings + to the &Progress; function, + in which case &SCons; will + display each string in turn. + This can be used to implement a "spinner" + by having &SCons; cycle through a + sequence of strings: + + + + + Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) + Program('f1.c') + Program('f2.c') + + + + + Note that here we have also used the + interval= + keyword argument to have &SCons; + only print a new "spinner" string + once every five evaluated nodes. + Using an interval= count, + even with strings that use $TARGET like + our examples above, + can be a good way to lessen the + work that &SCons; expends printing &Progress; strings, + while still giving the user feedback + that indicates &SCons; is still + working on evaluating the build. + + + + + + Lastly, you can have direct control + over how to print each evaluated node + by passing a Python function + (or other Python callable) + to the &Progress function. + Your function will be called + for each evaluated node, + allowing you to + implement more sophisticated logic + like adding a counter: + + + + + + screen = open('/dev/tty', 'w') + count = 0 + def progress_function(node) + count += 1 + screen.write('Node %4d: %s\r' % (count, node)) + + Progress(progress_function) + + + + + + Of course, if you choose, + you could completely ignore the + node argument to the function, + and just print a count, + or anything else you wish. + + + + + + (Note that there's an obvious follow-on question here: + how would you find the total number of nodes + that will be + evaluated so you can tell the user how + close the build is to finishing? + Unfortunately, in the general case, + there isn't a good way to do that, + short of having &SCons; evaluate its + dependency graph twice, + first to count the total and + the second time to actually build the targets. + This would be necessary because + you can't know in advance which + target(s) the user actually requested + to be built. + The entire build may consist of thousands of Nodes, + for example, + but maybe the user specifically requested + that only a single object file be built.) + + + +
+ +
+ Printing Detailed Build Status: the &GetBuildFailures; Function + + + + SCons, like most build tools, returns zero status to + the shell on success and nonzero status on failure. + Sometimes it's useful to give more information about + the build status at the end of the run, for instance + to print an informative message, send an email, or + page the poor slob who broke the build. + + + + + + SCons provides a &GetBuildFailures; method that + you can use in a python atexit function + to get a list of objects describing the actions that failed + while attempting to build targets. There can be more + than one if you're using -j. Here's a + simple example: + + + + + + import atexit + + def print_build_failures(): + from SCons.Script import GetBuildFailures + for bf in GetBuildFailures(): + print "%s failed: %s" % (bf.node, bf.errstr) + atexit.register(print_build_failures) + + + + + + The atexit.register call + registers print_build_failures + as an atexit callback, to be called + before &SCons; exits. When that function is called, + it calls &GetBuildFailures; to fetch the list of failed objects. + See the man page + for the detailed contents of the returned objects; + some of the more useful attributes are + .node, + .errstr, + .filename, and + .command. + The filename is not necessarily + the same file as the node; the + node is the target that was + being built when the error occurred, while the + filenameis the file or dir that + actually caused the error. + Note: only call &GetBuildFailures; at the end of the + build; calling it at any other time is undefined. + + + + + + Here is a more complete example showing how to + turn each element of &GetBuildFailures; into a string: + + + + + + # Make the build fail if we pass fail=1 on the command line + if ARGUMENTS.get('fail', 0): + Command('target', 'source', ['/bin/false']) + + def bf_to_str(bf): + """Convert an element of GetBuildFailures() to a string + in a useful way.""" + import SCons.Errors + if bf is None: # unknown targets product None in list + return '(unknown tgt)' + elif isinstance(bf, SCons.Errors.StopError): + return str(bf) + elif bf.node: + return str(bf.node) + ': ' + bf.errstr + elif bf.filename: + return bf.filename + ': ' + bf.errstr + return 'unknown failure: ' + bf.errstr + import atexit + + def build_status(): + """Convert the build status to a 2-tuple, (status, msg).""" + from SCons.Script import GetBuildFailures + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + status = 'failed' + failures_message = "\n".join(["Failed building %s" % bf_to_str(x) + for x in bf if x is not None]) + else: + # if bf is None, the build completed successfully. + status = 'ok' + failures_message = '' + return (status, failures_message) + + def display_build_status(): + """Display the build status. Called by atexit. + Here you could do all kinds of complicated things.""" + status, failures_message = build_status() + if status == 'failed': + print "FAILED!!!!" # could display alert, ring bell, etc. + elif status == 'ok': + print "Build succeeded." + print failures_message + + atexit.register(display_build_status) + + + + + + When this runs, you'll see the appropriate output: + + + + + scons -Q + scons -Q fail=1 + + +
diff --git a/doc/user/output.xml b/doc/user/output.xml new file mode 100644 index 0000000..89b443b --- /dev/null +++ b/doc/user/output.xml @@ -0,0 +1,703 @@ + + + + + A key aspect of creating a usable build configuration + is providing good output from the build + so its users can readily understand + what the build is doing + and get information about how to control the build. + &SCons; provides several ways of + controlling output from the build configuration + to help make the build + more useful and understandable. + + + +
+ Providing Build Help: the &Help; Function + + + + It's often very useful to be able to give + users some help that describes the + specific targets, build options, etc., + that can be used for your build. + &SCons; provides the &Help; function + to allow you to specify this help text: + + + + + Help(""" + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + """) + + + + + (Note the above use of the Python triple-quote syntax, + which comes in very handy for + specifying multi-line strings like help text.) + + + + + + When the &SConstruct; or &SConscript; files + contain such a call to the &Help; function, + the specified help text will be displayed in response to + the &SCons; -h option: + + + + + % scons -h + scons: Reading SConscript files ... + scons: done reading SConscript files. + + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + + Use scons -H for help about command-line options. + + + + + The &SConscript; files may contain + multiple calls to the &Help; function, + in which case the specified text(s) + will be concatenated when displayed. + This allows you to split up the + help text across multiple &SConscript; files. + In this situation, the order in + which the &SConscript; files are called + will determine the order in which the &Help; functions are called, + which will determine the order in which + the various bits of text will get concatenated. + + + + + + Another use would be to make the help text conditional + on some variable. + For example, suppose you only want to display + a line about building a Windows-only + version of a program when actually + run on Windows. + The following &SConstruct; file: + + + + + env = Environment() + + Help("\nType: 'scons program' to build the production program.\n") + + if env['PLATFORM'] == 'win32': + Help("\nType: 'scons windebug' to build the Windows debug version.\n") + + + + + Will display the complete help text on Windows: + + + + + C:\>scons -h + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + scons: done reading SConscript files. + + Type: 'scons program' to build the production program. + + Type: 'scons windebug' to build the Windows debug version. + + Use scons -H for help about command-line options. + + + + + But only show the relevant option on a Linux or UNIX system: + + + + + % scons -h + scons: Reading SConscript files ... + scons: done reading SConscript files. + + Type: 'scons program' to build the production program. + + Use scons -H for help about command-line options. + + + + + If there is no &Help; text in the &SConstruct; or + &SConscript; files, + &SCons; will revert to displaying its + standard list that describes the &SCons; command-line + options. + This list is also always displayed whenever + the -H option is used. + + + +
+ +
+ Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables + + + + Sometimes the commands executed + to compile object files or link programs + (or build other targets) + can get very long, + long enough to make it difficult for users + to distinguish error messages or + other important build output + from the commands themselves. + All of the default $*COM variables + that specify the command lines + used to build various types of target files + have a corresponding $*COMSTR variable + that can be set to an alternative + string that will be displayed + when the target is built. + + + + + + For example, suppose you want to + have &SCons; display a + "Compiling" + message whenever it's compiling an object file, + and a + "Linking" + when it's linking an executable. + You could write a &SConstruct; file + that looks like: + + + + + env = Environment(CCCOMSTR = "Compiling $TARGET", + LINKCOMSTR = "Linking $TARGET") + env.Program('foo.c') + + + + + Which would then yield the output: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + + + + + &SCons; performs complete variable substitution + on $*COMSTR variables, + so they have access to all of the + standard variables like &cv-TARGET; &cv-SOURCES;, etc., + as well as any construction variables + that happen to be configured in + the construction environment + used to build a specific target. + + + + + + Of course, sometimes it's still important to + be able to see the exact command + that &SCons; will execute to build a target. + For example, you may simply need to verify + that &SCons; is configured to supply + the right options to the compiler, + or a developer may want to + cut-and-paste a compile command + to add a few options + for a custom test. + + + + + + One common way to give users + control over whether or not + &SCons; should print the actual command line + or a short, configured summary + is to add support for a + VERBOSE + command-line variable to your &SConstruct; file. + A simple configuration for this might look like: + + + + + env = Environment() + if ARGUMENTS.get('VERBOSE') != "1': + env['CCCOMSTR'] = "Compiling $TARGET" + env['LINKCOMSTR'] = "Linking $TARGET" + env.Program('foo.c') + + + + + + By only setting the appropriate + $*COMSTR variables + if the user specifies + VERBOSE=1 + on the command line, + the user has control + over how &SCons; + displays these particular command lines: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + % scons -Q -c + Removed foo.o + Removed foo + % scons -Q VERBOSE=1 + cc -o foo.o -c foo.c + cc -o foo foo.o + + +
+ +
+ Providing Build Progress Output: the &Progress; Function + + + + Another aspect of providing good build output + is to give the user feedback + about what &SCons; is doing + even when nothing is being built at the moment. + This can be especially true for large builds + when most of the targets are already up-to-date. + Because &SCons; can take a long time + making absolutely sure that every + target is, in fact, up-to-date + with respect to a lot of dependency files, + it can be easy for users to mistakenly + conclude that &SCons; is hung + or that there is some other problem with the build. + + + + + + One way to deal with this perception + is to configure &SCons; to print something to + let the user know what it's "thinking about." + The &Progress; function + allows you to specify a string + that will be printed for every file + that &SCons; is "considering" + while it is traversing the dependency graph + to decide what targets are or are not up-to-date. + + + + + Progress('Evaluating $TARGET\n') + Program('f1.c') + Program('f2.c') + + + + + Note that the &Progress; function does not + arrange for a newline to be printed automatically + at the end of the string (as does the Python + print statement), + and we must specify the + \n + that we want printed at the end of the configured string. + This configuration, then, + will have &SCons; + print that it is Evaluating + each file that it encounters + in turn as it traverses the dependency graph: + + + + + % scons -Q + Evaluating SConstruct + Evaluating f1.c + Evaluating f1.o + cc -o f1.o -c f1.c + Evaluating f1 + cc -o f1 f1.o + Evaluating f2.c + Evaluating f2.o + cc -o f2.o -c f2.c + Evaluating f2 + cc -o f2 f2.o + Evaluating . + + + + + Of course, normally you don't want to add + all of these additional lines to your build output, + as that can make it difficult for the user + to find errors or other important messages. + A more useful way to display + this progress might be + to have the file names printed + directly to the user's screen, + not to the same standard output + stream where build output is printed, + and to use a carriage return character + (\r) + so that each file name gets re-printed on the same line. + Such a configuration would look like: + + + + + Progress('$TARGET\r', + file=open('/dev/tty', 'w'), + overwrite=True) + Program('f1.c') + Program('f2.c') + + + + + Note that we also specified the + overwrite=True argument + to the &Progress; function, + which causes &SCons; to + "wipe out" the previous string with space characters + before printing the next &Progress; string. + Without the + overwrite=True argument, + a shorter file name would not overwrite + all of the charactes in a longer file name that + precedes it, + making it difficult to tell what the + actual file name is on the output. + Also note that we opened up the + /dev/tty file + for direct access (on POSIX) to + the user's screen. + On Windows, the equivalent would be to open + the con: file name. + + + + + + Also, it's important to know that although you can use + $TARGET to substitute the name of + the node in the string, + the &Progress; function does not + perform general variable substitution + (because there's not necessarily a construction + environment involved in evaluating a node + like a source file, for example). + + + + + + You can also specify a list of strings + to the &Progress; function, + in which case &SCons; will + display each string in turn. + This can be used to implement a "spinner" + by having &SCons; cycle through a + sequence of strings: + + + + + Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) + Program('f1.c') + Program('f2.c') + + + + + Note that here we have also used the + interval= + keyword argument to have &SCons; + only print a new "spinner" string + once every five evaluated nodes. + Using an interval= count, + even with strings that use $TARGET like + our examples above, + can be a good way to lessen the + work that &SCons; expends printing &Progress; strings, + while still giving the user feedback + that indicates &SCons; is still + working on evaluating the build. + + + + + + Lastly, you can have direct control + over how to print each evaluated node + by passing a Python function + (or other Python callable) + to the &Progress; function. + Your function will be called + for each evaluated node, + allowing you to + implement more sophisticated logic + like adding a counter: + + + + + screen = open('/dev/tty', 'w') + count = 0 + def progress_function(node) + count += 1 + screen.write('Node %4d: %s\r' % (count, node)) + + Progress(progress_function) + + + + + Of course, if you choose, + you could completely ignore the + node argument to the function, + and just print a count, + or anything else you wish. + + + + + + (Note that there's an obvious follow-on question here: + how would you find the total number of nodes + that will be + evaluated so you can tell the user how + close the build is to finishing? + Unfortunately, in the general case, + there isn't a good way to do that, + short of having &SCons; evaluate its + dependency graph twice, + first to count the total and + the second time to actually build the targets. + This would be necessary because + you can't know in advance which + target(s) the user actually requested + to be built. + The entire build may consist of thousands of Nodes, + for example, + but maybe the user specifically requested + that only a single object file be built.) + + + +
+ +
+ Printing Detailed Build Status: the &GetBuildFailures; Function + + + + SCons, like most build tools, returns zero status to + the shell on success and nonzero status on failure. + Sometimes it's useful to give more information about + the build status at the end of the run, for instance + to print an informative message, send an email, or + page the poor slob who broke the build. + + + + + + SCons provides a &GetBuildFailures; method that + you can use in a python atexit function + to get a list of objects describing the actions that failed + while attempting to build targets. There can be more + than one if you're using -j. Here's a + simple example: + + + + + import atexit + + def print_build_failures(): + from SCons.Script import GetBuildFailures + for bf in GetBuildFailures(): + print "%s failed: %s" % (bf.node, bf.errstr) + atexit.register(print_build_failures) + + + + + The atexit.register call + registers print_build_failures + as an atexit callback, to be called + before &SCons; exits. When that function is called, + it calls &GetBuildFailures; to fetch the list of failed objects. + See the man page + for the detailed contents of the returned objects; + some of the more useful attributes are + .node, + .errstr, + .filename, and + .command. + The filename is not necessarily + the same file as the node; the + node is the target that was + being built when the error occurred, while the + filenameis the file or dir that + actually caused the error. + Note: only call &GetBuildFailures; at the end of the + build; calling it at any other time is undefined. + + + + + + Here is a more complete example showing how to + turn each element of &GetBuildFailures; into a string: + + + + + # Make the build fail if we pass fail=1 on the command line + if ARGUMENTS.get('fail', 0): + Command('target', 'source', ['/bin/false']) + + def bf_to_str(bf): + """Convert an element of GetBuildFailures() to a string + in a useful way.""" + import SCons.Errors + if bf is None: # unknown targets product None in list + return '(unknown tgt)' + elif isinstance(bf, SCons.Errors.StopError): + return str(bf) + elif bf.node: + return str(bf.node) + ': ' + bf.errstr + elif bf.filename: + return bf.filename + ': ' + bf.errstr + return 'unknown failure: ' + bf.errstr + import atexit + + def build_status(): + """Convert the build status to a 2-tuple, (status, msg).""" + from SCons.Script import GetBuildFailures + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + status = 'failed' + failures_message = "\n".join(["Failed building %s" % bf_to_str(x) + for x in bf if x is not None]) + else: + # if bf is None, the build completed successfully. + status = 'ok' + failures_message = '' + return (status, failures_message) + + def display_build_status(): + """Display the build status. Called by atexit. + Here you could do all kinds of complicated things.""" + status, failures_message = build_status() + if status == 'failed': + print "FAILED!!!!" # could display alert, ring bell, etc. + elif status == 'ok': + print "Build succeeded." + print failures_message + + atexit.register(display_build_status) + + + + + When this runs, you'll see the appropriate output: + + + + + % scons -Q + scons: `.' is up to date. + Build succeeded. + % scons -Q fail=1 + scons: *** [target] Source `source' not found, needed by target `target'. + FAILED!!!! + Failed building target: Source `source' not found, needed by target `target'. + + +
diff --git a/doc/user/parseconfig.in b/doc/user/parseconfig.in new file mode 100644 index 0000000..db97c35 --- /dev/null +++ b/doc/user/parseconfig.in @@ -0,0 +1,114 @@ + + + + + Configuring the right options to build programs to work with + libraries--especially shared libraries--that are available + on POSIX systems can be very complicated. + To help this situation, + various utilies with names that end in config + return the command-line options for the GNU Compiler Collection (GCC) + that are needed to use these libraries; + for example, the command-line options + to use a library named lib + would be found by calling a utility named lib-config. + + + + + + A more recent convention is that these options + are available from the generic pkg-config program, + which has common framework, error handling, and the like, + so that all the package creator has to do is provide the set of strings + for his particular package. + + + + + + &SCons; construction environments have a &ParseConfig; method + that executes a *config utility + (either pkg-config or a + more specific utility) + and configures the appropriate construction variables + in the environment + based on the command-line options + returned by the specified command. + + + + + + env = Environment() + env['CPPPATH'] = ['/lib/compat'] + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + + + + + + &SCons; will execute the specified command string, + parse the resultant flags, + and add the flags to the appropriate environment variables. + + + + + scons -Q + + + + + In the example above, &SCons; has added the include directory to + CPPPATH. + (Depending upon what other flags are emitted by the + pkg-config command, + other variables may have been extended as well.) + + + + + + Note that the options are merged with existing options using + the &MergeFlags; method, + so that each option only occurs once in the construction variable: + + + + + + env = Environment() + env.ParseConfig("pkg-config x11 --cflags --libs") + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + + + + + scons -Q + diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml new file mode 100644 index 0000000..1f85ad0 --- /dev/null +++ b/doc/user/parseconfig.xml @@ -0,0 +1,132 @@ + + + + + Configuring the right options to build programs to work with + libraries--especially shared libraries--that are available + on POSIX systems can be very complicated. + To help this situation, + various utilies with names that end in config + return the command-line options for the GNU Compiler Collection (GCC) + that are needed to use these libraries; + for example, the command-line options + to use a library named lib + would be found by calling a utility named lib-config. + + + + + + A more recent convention is that these options + are available from the generic pkg-config program, + which has common framework, error handling, and the like, + so that all the package creator has to do is provide the set of strings + for his particular package. + + + + + + &SCons; construction environments have a &ParseConfig; method + that executes a *config utility + (either pkg-config or a + more specific utility) + and configures the appropriate construction variables + in the environment + based on the command-line options + returned by the specified command. + + + + + env = Environment() + env['CPPPATH'] = ['/lib/compat'] + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + + + + + &SCons; will execute the specified command string, + parse the resultant flags, + and add the flags to the appropriate environment variables. + + + + + % scons -Q + Package x11 was not found in the pkg-config search path. + Perhaps you should add the directory containing `x11.pc' + to the PKG_CONFIG_PATH environment variable + No package 'x11' found + OSError: 'pkg-config x11 --cflags --libs' exited 1: + File "/home/my/project/SConstruct", line 3: + env.ParseConfig("pkg-config x11 --cflags --libs") + File "bootstrap/src/engine/SCons/Environment.py", line 1474: + None + File "bootstrap/src/engine/SCons/Environment.py", line 593: + None + + + + + In the example above, &SCons; has added the include directory to + CPPPATH. + (Depending upon what other flags are emitted by the + pkg-config command, + other variables may have been extended as well.) + + + + + + Note that the options are merged with existing options using + the &MergeFlags; method, + so that each option only occurs once in the construction variable: + + + + + env = Environment() + env.ParseConfig("pkg-config x11 --cflags --libs") + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + + + + % scons -Q + Package x11 was not found in the pkg-config search path. + Perhaps you should add the directory containing `x11.pc' + to the PKG_CONFIG_PATH environment variable + No package 'x11' found + OSError: 'pkg-config x11 --cflags --libs' exited 1: + File "/home/my/project/SConstruct", line 2: + env.ParseConfig("pkg-config x11 --cflags --libs") + File "bootstrap/src/engine/SCons/Environment.py", line 1474: + None + File "bootstrap/src/engine/SCons/Environment.py", line 593: + None + diff --git a/doc/user/parseflags.in b/doc/user/parseflags.in new file mode 100644 index 0000000..b21df4f --- /dev/null +++ b/doc/user/parseflags.in @@ -0,0 +1,184 @@ + + + + + &SCons; has a bewildering array of construction variables + for different types of options when building programs. + Sometimes you may not know exactly which variable + should be used for a particular option. + + + + + + &SCons; construction environments have a &ParseFlags; method + that takes a set of typical command-line options + and distrbutes them into the appropriate construction variables. + Historically, it was created to support the &ParseConfig; method, + so it focuses on options used by the GNU Compiler Collection (GCC) + for the C and C++ toolchains. + + + + + + &ParseFlags; returns a dictionary containing the options + distributed into their respective construction variables. + Normally, this dictionary would be passed to &MergeFlags; + to merge the options into a &consenv;, + but the dictionary can be edited if desired to provide + additional functionality. + (Note that if the flags are not going to be edited, + calling &MergeFlags; with the options directly + will avoid an additional step.) + + + + + + env = Environment() + d = env.ParseFlags("-I/opt/include -L/opt/lib -lfoo") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + Note that if the options are limited to generic types + like those above, + they will be correctly translated for other platform types: + + + + + scons -Q + + + + + Since the assumption is that the flags are used for the GCC toolchain, + unrecognized flags are placed in &cv-link-CCFLAGS; + so they will be used for both C and C++ compiles: + + + + + + env = Environment() + d = env.ParseFlags("-whatever") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + &ParseFlags; will also accept a (recursive) list of strings as input; + the list is flattened before the strings are processed: + + + + + + env = Environment() + d = env.ParseFlags(["-I/opt/include", ["-L/opt/lib", "-lfoo"]]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + If a string begins with a "!" (an exclamation mark, often called a bang), + the string is passed to the shell for execution. + The output of the command is then parsed: + + + + + + env = Environment() + d = env.ParseFlags(["!echo -I/opt/include", "!echo -L/opt/lib", "-lfoo"]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + &ParseFlags; is regularly updated for new options; + consult the man page for details about those currently recognized. + + diff --git a/doc/user/parseflags.xml b/doc/user/parseflags.xml new file mode 100644 index 0000000..6900291 --- /dev/null +++ b/doc/user/parseflags.xml @@ -0,0 +1,199 @@ + + + + + &SCons; has a bewildering array of construction variables + for different types of options when building programs. + Sometimes you may not know exactly which variable + should be used for a particular option. + + + + + + &SCons; construction environments have a &ParseFlags; method + that takes a set of typical command-line options + and distrbutes them into the appropriate construction variables. + Historically, it was created to support the &ParseConfig; method, + so it focuses on options used by the GNU Compiler Collection (GCC) + for the C and C++ toolchains. + + + + + + &ParseFlags; returns a dictionary containing the options + distributed into their respective construction variables. + Normally, this dictionary would be passed to &MergeFlags; + to merge the options into a &consenv;, + but the dictionary can be edited if desired to provide + additional functionality. + (Note that if the flags are not going to be edited, + calling &MergeFlags; with the options directly + will avoid an additional step.) + + + + + env = Environment() + d = env.ParseFlags("-I/opt/include -L/opt/lib -lfoo") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cc -o f1.o -c -I/opt/include f1.c + cc -o f1 f1.o -L/opt/lib -lfoo + + + + + Note that if the options are limited to generic types + like those above, + they will be correctly translated for other platform types: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cl /Fof1.obj /c f1.c /nologo /I\opt\include + link /nologo /OUT:f1.exe /LIBPATH:\opt\lib foo.lib f1.obj + + + + + Since the assumption is that the flags are used for the GCC toolchain, + unrecognized flags are placed in &cv-link-CCFLAGS; + so they will be used for both C and C++ compiles: + + + + + env = Environment() + d = env.ParseFlags("-whatever") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CCFLAGS -whatever + cc -o f1.o -c -whatever f1.c + cc -o f1 f1.o + + + + + &ParseFlags; will also accept a (recursive) list of strings as input; + the list is flattened before the strings are processed: + + + + + env = Environment() + d = env.ParseFlags(["-I/opt/include", ["-L/opt/lib", "-lfoo"]]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cc -o f1.o -c -I/opt/include f1.c + cc -o f1 f1.o -L/opt/lib -lfoo + + + + + If a string begins with a "!" (an exclamation mark, often called a bang), + the string is passed to the shell for execution. + The output of the command is then parsed: + + + + + env = Environment() + d = env.ParseFlags(["!echo -I/opt/include", "!echo -L/opt/lib", "-lfoo"]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cc -o f1.o -c -I/opt/include f1.c + cc -o f1 f1.o -L/opt/lib -lfoo + + + + + &ParseFlags; is regularly updated for new options; + consult the man page for details about those currently recognized. + + diff --git a/doc/user/preface.in b/doc/user/preface.in new file mode 100644 index 0000000..de4cb43 --- /dev/null +++ b/doc/user/preface.in @@ -0,0 +1,426 @@ + + + + + Thank you for taking the time to read about &SCons;. + &SCons; is a next-generation + software construction tool, + or make tool--that is, a software utility + for building software (or other files) + and keeping built software up-to-date + whenever the underlying input files change. + + + + + + The most distinctive thing about &SCons; + is that its configuration files are + actually scripts, + written in the &Python; programming language. + This is in contrast to most alternative build tools, + which typically invent a new language to + configure the build. + &SCons; still has a learning curve, of course, + because you have to know what functions to call + to set up your build properly, + but the underlying syntax used should be familiar + to anyone who has ever looked at a Python script. + + + + + + Paradoxically, + using Python as the configuration file format + makes &SCons; + easier + for non-programmers to learn + than the cryptic languages of other build tools, + which are usually invented by programmers for other programmers. + This is in no small part due to the + consistency and readability that are hallmarks of Python. + It just so happens that making a real, live + scripting language the basis for the + configuration files + makes it a snap for more accomplished programmers + to do more complicated things with builds, + as necessary. + + + + + +
+ &SCons; Principles + + + + There are a few overriding principles + we try to live up to in designing and implementing &SCons: + + + + + + + Correctness + + + + + First and foremost, + by default, &SCons; guarantees a correct build + even if it means sacrificing performance a little. + We strive to guarantee the build is correct + regardless of how the software being built is structured, + how it may have been written, + or how unusual the tools are that build it. + + + + + + + Performance + + + + + Given that the build is correct, + we try to make &SCons; build software + as quickly as possible. + In particular, wherever we may have needed to slow + down the default &SCons; behavior to guarantee a correct build, + we also try to make it easy to speed up &SCons; + through optimization options that let you trade off + guaranteed correctness in all end cases for + a speedier build in the usual cases. + + + + + + + Convenience + + + + + &SCons; tries to do as much for you out of the box as reasonable, + including detecting the right tools on your system + and using them correctly to build the software. + + + + + + + + + + In a nutshell, we try hard to make &SCons; just + "do the right thing" and build software correctly, + with a minimum of hassles. + + + +
+ + + + + +
+ A Caveat About This Guide's Completeness + + + + One word of warning as you read through this Guide: + Like too much Open Source software out there, + the &SCons; documentation isn't always + kept up-to-date with the available features. + In other words, + there's a lot that &SCons; can do that + isn't yet covered in this User's Guide. + (Come to think of it, + that also describes a lot of proprietary software, doesn't it?) + + + + + + Although this User's Guide isn't as complete as we'd like it to be, + our development process does emphasize + making sure that the &SCons; man page is kept up-to-date + with new features. + So if you're trying to figure out how to do something + that &SCons; supports + but can't find enough (or any) information here, + it would be worth your while to look + at the man page to see if the information is covered there. + And if you do, + maybe you'd even consider contributing + a section to the User's Guide + so the next person looking for + that information won't have to + go through the same thing...? + + + +
+ +
+ Acknowledgements + + + + &SCons; would not exist without a lot of help + from a lot of people, + many of whom may not even be aware + that they helped or served as inspiration. + So in no particular order, + and at the risk of leaving out someone: + + + + + + First and foremost, + &SCons; owes a tremendous debt to Bob Sidebotham, + the original author of the classic Perl-based &Cons; tool + which Bob first released to the world back around 1996. + Bob's work on Cons classic provided the underlying architecture + and model of specifying a build configuration + using a real scripting language. + My real-world experience working on Cons + informed many of the design decisions in SCons, + including the improved parallel build support, + making Builder objects easily definable by users, + and separating the build engine from the wrapping interface. + + + + + + Greg Wilson was instrumental in getting + &SCons; started as a real project + when he initiated the Software Carpentry design + competition in February 2000. + Without that nudge, + marrying the advantages of the Cons classic + architecture with the readability of Python + might have just stayed no more than a nice idea. + + + + + + The entire &SCons; team have been + absolutely wonderful to work with, + and &SCons; would be nowhere near as useful a + tool without the energy, enthusiasm + and time people have contributed over the past few years. + The "core team" + of Chad Austin, Anthony Roach, + Bill Deegan, Charles Crain, Steve Leblanc, Greg Noel, + Gary Oberbrunner, Greg Spencer and Christoph Wiedemann + have been great about reviewing my (and other) changes + and catching problems before they get in the code base. + Of particular technical note: + Anthony's outstanding and innovative work on the tasking engine + has given &SCons; a vastly superior parallel build model; + Charles has been the master of the crucial Node infrastructure; + Christoph's work on the Configure infrastructure + has added crucial Autoconf-like functionality; + and Greg has provided excellent support + for Microsoft Visual Studio. + + + + + + Special thanks to David Snopek for contributing + his underlying "Autoscons" code that formed + the basis of Christoph's work with the Configure functionality. + David was extremely generous in making + this code available to &SCons;, + given that he initially released it under the GPL + and &SCons; is released under a less-restrictive MIT-style license. + + + + + + + + Thanks to Peter Miller + for his splendid change management system, &Aegis;, + which has provided the &SCons; project + with a robust development methodology from day one, + and which showed me how you could + integrate incremental regression tests into + a practical development cycle + (years before eXtreme Programming arrived on the scene). + + + + + + And last, thanks to Guido van Rossum + for his elegant scripting language, + which is the basis not only for the &SCons; implementation, + but for the interface itself. + + + +
+ +
+ Contact + + + + The best way to contact people involved with SCons, + including the author, + is through the SCons mailing lists. + + + + + + If you want to ask general questions about how to use &SCons; + send email to &scons-users;. + + + + + + If you want to contact the &SCons; development community directly, + send email to &scons-devel;. + + + + + + If you want to receive announcements about &SCons, + join the low-volume &scons-announce; mailing list. + + + +
diff --git a/doc/user/preface.xml b/doc/user/preface.xml new file mode 100644 index 0000000..2c6d8f4 --- /dev/null +++ b/doc/user/preface.xml @@ -0,0 +1,426 @@ + + + + + Thank you for taking the time to read about &SCons;. + &SCons; is a next-generation + software construction tool, + or make tool--that is, a software utility + for building software (or other files) + and keeping built software up-to-date + whenever the underlying input files change. + + + + + + The most distinctive thing about &SCons; + is that its configuration files are + actually scripts, + written in the &Python; programming language. + This is in contrast to most alternative build tools, + which typically invent a new language to + configure the build. + &SCons; still has a learning curve, of course, + because you have to know what functions to call + to set up your build properly, + but the underlying syntax used should be familiar + to anyone who has ever looked at a Python script. + + + + + + Paradoxically, + using Python as the configuration file format + makes &SCons; + easier + for non-programmers to learn + than the cryptic languages of other build tools, + which are usually invented by programmers for other programmers. + This is in no small part due to the + consistency and readability that are hallmarks of Python. + It just so happens that making a real, live + scripting language the basis for the + configuration files + makes it a snap for more accomplished programmers + to do more complicated things with builds, + as necessary. + + + + + +
+ &SCons; Principles + + + + There are a few overriding principles + we try to live up to in designing and implementing &SCons;: + + + + + + + Correctness + + + + + First and foremost, + by default, &SCons; guarantees a correct build + even if it means sacrificing performance a little. + We strive to guarantee the build is correct + regardless of how the software being built is structured, + how it may have been written, + or how unusual the tools are that build it. + + + + + + + Performance + + + + + Given that the build is correct, + we try to make &SCons; build software + as quickly as possible. + In particular, wherever we may have needed to slow + down the default &SCons; behavior to guarantee a correct build, + we also try to make it easy to speed up &SCons; + through optimization options that let you trade off + guaranteed correctness in all end cases for + a speedier build in the usual cases. + + + + + + + Convenience + + + + + &SCons; tries to do as much for you out of the box as reasonable, + including detecting the right tools on your system + and using them correctly to build the software. + + + + + + + + + + In a nutshell, we try hard to make &SCons; just + "do the right thing" and build software correctly, + with a minimum of hassles. + + + +
+ + + + + +
+ A Caveat About This Guide's Completeness + + + + One word of warning as you read through this Guide: + Like too much Open Source software out there, + the &SCons; documentation isn't always + kept up-to-date with the available features. + In other words, + there's a lot that &SCons; can do that + isn't yet covered in this User's Guide. + (Come to think of it, + that also describes a lot of proprietary software, doesn't it?) + + + + + + Although this User's Guide isn't as complete as we'd like it to be, + our development process does emphasize + making sure that the &SCons; man page is kept up-to-date + with new features. + So if you're trying to figure out how to do something + that &SCons; supports + but can't find enough (or any) information here, + it would be worth your while to look + at the man page to see if the information is covered there. + And if you do, + maybe you'd even consider contributing + a section to the User's Guide + so the next person looking for + that information won't have to + go through the same thing...? + + + +
+ +
+ Acknowledgements + + + + &SCons; would not exist without a lot of help + from a lot of people, + many of whom may not even be aware + that they helped or served as inspiration. + So in no particular order, + and at the risk of leaving out someone: + + + + + + First and foremost, + &SCons; owes a tremendous debt to Bob Sidebotham, + the original author of the classic Perl-based &Cons; tool + which Bob first released to the world back around 1996. + Bob's work on Cons classic provided the underlying architecture + and model of specifying a build configuration + using a real scripting language. + My real-world experience working on Cons + informed many of the design decisions in SCons, + including the improved parallel build support, + making Builder objects easily definable by users, + and separating the build engine from the wrapping interface. + + + + + + Greg Wilson was instrumental in getting + &SCons; started as a real project + when he initiated the Software Carpentry design + competition in February 2000. + Without that nudge, + marrying the advantages of the Cons classic + architecture with the readability of Python + might have just stayed no more than a nice idea. + + + + + + The entire &SCons; team have been + absolutely wonderful to work with, + and &SCons; would be nowhere near as useful a + tool without the energy, enthusiasm + and time people have contributed over the past few years. + The "core team" + of Chad Austin, Anthony Roach, + Bill Deegan, Charles Crain, Steve Leblanc, Greg Noel, + Gary Oberbrunner, Greg Spencer and Christoph Wiedemann + have been great about reviewing my (and other) changes + and catching problems before they get in the code base. + Of particular technical note: + Anthony's outstanding and innovative work on the tasking engine + has given &SCons; a vastly superior parallel build model; + Charles has been the master of the crucial Node infrastructure; + Christoph's work on the Configure infrastructure + has added crucial Autoconf-like functionality; + and Greg has provided excellent support + for Microsoft Visual Studio. + + + + + + Special thanks to David Snopek for contributing + his underlying "Autoscons" code that formed + the basis of Christoph's work with the Configure functionality. + David was extremely generous in making + this code available to &SCons;, + given that he initially released it under the GPL + and &SCons; is released under a less-restrictive MIT-style license. + + + + + + + + Thanks to Peter Miller + for his splendid change management system, &Aegis;, + which has provided the &SCons; project + with a robust development methodology from day one, + and which showed me how you could + integrate incremental regression tests into + a practical development cycle + (years before eXtreme Programming arrived on the scene). + + + + + + And last, thanks to Guido van Rossum + for his elegant scripting language, + which is the basis not only for the &SCons; implementation, + but for the interface itself. + + + +
+ +
+ Contact + + + + The best way to contact people involved with SCons, + including the author, + is through the SCons mailing lists. + + + + + + If you want to ask general questions about how to use &SCons; + send email to &scons-users;. + + + + + + If you want to contact the &SCons; development community directly, + send email to &scons-devel;. + + + + + + If you want to receive announcements about &SCons;, + join the low-volume &scons-announce; mailing list. + + + +
diff --git a/doc/user/python.in b/doc/user/python.in new file mode 100644 index 0000000..aa49030 --- /dev/null +++ b/doc/user/python.in @@ -0,0 +1,154 @@ + + + diff --git a/doc/user/python.xml b/doc/user/python.xml new file mode 100644 index 0000000..aa49030 --- /dev/null +++ b/doc/user/python.xml @@ -0,0 +1,154 @@ + + + diff --git a/doc/user/repositories.in b/doc/user/repositories.in new file mode 100644 index 0000000..8c97b02 --- /dev/null +++ b/doc/user/repositories.in @@ -0,0 +1,641 @@ + + + + + Often, a software project will have + one or more central repositories, + directory trees that contain + source code, or derived files, or both. + You can eliminate additional unnecessary + rebuilds of files by having &SCons; + use files from one or more code repositories + to build files in your local build tree. + + + +
+ The &Repository; Method + + + + + + It's often useful to allow multiple programmers working + on a project to build software from + source files and/or derived files that + are stored in a centrally-accessible repository, + a directory copy of the source code tree. + (Note that this is not the sort of repository + maintained by a source code management system + like BitKeeper, CVS, or Subversion.) + + You use the &Repository; method + to tell &SCons; to search one or more + central code repositories (in order) + for any source files and derived files + that are not present in the local build tree: + + + + + + env = Environment() + env.Program('hello.c') + Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') + + + int main() { printf("Hello, world!\n"); } + + + + + + Multiple calls to the &Repository; method + will simply add repositories to the global list + that &SCons; maintains, + with the exception that &SCons; will automatically eliminate + the current directory and any non-existent + directories from the list. + + + +
+ +
+ Finding source files in repositories + + + + The above example + specifies that &SCons; + will first search for files under + the /usr/repository1 tree + and next under the /usr/repository2 tree. + &SCons; expects that any files it searches + for will be found in the same position + relative to the top-level directory. + In the above example, if the &hello_c; file is not + found in the local build tree, + &SCons; will search first for + a /usr/repository1/hello.c file + and then for a /usr/repository2/hello.c file + to use in its place. + + + + + + So given the &SConstruct; file above, + if the &hello_c; file exists in the local + build directory, + &SCons; will rebuild the &hello; program + as normal: + + + + + scons -Q + + + + + If, however, there is no local &hello_c; file, + but one exists in /usr/repository1, + &SCons; will recompile the &hello; program + from the source file it finds in the repository: + + + + + + env = Environment() + env.Program('hello.c') + Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') + + + int main() { printf("Hello, world!\n"); } + + + + + scons -Q + + + + + And similarly, if there is no local &hello_c; file + and no /usr/repository1/hello.c, + but one exists in /usr/repository2: + + + + + + env = Environment() + env.Program('hello.c') + Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') + + + int main() { printf("Hello, world!\n"); } + + + + + scons -Q + + + + + + +
+ +
+ Finding <literal>#include</literal> files in repositories + + + + We've already seen that SCons will scan the contents of + a source file for #include file names + and realize that targets built from that source file + also depend on the #include file(s). + For each directory in the &cv-link-CPPPATH; list, + &SCons; will actually search the corresponding directories + in any repository trees and establish the + correct dependencies on any + #include files that it finds + in repository directory. + + + + + + Unless the C compiler also knows about these directories + in the repository trees, though, + it will be unable to find the #include files. + If, for example, the &hello_c; file in + our previous example includes the &hello.h; + in its current directory, + and the &hello.h; only exists in the repository: + + + + + % scons -Q + cc -o hello.o -c hello.c + hello.c:1: hello.h: No such file or directory + + + + + In order to inform the C compiler about the repositories, + &SCons; will add appropriate + -I flags to the compilation commands + for each directory in the &cv-CPPPATH; list. + So if we add the current directory to the + construction environment &cv-CPPPATH; like so: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('hello.c') + Repository('__ROOT__/usr/repository1') + + + int main() { printf("Hello, world!\n"); } + + + + + + Then re-executing &SCons; yields: + + + + + scons -Q + + + + + The order of the -I options replicates, + for the C preprocessor, + the same repository-directory search path + that &SCons; uses for its own dependency analysis. + If there are multiple repositories and multiple &cv-CPPPATH; + directories, &SCons; will add the repository directories + to the beginning of each &cv-CPPPATH; directory, + rapidly multiplying the number of -I flags. + If, for example, the &cv-CPPPATH; contains three directories + (and shorter repository path names!): + + + + + + env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3']) + env.Program('hello.c') + Repository('__ROOT__/r1', '__ROOT__/r2') + + + int main() { printf("Hello, world!\n"); } + + + + + + Then we'll end up with nine -I options + on the command line, + three (for each of the &cv-CPPPATH; directories) + times three (for the local directory plus the two repositories): + + + + + scons -Q + + + + +
+ Limitations on <literal>#include</literal> files in repositories + + + + &SCons; relies on the C compiler's + -I options to control the order in which + the preprocessor will search the repository directories + for #include files. + This causes a problem, however, with how the C preprocessor + handles #include lines with + the file name included in double-quotes. + + + + + + As we've seen, + &SCons; will compile the &hello_c; file from + the repository if it doesn't exist in + the local directory. + If, however, the &hello_c; file in the repository contains + a #include line with the file name in + double quotes: + + + + + #include "hello.h" + int + main(int argc, char *argv[]) + { + printf(HELLO_MESSAGE); + return (0); + } + + + + + Then the C preprocessor will always + use a &hello_h; file from the repository directory first, + even if there is a &hello_h; file in the local directory, + despite the fact that the command line specifies + -I as the first option: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('hello.c') + Repository('__ROOT__/usr/repository1') + + + int main() { printf("Hello, world!\n"); } + + + + + scons -Q + + + + + This behavior of the C preprocessor--always search + for a #include file in double-quotes + first in the same directory as the source file, + and only then search the -I--can + not, in general, be changed. + In other words, it's a limitation + that must be lived with if you want to use + code repositories in this way. + There are three ways you can possibly + work around this C preprocessor behavior: + + + + + + + + + Some modern versions of C compilers do have an option + to disable or control this behavior. + If so, add that option to &cv-link-CFLAGS; + (or &cv-link-CXXFLAGS; or both) in your construction environment(s). + Make sure the option is used for all construction + environments that use C preprocessing! + + + + + + + + Change all occurrences of #include "file.h" + to #include &lt;file.h&gt;. + Use of #include with angle brackets + does not have the same behavior--the -I + directories are searched first + for #include files--which + gives &SCons; direct control over the list of + directories the C preprocessor will search. + + + + + + + + Require that everyone working with compilation from + repositories check out and work on entire directories of files, + not individual files. + (If you use local wrapper scripts around + your source code control system's command, + you could add logic to enforce this restriction there. + + + + + + +
+ +
+ +
+ Finding the &SConstruct; file in repositories + + + + &SCons; will also search in repositories + for the &SConstruct; file and any specified &SConscript; files. + This poses a problem, though: how can &SCons; search a + repository tree for an &SConstruct; file + if the &SConstruct; file itself contains the information + about the pathname of the repository? + To solve this problem, &SCons; allows you + to specify repository directories + on the command line using the -Y option: + + + + + % scons -Q -Y /usr/repository1 -Y /usr/repository2 + + + + + When looking for source or derived files, + &SCons; will first search the repositories + specified on the command line, + and then search the repositories + specified in the &SConstruct; or &SConscript; files. + + + +
+ +
+ Finding derived files in repositories + + + + If a repository contains not only source files, + but also derived files (such as object files, + libraries, or executables), &SCons; will perform + its normal MD5 signature calculation to + decide if a derived file in a repository is up-to-date, + or the derived file must be rebuilt in the local build directory. + For the &SCons; signature calculation to work correctly, + a repository tree must contain the &sconsign; files + that &SCons; uses to keep track of signature information. + + + + + + Usually, this would be done by a build integrator + who would run &SCons; in the repository + to create all of its derived files and &sconsign; files, + or who would run &SCons; in a separate build directory + and copy the resulting tree to the desired repository: + + + + + + env = Environment() + env.Program(['hello.c', 'file1.c', 'file2.c']) + Repository('/usr/repository1', '/usr/repository2') + + + int main() { printf("Hello, world!\n"); } + + + int f1() { printf("file1\n"); } + + + int f2() { printf("file2.c\n"); } + + + + + cd /usr/repository1 + scons -Q + + + + + (Note that this is safe even if the &SConstruct; file + lists /usr/repository1 as a repository, + because &SCons; will remove the current build directory + from its repository list for that invocation.) + + + + + + Now, with the repository populated, + we only need to create the one local source file + we're interested in working with at the moment, + and use the -Y option to + tell &SCons; to fetch any other files it needs + from the repository: + + + + + + % cd $HOME/build + % edit hello.c + % scons -Q -Y /usr/repository1 + cc -c -o hello.o hello.c + cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o + + + + + Notice that &SCons; realizes that it does not need to + rebuild local copies file1.o and file2.o files, + but instead uses the already-compiled files + from the repository. + + + +
+ +
+ Guaranteeing local copies of files + + + + If the repository tree contains the complete results of a build, + and we try to build from the repository + without any files in our local tree, + something moderately surprising happens: + + + + + % mkdir $HOME/build2 + % cd $HOME/build2 + % scons -Q -Y /usr/all/repository hello + scons: `hello' is up-to-date. + + + + + Why does &SCons; say that the &hello; program + is up-to-date when there is no &hello; program + in the local build directory? + Because the repository (not the local directory) + contains the up-to-date &hello; program, + and &SCons; correctly determines that nothing + needs to be done to rebuild that + up-to-date copy of the file. + + + + + + There are, however, many times when you want to ensure that a + local copy of a file always exists. + A packaging or testing script, for example, + may assume that certain generated files exist locally. + To tell &SCons; to make a copy of any up-to-date repository + file in the local build directory, + use the &Local; function: + + + + + + env = Environment() + hello = env.Program('hello.c') + Local(hello) + + + int main() { printf("Hello, world!\n"); } + + + + + + If we then run the same command, + &SCons; will make a local copy of the program + from the repository copy, + and tell you that it is doing so: + + + + + % scons -Y /usr/all/repository hello + Local copy of hello from /usr/all/repository/hello + scons: `hello' is up-to-date. + + + + + (Notice that, because the act of making the local copy + is not considered a "build" of the &hello; file, + &SCons; still reports that it is up-to-date.) + + + +
diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml new file mode 100644 index 0000000..7d955c3 --- /dev/null +++ b/doc/user/repositories.xml @@ -0,0 +1,595 @@ + + + + + Often, a software project will have + one or more central repositories, + directory trees that contain + source code, or derived files, or both. + You can eliminate additional unnecessary + rebuilds of files by having &SCons; + use files from one or more code repositories + to build files in your local build tree. + + + +
+ The &Repository; Method + + + + + + It's often useful to allow multiple programmers working + on a project to build software from + source files and/or derived files that + are stored in a centrally-accessible repository, + a directory copy of the source code tree. + (Note that this is not the sort of repository + maintained by a source code management system + like BitKeeper, CVS, or Subversion.) + + You use the &Repository; method + to tell &SCons; to search one or more + central code repositories (in order) + for any source files and derived files + that are not present in the local build tree: + + + + + env = Environment() + env.Program('hello.c') + Repository('/usr/repository1', '/usr/repository2') + + + + + Multiple calls to the &Repository; method + will simply add repositories to the global list + that &SCons; maintains, + with the exception that &SCons; will automatically eliminate + the current directory and any non-existent + directories from the list. + + + +
+ +
+ Finding source files in repositories + + + + The above example + specifies that &SCons; + will first search for files under + the /usr/repository1 tree + and next under the /usr/repository2 tree. + &SCons; expects that any files it searches + for will be found in the same position + relative to the top-level directory. + In the above example, if the &hello_c; file is not + found in the local build tree, + &SCons; will search first for + a /usr/repository1/hello.c file + and then for a /usr/repository2/hello.c file + to use in its place. + + + + + + So given the &SConstruct; file above, + if the &hello_c; file exists in the local + build directory, + &SCons; will rebuild the &hello; program + as normal: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + If, however, there is no local &hello_c; file, + but one exists in /usr/repository1, + &SCons; will recompile the &hello; program + from the source file it finds in the repository: + + + + + + + % scons -Q + cc -o hello.o -c /usr/repository1/hello.c + cc -o hello hello.o + + + + + And similarly, if there is no local &hello_c; file + and no /usr/repository1/hello.c, + but one exists in /usr/repository2: + + + + + + + % scons -Q + cc -o hello.o -c /usr/repository2/hello.c + cc -o hello hello.o + + + + + + +
+ +
+ Finding <literal>#include</literal> files in repositories + + + + We've already seen that SCons will scan the contents of + a source file for #include file names + and realize that targets built from that source file + also depend on the #include file(s). + For each directory in the &cv-link-CPPPATH; list, + &SCons; will actually search the corresponding directories + in any repository trees and establish the + correct dependencies on any + #include files that it finds + in repository directory. + + + + + + Unless the C compiler also knows about these directories + in the repository trees, though, + it will be unable to find the #include files. + If, for example, the &hello_c; file in + our previous example includes the &hello;.h; + in its current directory, + and the &hello;.h; only exists in the repository: + + + + + % scons -Q + cc -o hello.o -c hello.c + hello.c:1: hello.h: No such file or directory + + + + + In order to inform the C compiler about the repositories, + &SCons; will add appropriate + -I flags to the compilation commands + for each directory in the &cv-CPPPATH; list. + So if we add the current directory to the + construction environment &cv-CPPPATH; like so: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('hello.c') + Repository('/usr/repository1') + + + + + Then re-executing &SCons; yields: + + + + + % scons -Q + cc -o hello.o -c -I. -I/usr/repository1 hello.c + cc -o hello hello.o + + + + + The order of the -I options replicates, + for the C preprocessor, + the same repository-directory search path + that &SCons; uses for its own dependency analysis. + If there are multiple repositories and multiple &cv-CPPPATH; + directories, &SCons; will add the repository directories + to the beginning of each &cv-CPPPATH; directory, + rapidly multiplying the number of -I flags. + If, for example, the &cv-CPPPATH; contains three directories + (and shorter repository path names!): + + + + + env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3']) + env.Program('hello.c') + Repository('/r1', '/r2') + + + + + Then we'll end up with nine -I options + on the command line, + three (for each of the &cv-CPPPATH; directories) + times three (for the local directory plus the two repositories): + + + + + % scons -Q + cc -o hello.o -c -Idir1 -I/r1/dir1 -I/r2/dir1 -Idir2 -I/r1/dir2 -I/r2/dir2 -Idir3 -I/r1/dir3 -I/r2/dir3 hello.c + cc -o hello hello.o + + + + +
+ Limitations on <literal>#include</literal> files in repositories + + + + &SCons; relies on the C compiler's + -I options to control the order in which + the preprocessor will search the repository directories + for #include files. + This causes a problem, however, with how the C preprocessor + handles #include lines with + the file name included in double-quotes. + + + + + + As we've seen, + &SCons; will compile the &hello_c; file from + the repository if it doesn't exist in + the local directory. + If, however, the &hello_c; file in the repository contains + a #include line with the file name in + double quotes: + + + + + #include "hello.h" + int + main(int argc, char *argv[]) + { + printf(HELLO_MESSAGE); + return (0); + } + + + + + Then the C preprocessor will always + use a &hello_h; file from the repository directory first, + even if there is a &hello_h; file in the local directory, + despite the fact that the command line specifies + -I as the first option: + + + + + + + % scons -Q + cc -o hello.o -c -I. -I/usr/repository1 /usr/repository1/hello.c + cc -o hello hello.o + + + + + This behavior of the C preprocessor--always search + for a #include file in double-quotes + first in the same directory as the source file, + and only then search the -I--can + not, in general, be changed. + In other words, it's a limitation + that must be lived with if you want to use + code repositories in this way. + There are three ways you can possibly + work around this C preprocessor behavior: + + + + + + + + + Some modern versions of C compilers do have an option + to disable or control this behavior. + If so, add that option to &cv-link-CFLAGS; + (or &cv-link-CXXFLAGS; or both) in your construction environment(s). + Make sure the option is used for all construction + environments that use C preprocessing! + + + + + + + + Change all occurrences of #include "file.h" + to #include <file.h>. + Use of #include with angle brackets + does not have the same behavior--the -I + directories are searched first + for #include files--which + gives &SCons; direct control over the list of + directories the C preprocessor will search. + + + + + + + + Require that everyone working with compilation from + repositories check out and work on entire directories of files, + not individual files. + (If you use local wrapper scripts around + your source code control system's command, + you could add logic to enforce this restriction there. + + + + + + +
+ +
+ +
+ Finding the &SConstruct; file in repositories + + + + &SCons; will also search in repositories + for the &SConstruct; file and any specified &SConscript; files. + This poses a problem, though: how can &SCons; search a + repository tree for an &SConstruct; file + if the &SConstruct; file itself contains the information + about the pathname of the repository? + To solve this problem, &SCons; allows you + to specify repository directories + on the command line using the -Y option: + + + + + % scons -Q -Y /usr/repository1 -Y /usr/repository2 + + + + + When looking for source or derived files, + &SCons; will first search the repositories + specified on the command line, + and then search the repositories + specified in the &SConstruct; or &SConscript; files. + + + +
+ +
+ Finding derived files in repositories + + + + If a repository contains not only source files, + but also derived files (such as object files, + libraries, or executables), &SCons; will perform + its normal MD5 signature calculation to + decide if a derived file in a repository is up-to-date, + or the derived file must be rebuilt in the local build directory. + For the &SCons; signature calculation to work correctly, + a repository tree must contain the &sconsign; files + that &SCons; uses to keep track of signature information. + + + + + + Usually, this would be done by a build integrator + who would run &SCons; in the repository + to create all of its derived files and &sconsign; files, + or who would run &SCons; in a separate build directory + and copy the resulting tree to the desired repository: + + + + + + + % cd /usr/repository1 + % scons -Q + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o hello.o -c hello.c + cc -o hello hello.o file1.o file2.o + + + + + (Note that this is safe even if the &SConstruct; file + lists /usr/repository1 as a repository, + because &SCons; will remove the current build directory + from its repository list for that invocation.) + + + + + + Now, with the repository populated, + we only need to create the one local source file + we're interested in working with at the moment, + and use the -Y option to + tell &SCons; to fetch any other files it needs + from the repository: + + + + + + % cd $HOME/build + % edit hello.c + % scons -Q -Y /usr/repository1 + cc -c -o hello.o hello.c + cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o + + + + + Notice that &SCons; realizes that it does not need to + rebuild local copies file1.o and file2.o files, + but instead uses the already-compiled files + from the repository. + + + +
+ +
+ Guaranteeing local copies of files + + + + If the repository tree contains the complete results of a build, + and we try to build from the repository + without any files in our local tree, + something moderately surprising happens: + + + + + % mkdir $HOME/build2 + % cd $HOME/build2 + % scons -Q -Y /usr/all/repository hello + scons: `hello' is up-to-date. + + + + + Why does &SCons; say that the &hello; program + is up-to-date when there is no &hello; program + in the local build directory? + Because the repository (not the local directory) + contains the up-to-date &hello; program, + and &SCons; correctly determines that nothing + needs to be done to rebuild that + up-to-date copy of the file. + + + + + + There are, however, many times when you want to ensure that a + local copy of a file always exists. + A packaging or testing script, for example, + may assume that certain generated files exist locally. + To tell &SCons; to make a copy of any up-to-date repository + file in the local build directory, + use the &Local; function: + + + + + env = Environment() + hello = env.Program('hello.c') + Local(hello) + + + + + If we then run the same command, + &SCons; will make a local copy of the program + from the repository copy, + and tell you that it is doing so: + + + + + % scons -Y /usr/all/repository hello + Local copy of hello from /usr/all/repository/hello + scons: `hello' is up-to-date. + + + + + (Notice that, because the act of making the local copy + is not considered a "build" of the &hello; file, + &SCons; still reports that it is up-to-date.) + + + +
diff --git a/doc/user/run.in b/doc/user/run.in new file mode 100644 index 0000000..946a7ed --- /dev/null +++ b/doc/user/run.in @@ -0,0 +1,375 @@ + + + + + + + XXX + + + +
+ Command-Line Options + + + + XXX + + + +
+ +
+ Getting at Command-Line Arguments + + + + XXX + + + +
+ +
+ Selective Builds + + + + XXX + + + +
+ + + +
+ Overriding Construction Variables + + + + XXX + + + +
diff --git a/doc/user/run.xml b/doc/user/run.xml new file mode 100644 index 0000000..946a7ed --- /dev/null +++ b/doc/user/run.xml @@ -0,0 +1,375 @@ + + + + + + + XXX + + + +
+ Command-Line Options + + + + XXX + + + +
+ +
+ Getting at Command-Line Arguments + + + + XXX + + + +
+ +
+ Selective Builds + + + + XXX + + + +
+ + + +
+ Overriding Construction Variables + + + + XXX + + + +
diff --git a/doc/user/scanners.in b/doc/user/scanners.in new file mode 100644 index 0000000..8fa36d7 --- /dev/null +++ b/doc/user/scanners.in @@ -0,0 +1,331 @@ + + + + + + + &SCons; has built-in scanners that know how to look in + C, Fortran and IDL source files for information about + other files that targets built from those files depend on--for example, + in the case of files that use the C preprocessor, + the .h files that are specified + using #include lines in the source. + You can use the same mechanisms that &SCons; uses to create + its built-in scanners to write scanners of your own for file types + that &SCons; does not know how to scan "out of the box." + + + +
+ A Simple Scanner Example + + + + Suppose, for example, that we want to create a simple scanner + for .foo files. + A .foo file contains some text that + will be processed, + and can include other files on lines that begin + with include + followed by a file name: + + + + + include filename.foo + + + + + Scanning a file will be handled by a Python function + that you must supply. + Here is a function that will use the Python + re module + to scan for the include lines in our example: + + + + + import re + + include_re = re.compile(r'^include\s+(\S+)$', re.M) + + def kfile_scan(node, env, path, arg): + contents = node.get_text_contents() + return include_re.findall(contents) + + + + + The scanner function must + accept the four specified arguments + and return a list of implicit dependencies. + Presumably, these would be dependencies found + from examining the contents of the file, + although the function can perform any + manipulation at all to generate the list of + dependencies. + + + + + + + node + + + + + An &SCons; node object representing the file being scanned. + The path name to the file can be + used by converting the node to a string + using the str() function, + or an internal &SCons; get_text_contents() + object method can be used to fetch the contents. + + + + + + + env + + + + + The construction environment in effect for this scan. + The scanner function may choose to use construction + variables from this environment to affect its behavior. + + + + + + + path + + + + + A list of directories that form the search path for included files + for this scanner. + This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH; + variables. + + + + + + + arg + + + + + An optional argument that you can choose to + have passed to this scanner function by + various scanner instances. + + + + + + + + + + A Scanner object is created using the &Scanner; function, + which typically takes an skeys argument + to associate the type of file suffix with this scanner. + The Scanner object must then be associated with the + &cv-link-SCANNERS; construction variable of a construction environment, + typically by using the &Append; method: + + + + + kscan = Scanner(function = kfile_scan, + skeys = ['.k']) + env.Append(SCANNERS = kscan) + + + + + When we put it all together, it looks like: + + + + + + import re + + include_re = re.compile(r'^include\s+(\S+)$', re.M) + + def kfile_scan(node, env, path): + contents = node.get_text_contents() + includes = include_re.findall(contents) + return includes + + kscan = Scanner(function = kfile_scan, + skeys = ['.k']) + + env = Environment(ENV = {'PATH' : '__ROOT__/usr/local/bin'}) + env.Append(SCANNERS = kscan) + + env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') + + + include other_file + + + other_file + + + + + + cat + + + + + +
diff --git a/doc/user/scanners.xml b/doc/user/scanners.xml new file mode 100644 index 0000000..d34b222 --- /dev/null +++ b/doc/user/scanners.xml @@ -0,0 +1,317 @@ + + + + + + + &SCons; has built-in scanners that know how to look in + C, Fortran and IDL source files for information about + other files that targets built from those files depend on--for example, + in the case of files that use the C preprocessor, + the .h files that are specified + using #include lines in the source. + You can use the same mechanisms that &SCons; uses to create + its built-in scanners to write scanners of your own for file types + that &SCons; does not know how to scan "out of the box." + + + +
+ A Simple Scanner Example + + + + Suppose, for example, that we want to create a simple scanner + for .foo files. + A .foo file contains some text that + will be processed, + and can include other files on lines that begin + with include + followed by a file name: + + + + + include filename.foo + + + + + Scanning a file will be handled by a Python function + that you must supply. + Here is a function that will use the Python + re module + to scan for the include lines in our example: + + + + + import re + + include_re = re.compile(r'^include\s+(\S+)$', re.M) + + def kfile_scan(node, env, path, arg): + contents = node.get_text_contents() + return include_re.findall(contents) + + + + + The scanner function must + accept the four specified arguments + and return a list of implicit dependencies. + Presumably, these would be dependencies found + from examining the contents of the file, + although the function can perform any + manipulation at all to generate the list of + dependencies. + + + + + + + node + + + + + An &SCons; node object representing the file being scanned. + The path name to the file can be + used by converting the node to a string + using the str() function, + or an internal &SCons; get_text_contents() + object method can be used to fetch the contents. + + + + + + + env + + + + + The construction environment in effect for this scan. + The scanner function may choose to use construction + variables from this environment to affect its behavior. + + + + + + + path + + + + + A list of directories that form the search path for included files + for this scanner. + This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH; + variables. + + + + + + + arg + + + + + An optional argument that you can choose to + have passed to this scanner function by + various scanner instances. + + + + + + + + + + A Scanner object is created using the &Scanner; function, + which typically takes an skeys argument + to associate the type of file suffix with this scanner. + The Scanner object must then be associated with the + &cv-link-SCANNERS; construction variable of a construction environment, + typically by using the &Append; method: + + + + + kscan = Scanner(function = kfile_scan, + skeys = ['.k']) + env.Append(SCANNERS = kscan) + + + + + When we put it all together, it looks like: + + + + + import re + + include_re = re.compile(r'^include\s+(\S+)$', re.M) + + def kfile_scan(node, env, path): + contents = node.get_text_contents() + includes = include_re.findall(contents) + return includes + + kscan = Scanner(function = kfile_scan, + skeys = ['.k']) + + env = Environment(ENV = {'PATH' : '/usr/local/bin'}) + env.Append(SCANNERS = kscan) + + env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') + + + + +
diff --git a/doc/user/sconf.in b/doc/user/sconf.in new file mode 100644 index 0000000..c4092c9 --- /dev/null +++ b/doc/user/sconf.in @@ -0,0 +1,486 @@ + + + + + &SCons; has integrated support for multi-platform build configuration + similar to that offered by GNU &Autoconf;, + such as + figuring out what libraries or header files + are available on the local system. + This section describes how to use + this &SCons feature. + + + + + + This chapter is still under development, + so not everything is explained as well as it should be. + See the &SCons; man page for additional information. + + + +
+ &Configure_Contexts; + + + + The basic framework for multi-platform build configuration + in &SCons; is to attach a &configure_context; to a + construction environment by calling the &Configure; function, + perform a number of checks for + libraries, functions, header files, etc., + and to then call the configure context's &Finish; method + to finish off the configuration: + + + + + env = Environment() + conf = Configure(env) + # Checks for libraries, header files, etc. go here! + env = conf.Finish() + + + + + &SCons; provides a number of basic checks, + as well as a mechanism for adding your own custom checks. + + + + + + Note that &SCons; uses its own dependency + mechanism to determine when a check + needs to be run--that is, + &SCons; does not run the checks + every time it is invoked, + but caches the values returned by previous checks + and uses the cached values unless something has changed. + This saves a tremendous amount + of developer time while working on + cross-platform build issues. + + + + + + The next sections describe + the basic checks that &SCons; supports, + as well as how to add your own custom checks. + + + +
+ +
+ Checking for the Existence of Header Files + + + + Testing the existence of a header file + requires knowing what language the header file is. + A configure context has a &CheckCHeader; method + that checks for the existence of a C header file: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckCHeader('math.h'): + print 'Math.h must be installed!' + Exit(1) + if conf.CheckCHeader('foo.h'): + conf.env.Append('-DHAS_FOO_H') + env = conf.Finish() + + + + + Note that you can choose to terminate + the build if a given header file doesn't exist, + or you can modify the construction environment + based on the existence of a header file. + + + + + + If you need to check for the existence + a C++ header file, + use the &CheckCXXHeader; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckCXXHeader('vector.h'): + print 'vector.h must be installed!' + Exit(1) + env = conf.Finish() + + +
+ +
+ Checking for the Availability of a Function + + + + Check for the availability of a specific function + using the &CheckFunc; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckFunc('strcpy'): + print 'Did not find strcpy(), using local version' + conf.env.Append(CPPDEFINES = '-Dstrcpy=my_local_strcpy') + env = conf.Finish() + + +
+ +
+ Checking for the Availability of a Library + + + + Check for the availability of a library + using the &CheckLib; method. + You only specify the basename of the library, + you don't need to add a lib + prefix or a .a or .lib suffix: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckLib('m'): + print 'Did not find libm.a or m.lib, exiting!' + Exit(1) + env = conf.Finish() + + + + + Because the ability to use a library successfully + often depends on having access to a header file + that describes the library's interface, + you can check for a library + and a header file + at the same time by using the + &CheckLibWithHeader; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckLibWithHeader('m', 'math.h', 'c'): + print 'Did not find libm.a or m.lib, exiting!' + Exit(1) + env = conf.Finish() + + + + + This is essentially shorthand for + separate calls to the &CheckHeader; and &CheckLib; + functions. + + + +
+ +
+ Checking for the Availability of a &typedef; + + + + Check for the availability of a &typedef; + by using the &CheckType; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckType('off_t'): + print 'Did not find off_t typedef, assuming int' + conf.env.Append(CCFLAGS = '-Doff_t=int') + env = conf.Finish() + + + + + You can also add a string that will be + placed at the beginning of the test file + that will be used to check for the &typedef;. + This provide a way to specify + files that must be included to find the &typedef;: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckType('off_t', '#include &lt;sys/types.h&gt;\n'): + print 'Did not find off_t typedef, assuming int' + conf.env.Append(CCFLAGS = '-Doff_t=int') + env = conf.Finish() + + +
+ +
+ Adding Your Own Custom Checks + + + + A custom check is a Python function + that checks for a certain condition to exist + on the running system, + usually using methods that &SCons; + supplies to take care of the details + of checking whether a compilation succeeds, + a link succeeds, + a program is runnable, + etc. + A simple custom check for the existence of + a specific library might look as follows: + + + + + mylib_test_source_file = """ + #include &lt;mylib.h&gt; + int main(int argc, char **argv) + { + MyLibrary mylib(argc, argv); + return 0; + } + """ + + def CheckMyLibrary(context): + context.Message('Checking for MyLibrary...') + result = context.TryLink(mylib_test_source_file, '.c') + context.Result(result) + return result + + + + + The &Message; and &Result; methods + should typically begin and end a custom check to + let the user know what's going on: + the &Message; call prints the + specified message (with no trailing newline) + and the &Result; call prints + yes if the check succeeds and + no if it doesn't. + The &TryLink; method + actually tests for whether the + specified program text + will successfully link. + + + + + + (Note that a custom check can modify + its check based on any arguments you + choose to pass it, + or by using or modifying the configure context environment + in the context.env attribute.) + + + + + + This custom check function is + then attached to the &configure_context; + by passing a dictionary + to the &Configure; call + that maps a name of the check + to the underlying function: + + + + + env = Environment() + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + + + + + You'll typically want to make + the check and the function name the same, + as we've done here, + to avoid potential confusion. + + + + + + We can then put these pieces together + and actually call the CheckMyLibrary check + as follows: + + + + + mylib_test_source_file = """ + #include &lt;mylib.h&gt; + int main(int argc, char **argv) + { + MyLibrary mylib(argc, argv); + return 0; + } + """ + + def CheckMyLibrary(context): + context.Message('Checking for MyLibrary... ') + result = context.TryLink(mylib_test_source_file, '.c') + context.Result(result) + return result + + env = Environment() + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + if not conf.CheckMyLibrary(): + print 'MyLibrary is not installed!' + Exit(1) + env = conf.Finish() + + # We would then add actual calls like Program() to build + # something using the "env" construction environment. + + + + + If MyLibrary is not installed on the system, + the output will look like: + + + + + % scons + scons: Reading SConscript file ... + Checking for MyLibrary... failed + MyLibrary is not installed! + + + + + If MyLibrary is installed, + the output will look like: + + + + + % scons + scons: Reading SConscript file ... + Checking for MyLibrary... failed + scons: done reading SConscript + scons: Building targets ... + . + . + . + + +
+ +
+ Not Configuring When Cleaning Targets + + + + Using multi-platform configuration + as described in the previous sections + will run the configuration commands + even when invoking + scons -c + to clean targets: + + + + + % scons -Q -c + Checking for MyLibrary... yes + Removed foo.o + Removed foo + + + + + Although running the platform checks + when removing targets doesn't hurt anything, + it's usually unnecessary. + You can avoid this by using the + &GetOption(); method to + check whether the (clean) + option has been invoked on the command line: + + + + + env = Environment() + if not env.GetOption('clean'): + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + if not conf.CheckMyLibrary(): + print 'MyLibrary is not installed!' + Exit(1) + env = conf.Finish() + + + + % scons -Q -c + Removed foo.o + Removed foo + + +
+ + diff --git a/doc/user/sconf.xml b/doc/user/sconf.xml new file mode 100644 index 0000000..16ba534 --- /dev/null +++ b/doc/user/sconf.xml @@ -0,0 +1,486 @@ + + + + + &SCons; has integrated support for multi-platform build configuration + similar to that offered by GNU &Autoconf;, + such as + figuring out what libraries or header files + are available on the local system. + This section describes how to use + this &SCons; feature. + + + + + + This chapter is still under development, + so not everything is explained as well as it should be. + See the &SCons; man page for additional information. + + + +
+ &Configure_Contexts; + + + + The basic framework for multi-platform build configuration + in &SCons; is to attach a &configure_context; to a + construction environment by calling the &Configure; function, + perform a number of checks for + libraries, functions, header files, etc., + and to then call the configure context's &Finish; method + to finish off the configuration: + + + + + env = Environment() + conf = Configure(env) + # Checks for libraries, header files, etc. go here! + env = conf.Finish() + + + + + &SCons; provides a number of basic checks, + as well as a mechanism for adding your own custom checks. + + + + + + Note that &SCons; uses its own dependency + mechanism to determine when a check + needs to be run--that is, + &SCons; does not run the checks + every time it is invoked, + but caches the values returned by previous checks + and uses the cached values unless something has changed. + This saves a tremendous amount + of developer time while working on + cross-platform build issues. + + + + + + The next sections describe + the basic checks that &SCons; supports, + as well as how to add your own custom checks. + + + +
+ +
+ Checking for the Existence of Header Files + + + + Testing the existence of a header file + requires knowing what language the header file is. + A configure context has a &CheckCHeader; method + that checks for the existence of a C header file: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckCHeader('math.h'): + print 'Math.h must be installed!' + Exit(1) + if conf.CheckCHeader('foo.h'): + conf.env.Append('-DHAS_FOO_H') + env = conf.Finish() + + + + + Note that you can choose to terminate + the build if a given header file doesn't exist, + or you can modify the construction environment + based on the existence of a header file. + + + + + + If you need to check for the existence + a C++ header file, + use the &CheckCXXHeader; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckCXXHeader('vector.h'): + print 'vector.h must be installed!' + Exit(1) + env = conf.Finish() + + +
+ +
+ Checking for the Availability of a Function + + + + Check for the availability of a specific function + using the &CheckFunc; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckFunc('strcpy'): + print 'Did not find strcpy(), using local version' + conf.env.Append(CPPDEFINES = '-Dstrcpy=my_local_strcpy') + env = conf.Finish() + + +
+ +
+ Checking for the Availability of a Library + + + + Check for the availability of a library + using the &CheckLib; method. + You only specify the basename of the library, + you don't need to add a lib + prefix or a .a or .lib suffix: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckLib('m'): + print 'Did not find libm.a or m.lib, exiting!' + Exit(1) + env = conf.Finish() + + + + + Because the ability to use a library successfully + often depends on having access to a header file + that describes the library's interface, + you can check for a library + and a header file + at the same time by using the + &CheckLibWithHeader; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckLibWithHeader('m', 'math.h', 'c'): + print 'Did not find libm.a or m.lib, exiting!' + Exit(1) + env = conf.Finish() + + + + + This is essentially shorthand for + separate calls to the &CheckHeader; and &CheckLib; + functions. + + + +
+ +
+ Checking for the Availability of a &typedef; + + + + Check for the availability of a &typedef; + by using the &CheckType; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckType('off_t'): + print 'Did not find off_t typedef, assuming int' + conf.env.Append(CCFLAGS = '-Doff_t=int') + env = conf.Finish() + + + + + You can also add a string that will be + placed at the beginning of the test file + that will be used to check for the &typedef;. + This provide a way to specify + files that must be included to find the &typedef;: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckType('off_t', '#include <sys/types.h>\n'): + print 'Did not find off_t typedef, assuming int' + conf.env.Append(CCFLAGS = '-Doff_t=int') + env = conf.Finish() + + +
+ +
+ Adding Your Own Custom Checks + + + + A custom check is a Python function + that checks for a certain condition to exist + on the running system, + usually using methods that &SCons; + supplies to take care of the details + of checking whether a compilation succeeds, + a link succeeds, + a program is runnable, + etc. + A simple custom check for the existence of + a specific library might look as follows: + + + + + mylib_test_source_file = """ + #include <mylib.h> + int main(int argc, char **argv) + { + MyLibrary mylib(argc, argv); + return 0; + } + """ + + def CheckMyLibrary(context): + context.Message('Checking for MyLibrary...') + result = context.TryLink(mylib_test_source_file, '.c') + context.Result(result) + return result + + + + + The &Message; and &Result; methods + should typically begin and end a custom check to + let the user know what's going on: + the &Message; call prints the + specified message (with no trailing newline) + and the &Result; call prints + yes if the check succeeds and + no if it doesn't. + The &TryLink; method + actually tests for whether the + specified program text + will successfully link. + + + + + + (Note that a custom check can modify + its check based on any arguments you + choose to pass it, + or by using or modifying the configure context environment + in the context.env attribute.) + + + + + + This custom check function is + then attached to the &configure_context; + by passing a dictionary + to the &Configure; call + that maps a name of the check + to the underlying function: + + + + + env = Environment() + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + + + + + You'll typically want to make + the check and the function name the same, + as we've done here, + to avoid potential confusion. + + + + + + We can then put these pieces together + and actually call the CheckMyLibrary check + as follows: + + + + + mylib_test_source_file = """ + #include <mylib.h> + int main(int argc, char **argv) + { + MyLibrary mylib(argc, argv); + return 0; + } + """ + + def CheckMyLibrary(context): + context.Message('Checking for MyLibrary... ') + result = context.TryLink(mylib_test_source_file, '.c') + context.Result(result) + return result + + env = Environment() + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + if not conf.CheckMyLibrary(): + print 'MyLibrary is not installed!' + Exit(1) + env = conf.Finish() + + # We would then add actual calls like Program() to build + # something using the "env" construction environment. + + + + + If MyLibrary is not installed on the system, + the output will look like: + + + + + % scons + scons: Reading SConscript file ... + Checking for MyLibrary... failed + MyLibrary is not installed! + + + + + If MyLibrary is installed, + the output will look like: + + + + + % scons + scons: Reading SConscript file ... + Checking for MyLibrary... failed + scons: done reading SConscript + scons: Building targets ... + . + . + . + + +
+ +
+ Not Configuring When Cleaning Targets + + + + Using multi-platform configuration + as described in the previous sections + will run the configuration commands + even when invoking + scons -c + to clean targets: + + + + + % scons -Q -c + Checking for MyLibrary... yes + Removed foo.o + Removed foo + + + + + Although running the platform checks + when removing targets doesn't hurt anything, + it's usually unnecessary. + You can avoid this by using the + &GetOption;(); method to + check whether the (clean) + option has been invoked on the command line: + + + + + env = Environment() + if not env.GetOption('clean'): + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + if not conf.CheckMyLibrary(): + print 'MyLibrary is not installed!' + Exit(1) + env = conf.Finish() + + + + % scons -Q -c + Removed foo.o + Removed foo + + +
+ + diff --git a/doc/user/separate.in b/doc/user/separate.in new file mode 100644 index 0000000..edc6da8 --- /dev/null +++ b/doc/user/separate.in @@ -0,0 +1,540 @@ + + + + + + + It's often useful to keep any built files completely + separate from the source files. + In &SCons;, this is usually done by creating one or more separate + variant directory trees + that are used to hold the built objects files, libraries, + and executable programs, etc. + for a specific flavor, or variant, of build. + &SCons; provides two ways to do this, + one through the &SConscript; function that we've already seen, + and the second through a more flexible &VariantDir; function. + + + + + + One historical note: the &VariantDir; function + used to be called &BuildDir;. + That name is still supported + but has been deprecated + because the &SCons; functionality + differs from the model of a "build directory" + implemented by other build systems like the GNU Autotools. + + + +
+ Specifying a Variant Directory Tree as Part of an &SConscript; Call + + + + The most straightforward way to establish a variant directory tree + uses the fact that the usual way to + set up a build hierarchy is to have an + &SConscript; file in the source subdirectory. + If you then pass a &variant_dir; argument to the + &SConscript; function call: + + + + + + SConscript('src/SConscript', variant_dir='build') + + + env = Environment() + env.Program('hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + &SCons; will then build all of the files in + the &build; subdirectory: + + + + + ls src + scons -Q + ls build + + + + + But wait a minute--what's going on here? + &SCons; created the object file + build/hello.o + in the &build; subdirectory, + as expected. + But even though our &hello_c; file lives in the &src; subdirectory, + &SCons; has actually compiled a + build/hello.c file + to create the object file. + + + + + + What's happened is that &SCons; has duplicated + the &hello_c; file from the &src; subdirectory + to the &build; subdirectory, + and built the program from there. + The next section explains why &SCons; does this. + + + +
+ +
+ Why &SCons; Duplicates Source Files in a Variant Directory Tree + + + + &SCons; duplicates source files in variant directory trees + because it's the most straightforward way to guarantee a correct build + regardless of include-file directory paths, + relative references between files, + or tool support for putting files in different locations, + and the &SCons; philosophy is to, by default, + guarantee a correct build in all cases. + + + + + + The most direct reason to duplicate source files + in variant directories + is simply that some tools (mostly older versions) + are written to only build their output files + in the same directory as the source files. + In this case, the choices are either + to build the output file in the source directory + and move it to the variant directory, + or to duplicate the source files in the variant directory. + + + + + + Additionally, + relative references between files + can cause problems if we don't + just duplicate the hierarchy of source files + in the variant directory. + You can see this at work in + use of the C preprocessor #include + mechanism with double quotes, not angle brackets: + + + + + #include "file.h" + + + + + The de facto standard behavior + for most C compilers in this case + is to first look in the same directory + as the source file that contains the #include line, + then to look in the directories in the preprocessor search path. + Add to this that the &SCons; implementation of + support for code repositories + (described below) + means not all of the files + will be found in the same directory hierarchy, + and the simplest way to make sure + that the right include file is found + is to duplicate the source files into the variant directory, + which provides a correct build + regardless of the original location(s) of the source files. + + + + + + Although source-file duplication guarantees a correct build + even in these end-cases, + it can usually be safely disabled. + The next section describes + how you can disable the duplication of source files + in the variant directory. + + + +
+ +
+ Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree + + + + In most cases and with most tool sets, + &SCons; can place its target files in a build subdirectory + without + duplicating the source files + and everything will work just fine. + You can disable the default &SCons; behavior + by specifying duplicate=0 + when you call the &SConscript; function: + + + + + SConscript('src/SConscript', variant_dir='build', duplicate=0) + + + + + When this flag is specified, + &SCons; uses the variant directory + like most people expect--that is, + the output files are placed in the variant directory + while the source files stay in the source directory: + + + + + % ls src + SConscript + hello.c + % scons -Q + cc -c src/hello.c -o build/hello.o + cc -o build/hello build/hello.o + % ls build + hello + hello.o + + +
+ +
+ The &VariantDir; Function + + + + Use the &VariantDir; function to establish that target + files should be built in a separate directory + from the source files: + + + + + + VariantDir('build', 'src') + env = Environment() + env.Program('build/hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + Note that when you're not using + an &SConscript; file in the &src; subdirectory, + you must actually specify that + the program must be built from + the build/hello.c + file that &SCons; will duplicate in the + &build; subdirectory. + + + + + + When using the &VariantDir; function directly, + &SCons; still duplicates the source files + in the variant directory by default: + + + + + ls src + scons -Q + ls build + + + + + You can specify the same duplicate=0 argument + that you can specify for an &SConscript; call: + + + + + + VariantDir('build', 'src', duplicate=0) + env = Environment() + env.Program('build/hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + In which case &SCons; + will disable duplication of the source files: + + + + + ls src + scons -Q + ls build + + +
+ +
+ Using &VariantDir; With an &SConscript; File + + + + Even when using the &VariantDir; function, + it's much more natural to use it with + a subsidiary &SConscript; file. + For example, if the + src/SConscript + looks like this: + + + + + + VariantDir('build', 'src') + SConscript('build/SConscript') + + + env = Environment() + env.Program('hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + Then our &SConstruct; file could look like: + + + + + + + + + Yielding the following output: + + + + + ls src + scons -Q + ls build + + + + + Notice that this is completely equivalent + to the use of &SConscript; that we + learned about in the previous section. + + + +
+ +
+ Using &Glob; with &VariantDir; + + + + The &Glob; file name pattern matching function + works just as usual when using &VariantDir;. + For example, if the + src/SConscript + looks like this: + + + + + + VariantDir('build', 'src') + SConscript('build/SConscript') + + + env = Environment() + env.Program('hello', Glob('*.c')) + + + #include "f2.h" + int main() { printf(f2()); } + + + const char * f2() { return("Hello, world!\n"); } + + + const char * f2(); + + + + + + Then with the same &SConstruct; file as in the previous section, + and source files f1.c + and f2.c in src, + we would see the following output: + + + + + ls src + scons -Q + ls build + + + + + The &Glob; function returns Nodes in the + build/ tree, as you'd expect. + + + +
+ + diff --git a/doc/user/separate.xml b/doc/user/separate.xml new file mode 100644 index 0000000..fd5bdf0 --- /dev/null +++ b/doc/user/separate.xml @@ -0,0 +1,520 @@ + + + + + + + It's often useful to keep any built files completely + separate from the source files. + In &SCons;, this is usually done by creating one or more separate + variant directory trees + that are used to hold the built objects files, libraries, + and executable programs, etc. + for a specific flavor, or variant, of build. + &SCons; provides two ways to do this, + one through the &SConscript; function that we've already seen, + and the second through a more flexible &VariantDir; function. + + + + + + One historical note: the &VariantDir; function + used to be called &BuildDir;. + That name is still supported + but has been deprecated + because the &SCons; functionality + differs from the model of a "build directory" + implemented by other build systems like the GNU Autotools. + + + +
+ Specifying a Variant Directory Tree as Part of an &SConscript; Call + + + + The most straightforward way to establish a variant directory tree + uses the fact that the usual way to + set up a build hierarchy is to have an + &SConscript; file in the source subdirectory. + If you then pass a &variant_dir; argument to the + &SConscript; function call: + + + + + SConscript('src/SConscript', variant_dir='build') + + + + + &SCons; will then build all of the files in + the &build; subdirectory: + + + + + % ls src + SConscript hello.c + % scons -Q + cc -o build/hello.o -c build/hello.c + cc -o build/hello build/hello.o + % ls build + SConscript hello hello.c hello.o + + + + + But wait a minute--what's going on here? + &SCons; created the object file + build/hello.o + in the &build; subdirectory, + as expected. + But even though our &hello_c; file lives in the &src; subdirectory, + &SCons; has actually compiled a + build/hello.c file + to create the object file. + + + + + + What's happened is that &SCons; has duplicated + the &hello_c; file from the &src; subdirectory + to the &build; subdirectory, + and built the program from there. + The next section explains why &SCons; does this. + + + +
+ +
+ Why &SCons; Duplicates Source Files in a Variant Directory Tree + + + + &SCons; duplicates source files in variant directory trees + because it's the most straightforward way to guarantee a correct build + regardless of include-file directory paths, + relative references between files, + or tool support for putting files in different locations, + and the &SCons; philosophy is to, by default, + guarantee a correct build in all cases. + + + + + + The most direct reason to duplicate source files + in variant directories + is simply that some tools (mostly older versions) + are written to only build their output files + in the same directory as the source files. + In this case, the choices are either + to build the output file in the source directory + and move it to the variant directory, + or to duplicate the source files in the variant directory. + + + + + + Additionally, + relative references between files + can cause problems if we don't + just duplicate the hierarchy of source files + in the variant directory. + You can see this at work in + use of the C preprocessor #include + mechanism with double quotes, not angle brackets: + + + + + #include "file.h" + + + + + The de facto standard behavior + for most C compilers in this case + is to first look in the same directory + as the source file that contains the #include line, + then to look in the directories in the preprocessor search path. + Add to this that the &SCons; implementation of + support for code repositories + (described below) + means not all of the files + will be found in the same directory hierarchy, + and the simplest way to make sure + that the right include file is found + is to duplicate the source files into the variant directory, + which provides a correct build + regardless of the original location(s) of the source files. + + + + + + Although source-file duplication guarantees a correct build + even in these end-cases, + it can usually be safely disabled. + The next section describes + how you can disable the duplication of source files + in the variant directory. + + + +
+ +
+ Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree + + + + In most cases and with most tool sets, + &SCons; can place its target files in a build subdirectory + without + duplicating the source files + and everything will work just fine. + You can disable the default &SCons; behavior + by specifying duplicate=0 + when you call the &SConscript; function: + + + + + SConscript('src/SConscript', variant_dir='build', duplicate=0) + + + + + When this flag is specified, + &SCons; uses the variant directory + like most people expect--that is, + the output files are placed in the variant directory + while the source files stay in the source directory: + + + + + % ls src + SConscript + hello.c + % scons -Q + cc -c src/hello.c -o build/hello.o + cc -o build/hello build/hello.o + % ls build + hello + hello.o + + +
+ +
+ The &VariantDir; Function + + + + Use the &VariantDir; function to establish that target + files should be built in a separate directory + from the source files: + + + + + VariantDir('build', 'src') + env = Environment() + env.Program('build/hello.c') + + + + + Note that when you're not using + an &SConscript; file in the &src; subdirectory, + you must actually specify that + the program must be built from + the build/hello.c + file that &SCons; will duplicate in the + &build; subdirectory. + + + + + + When using the &VariantDir; function directly, + &SCons; still duplicates the source files + in the variant directory by default: + + + + + % ls src + hello.c + % scons -Q + cc -o build/hello.o -c build/hello.c + cc -o build/hello build/hello.o + % ls build + hello hello.c hello.o + + + + + You can specify the same duplicate=0 argument + that you can specify for an &SConscript; call: + + + + + VariantDir('build', 'src', duplicate=0) + env = Environment() + env.Program('build/hello.c') + + + + + In which case &SCons; + will disable duplication of the source files: + + + + + % ls src + hello.c + % scons -Q + cc -o build/hello.o -c src/hello.c + cc -o build/hello build/hello.o + % ls build + hello hello.o + + +
+ +
+ Using &VariantDir; With an &SConscript; File + + + + Even when using the &VariantDir; function, + it's much more natural to use it with + a subsidiary &SConscript; file. + For example, if the + src/SConscript + looks like this: + + + + + env = Environment() + env.Program('hello.c') + + + + + Then our &SConstruct; file could look like: + + + + + + VariantDir('build', 'src') + SConscript('build/SConscript') + + + + + Yielding the following output: + + + + + % ls src + SConscript hello.c + % scons -Q + cc -o build/hello.o -c build/hello.c + cc -o build/hello build/hello.o + % ls build + SConscript hello hello.c hello.o + + + + + Notice that this is completely equivalent + to the use of &SConscript; that we + learned about in the previous section. + + + +
+ +
+ Using &Glob; with &VariantDir; + + + + The &Glob; file name pattern matching function + works just as usual when using &VariantDir;. + For example, if the + src/SConscript + looks like this: + + + + + env = Environment() + env.Program('hello', Glob('*.c')) + + + + + Then with the same &SConstruct; file as in the previous section, + and source files f1.c + and f2.c in src, + we would see the following output: + + + + + % ls src + SConscript f1.c f2.c f2.h + % scons -Q + cc -o build/f1.o -c build/f1.c + cc -o build/f2.o -c build/f2.c + cc -o build/hello build/f1.o build/f2.o + % ls build + SConscript f1.c f1.o f2.c f2.h f2.o hello + + + + + The &Glob; function returns Nodes in the + build/ tree, as you'd expect. + + + +
+ + diff --git a/doc/user/sideeffect.in b/doc/user/sideeffect.in new file mode 100644 index 0000000..58c7306 --- /dev/null +++ b/doc/user/sideeffect.in @@ -0,0 +1,216 @@ + + + + + + + Sometimes a program the you need to call + to build a target file + will also update another file, + such as a log file describing what the program + does while building the target. + For example, we the folowing configuration + would have &SCons; invoke a hypothetical + script named build + (in the local directory) + with command-line arguments that write + log information to a common + logfile.txt file: + + + + + env = Environment() + env.Command('file1.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + env.Command('file2.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + + + + + This can cause problems when running + the build in parallel if + &SCons; decides to update both targets + by running both program invocations at the same time. + The multiple program invocations + may interfere with each other + writing to the common log file, + leading at best to intermixed output in the log file, + and at worst to an actual failed build + (on a system like Windows, for example, + where only one process at a time can open the log file for writing). + + + + + + We can make sure that &SCons; does not + run these build + commands at the same time + by using the &SideEffect; function + to specify that updating + the logfile.txt file + is a side effect of building the specified + file1 + and + file2 + target files: + + + + + + env = Environment() + f1 = env.Command('file1.out', 'file1.in', + './build --log logfile.txt $SOURCE $TARGET') + f2 = env.Command('file2.out', 'file2.in', + './build --log logfile.txt $SOURCE $TARGET') + env.SideEffect('logfile.txt', f1 + f2) + + file1.in + file2.in + + cat + + + + + + + + + + This makes sure the the two + ./build steps are run sequentially, + even withthe --jobs=2 in the command line: + + + + + scons -Q --jobs=2 + + + + + The &SideEffect; function can be called multiple + times for the same side-effect file. + Additionally, the name used as a &SideEffect; does not + even need to actually exist as a file on disk. + &SCons; will still make sure + that the relevant targets + will be executed sequentially, not in parallel: + + + + + + env = Environment() + f1 = env.Command('file1.out', [], 'echo >$TARGET data1') + env.SideEffect('not_really_updated', f1) + f2 = env.Command('file2.out', [], 'echo >$TARGET data2') + env.SideEffect('not_really_updated', f2) + + + + + scons -Q --jobs=2 + + + + + Note that it might be tempting to + use &SideEffect; for additional target files + that a command produces. + For example, versions the Microsoft Visual C/C++ compiler + produce a foo.ilk + alongside compiling foo.obj file. + Specifying foo.ilk as a + side-effect of foo.obj + is not a recommended use of &SideEffect;, + because &SCons; handle side-effect files + slightly differently in its analysis of the dependency graph. + When a command produces multiple output files, + they should be specified as multiple targets of + the call to the relevant builder function, + and the &SideEffect; function itself should really only be used + when it's important to ensure that commands are not executed in parallel, + such as when a "peripheral" file (such as a log file) + may actually updated by more than one command invocation. + + diff --git a/doc/user/sideeffect.xml b/doc/user/sideeffect.xml new file mode 100644 index 0000000..df62eca --- /dev/null +++ b/doc/user/sideeffect.xml @@ -0,0 +1,211 @@ + + + + + + + Sometimes a program the you need to call + to build a target file + will also update another file, + such as a log file describing what the program + does while building the target. + For example, we the folowing configuration + would have &SCons; invoke a hypothetical + script named build + (in the local directory) + with command-line arguments that write + log information to a common + logfile.txt file: + + + + + env = Environment() + env.Command('file1.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + env.Command('file2.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + + + + + This can cause problems when running + the build in parallel if + &SCons; decides to update both targets + by running both program invocations at the same time. + The multiple program invocations + may interfere with each other + writing to the common log file, + leading at best to intermixed output in the log file, + and at worst to an actual failed build + (on a system like Windows, for example, + where only one process at a time can open the log file for writing). + + + + + + We can make sure that &SCons; does not + run these build + commands at the same time + by using the &SideEffect; function + to specify that updating + the logfile.txt file + is a side effect of building the specified + file1 + and + file2 + target files: + + + + + env = Environment() + f1 = env.Command('file1.out', 'file1.in', + './build --log logfile.txt $SOURCE $TARGET') + f2 = env.Command('file2.out', 'file2.in', + './build --log logfile.txt $SOURCE $TARGET') + env.SideEffect('logfile.txt', f1 + f2) + + + + + + + + + This makes sure the the two + ./build steps are run sequentially, + even withthe --jobs=2 in the command line: + + + + + % scons -Q --jobs=2 + ./build --log logfile.txt file1.in file1.out + ./build --log logfile.txt file2.in file2.out + + + + + The &SideEffect; function can be called multiple + times for the same side-effect file. + Additionally, the name used as a &SideEffect; does not + even need to actually exist as a file on disk. + &SCons; will still make sure + that the relevant targets + will be executed sequentially, not in parallel: + + + + + env = Environment() + f1 = env.Command('file1.out', [], 'echo >$TARGET data1') + env.SideEffect('not_really_updated', f1) + f2 = env.Command('file2.out', [], 'echo >$TARGET data2') + env.SideEffect('not_really_updated', f2) + + + + % scons -Q --jobs=2 + echo > file1.out data1 + echo > file2.out data2 + + + + + Note that it might be tempting to + use &SideEffect; for additional target files + that a command produces. + For example, versions the Microsoft Visual C/C++ compiler + produce a foo.ilk + alongside compiling foo.obj file. + Specifying foo.ilk as a + side-effect of foo.obj + is not a recommended use of &SideEffect;, + because &SCons; handle side-effect files + slightly differently in its analysis of the dependency graph. + When a command produces multiple output files, + they should be specified as multiple targets of + the call to the relevant builder function, + and the &SideEffect; function itself should really only be used + when it's important to ensure that commands are not executed in parallel, + such as when a "peripheral" file (such as a log file) + may actually updated by more than one command invocation. + + diff --git a/doc/user/simple.in b/doc/user/simple.in new file mode 100644 index 0000000..4d42934 --- /dev/null +++ b/doc/user/simple.in @@ -0,0 +1,517 @@ + + + + + In this chapter, + you will see several examples of + very simple build configurations using &SCons;, + which will demonstrate how easy + it is to use &SCons; to + build programs from several different programming languages + on different types of systems. + + + +
+ Building Simple C / C++ Programs + + + + Here's the famous "Hello, World!" program in C: + + + + + int + main() + { + printf("Hello, world!\n"); + } + + + + + And here's how to build it using &SCons;. + Enter the following into a file named &SConstruct;: + + + + + + Program('hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + This minimal configuration file gives + &SCons; two pieces of information: + what you want to build + (an executable program), + and the input file from + which you want it built + (the hello.c file). + &b-link-Program; is a builder_method, + a Python call that tells &SCons; that you want to build an + executable program. + + + + + + That's it. Now run the &scons; command to build the program. + On a POSIX-compliant system like Linux or UNIX, + you'll see something like: + + + + + scons + + + + + On a Windows system with the Microsoft Visual C++ compiler, + you'll see something like: + + + + + scons + + + + + First, notice that you only need + to specify the name of the source file, + and that &SCons; correctly deduces the names of + the object and executable files to be built + from the base of the source file name. + + + + + + Second, notice that the same input &SConstruct; file, + without any changes, + generates the correct output file names on both systems: + hello.o and hello + on POSIX systems, + hello.obj and hello.exe + on Windows systems. + This is a simple example of how &SCons; + makes it extremely easy to + write portable software builds. + + + + + + (Note that we won't provide duplicate side-by-side + POSIX and Windows output for all of the examples in this guide; + just keep in mind that, unless otherwise specified, + any of the examples should work equally well on both types of systems.) + + + +
+ +
+ Building Object Files + + + + The &b-link-Program; builder method is only one of + many builder methods that &SCons; provides + to build different types of files. + Another is the &b-link-Object; builder method, + which tells &SCons; to build an object file + from the specified source file: + + + + + + Object('hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + + Now when you run the &scons; command to build the program, + it will build just the &hello_o; object file on a POSIX system: + + + + + scons + + + + + And just the &hello_obj; object file + on a Windows system (with the Microsoft Visual C++ compiler): + + + + + scons + + +
+ +
+ Simple Java Builds + + + + &SCons; also makes building with Java extremely easy. + Unlike the &b-link-Program; and &b-link-Object; builder methods, + however, the &b-link-Java; builder method + requires that you specify + the name of a destination directory in which + you want the class files placed, + followed by the source directory + in which the .java files live: + + + + + + Java('classes', 'src') + + + public class Example1 + { + public static void main(String[] args) + { + System.out.println("Hello Java world!\n"); + } + } + + + + + + If the src directory + contains a single hello.java file, + then the output from running the &scons; command + would look something like this + (on a POSIX system): + + + + + scons + + + + + We'll cover Java builds in more detail, + including building Java archive (.jar) + and other types of file, + in . + + + +
+ +
+ Cleaning Up After a Build + + + + When using &SCons;, it is unnecessary to add special + commands or target names to clean up after a build. + Instead, you simply use the + -c or --clean + option when you invoke &SCons;, + and &SCons; removes the appropriate built files. + So if we build our example above + and then invoke scons -c + afterwards, the output on POSIX looks like: + + + + + + Program('hello.c') + + + int main() { printf("Hello, world!\n"); } + + + + + scons + scons -c + + + + + And the output on Windows looks like: + + + + + scons + scons -c + + + + + Notice that &SCons; changes its output to tell you that it + is Cleaning targets ... and + done cleaning targets. + + + +
+ +
+ The &SConstruct; File + + + + If you're used to build systems like &Make; + you've already figured out that the &SConstruct; file + is the &SCons; equivalent of a &Makefile;. + That is, the &SConstruct; file is the input file + that &SCons; reads to control the build. + + + +
+ &SConstruct; Files Are Python Scripts + + + + There is, however, an important difference between + an &SConstruct; file and a &Makefile;: + the &SConstruct; file is actually a Python script. + If you're not already familiar with Python, don't worry. + This User's Guide will introduce you step-by-step + to the relatively small amount of Python you'll + need to know to be able to use &SCons; effectively. + And Python is very easy to learn. + + + + + + One aspect of using Python as the + scripting language is that you can put comments + in your &SConstruct; file using Python's commenting convention; + that is, everything between a '#' and the end of the line + will be ignored: + + + + + # Arrange to build the "hello" program. + Program('hello.c') # "hello.c" is the source file. + + + + + You'll see throughout the remainder of this Guide + that being able to use the power of a + real scripting language + can greatly simplify the solutions + to complex requirements of real-world builds. + + + +
+ +
+ &SCons; Functions Are Order-Independent + + + + One important way in which the &SConstruct; + file is not exactly like a normal Python script, + and is more like a &Makefile, + is that the order in which + the &SCons; functions are called in + the &SConstruct; file + does not + affect the order in which &SCons; + actually builds the programs and object files + you want it to build. + In programming parlance, + the &SConstruct; file is + declarative, + meaning you tell &SCons; what you want done + and let it figure out the order in which to do it, + rather than strictly imperative, + where you specify explicitly the order in + which to do things. + + + In other words, when you call the &b-link-Program; builder + (or any other builder method), + you're not telling &SCons; to build + the program at the instant the builder method is called. + Instead, you're telling &SCons; to build the program + that you want, for example, + a program built from a file named &hello_c;, + and it's up to &SCons; to build that program + (and any other files) whenever it's necessary. + (We'll learn more about how + &SCons; decides when building or rebuilding a file + is necessary in , below.) + + + + + + &SCons; reflects this distinction between + calling a builder method like &b-Program; + and actually building the program + by printing the status messages that indicate + when it's "just reading" the &SConstruct; file, + and when it's actually building the target files. + This is to make it clear when &SCons; is + executing the Python statements that make up the &SConstruct; file, + and when &SCons; is actually executing the + commands or other actions to + build the necessary files. + + + + + + Let's clarify this with an example. + Python has a print statement that + prints a string of characters to the screen. + If we put print statements around + our calls to the &b-Program; builder method: + + + + + + print "Calling Program('hello.c')" + Program('hello.c') + print "Calling Program('goodbye.c')" + Program('goodbye.c') + print "Finished calling Program()" + + + int main() { printf("Hello, world!\n"); } + + + int main() { printf("Goodbye, world!\n"); } + + + + + + Then when we execute &SCons;, + we see the output from the print + statements in between the messages about + reading the &SConscript; files, + indicating that that is when the + Python statements are being executed: + + + + + scons + + + + + Notice also that &SCons; built the &goodbye; program first, + even though the "reading &SConscript" output + shows that we called Program('hello.c') + first in the &SConstruct; file. + + + +
+ +
+ +
+ Making the &SCons; Output Less Verbose + + + + You've already seen how &SCons; prints + some messages about what it's doing, + surrounding the actual commands used to build the software: + + + + + scons + + + + + These messages emphasize the + order in which &SCons; does its work: + all of the configuration files + (generically referred to as &SConscript; files) + are read and executed first, + and only then are the target files built. + Among other benefits, these messages help to distinguish between + errors that occur while the configuration files are read, + and errors that occur while targets are being built. + + + + + + One drawback, of course, is that these messages clutter the output. + Fortunately, they're easily disabled by using + the &Q; option when invoking &SCons;: + + + + + scons -Q + + + + + Because we want this User's Guide to focus + on what &SCons; is actually doing, + we're going to use the &Q; option + to remove these messages from the + output of all the remaining examples in this Guide. + + + +
diff --git a/doc/user/simple.xml b/doc/user/simple.xml new file mode 100644 index 0000000..598d44b --- /dev/null +++ b/doc/user/simple.xml @@ -0,0 +1,587 @@ + + + + + In this chapter, + you will see several examples of + very simple build configurations using &SCons;, + which will demonstrate how easy + it is to use &SCons; to + build programs from several different programming languages + on different types of systems. + + + +
+ Building Simple C / C++ Programs + + + + Here's the famous "Hello, World!" program in C: + + + + + int + main() + { + printf("Hello, world!\n"); + } + + + + + And here's how to build it using &SCons;. + Enter the following into a file named &SConstruct;: + + + + + Program('hello.c') + + + + + This minimal configuration file gives + &SCons; two pieces of information: + what you want to build + (an executable program), + and the input file from + which you want it built + (the hello.c file). + &b-link-Program; is a builder_method, + a Python call that tells &SCons; that you want to build an + executable program. + + + + + + That's it. Now run the &scons; command to build the program. + On a POSIX-compliant system like Linux or UNIX, + you'll see something like: + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cc -o hello.o -c hello.c + cc -o hello hello.o + scons: done building targets. + + + + + On a Windows system with the Microsoft Visual C++ compiler, + you'll see something like: + + + + + C:\>scons + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + scons: done reading SConscript files. + scons: Building targets ... + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:hello.exe hello.obj + scons: done building targets. + + + + + First, notice that you only need + to specify the name of the source file, + and that &SCons; correctly deduces the names of + the object and executable files to be built + from the base of the source file name. + + + + + + Second, notice that the same input &SConstruct; file, + without any changes, + generates the correct output file names on both systems: + hello.o and hello + on POSIX systems, + hello.obj and hello.exe + on Windows systems. + This is a simple example of how &SCons; + makes it extremely easy to + write portable software builds. + + + + + + (Note that we won't provide duplicate side-by-side + POSIX and Windows output for all of the examples in this guide; + just keep in mind that, unless otherwise specified, + any of the examples should work equally well on both types of systems.) + + + +
+ +
+ Building Object Files + + + + The &b-link-Program; builder method is only one of + many builder methods that &SCons; provides + to build different types of files. + Another is the &b-link-Object; builder method, + which tells &SCons; to build an object file + from the specified source file: + + + + + Object('hello.c') + + + + + Now when you run the &scons; command to build the program, + it will build just the &hello_o; object file on a POSIX system: + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cc -o hello.o -c hello.c + scons: done building targets. + + + + + And just the &hello_obj; object file + on a Windows system (with the Microsoft Visual C++ compiler): + + + + + C:\>scons + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + scons: done reading SConscript files. + scons: Building targets ... + cl /Fohello.obj /c hello.c /nologo + scons: done building targets. + + +
+ +
+ Simple Java Builds + + + + &SCons; also makes building with Java extremely easy. + Unlike the &b-link-Program; and &b-link-Object; builder methods, + however, the &b-link-Java; builder method + requires that you specify + the name of a destination directory in which + you want the class files placed, + followed by the source directory + in which the .java files live: + + + + + Java('classes', 'src') + + + + + If the src directory + contains a single hello.java file, + then the output from running the &scons; command + would look something like this + (on a POSIX system): + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + javac -d classes -sourcepath src src/hello.java + scons: done building targets. + + + + + We'll cover Java builds in more detail, + including building Java archive (.jar) + and other types of file, + in . + + + +
+ +
+ Cleaning Up After a Build + + + + When using &SCons;, it is unnecessary to add special + commands or target names to clean up after a build. + Instead, you simply use the + -c or --clean + option when you invoke &SCons;, + and &SCons; removes the appropriate built files. + So if we build our example above + and then invoke scons -c + afterwards, the output on POSIX looks like: + + + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cc -o hello.o -c hello.c + cc -o hello hello.o + scons: done building targets. + % scons -c + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Cleaning targets ... + Removed hello.o + Removed hello + scons: done cleaning targets. + + + + + And the output on Windows looks like: + + + + + C:\>scons + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + scons: done reading SConscript files. + scons: Building targets ... + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:hello.exe hello.obj + scons: done building targets. + C:\>scons -c + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + scons: done reading SConscript files. + scons: Cleaning targets ... + Removed hello.obj + Removed hello.exe + scons: done cleaning targets. + + + + + Notice that &SCons; changes its output to tell you that it + is Cleaning targets ... and + done cleaning targets. + + + +
+ +
+ The &SConstruct; File + + + + If you're used to build systems like &Make; + you've already figured out that the &SConstruct; file + is the &SCons; equivalent of a &Makefile;. + That is, the &SConstruct; file is the input file + that &SCons; reads to control the build. + + + +
+ &SConstruct; Files Are Python Scripts + + + + There is, however, an important difference between + an &SConstruct; file and a &Makefile;: + the &SConstruct; file is actually a Python script. + If you're not already familiar with Python, don't worry. + This User's Guide will introduce you step-by-step + to the relatively small amount of Python you'll + need to know to be able to use &SCons; effectively. + And Python is very easy to learn. + + + + + + One aspect of using Python as the + scripting language is that you can put comments + in your &SConstruct; file using Python's commenting convention; + that is, everything between a '#' and the end of the line + will be ignored: + + + + + # Arrange to build the "hello" program. + Program('hello.c') # "hello.c" is the source file. + + + + + You'll see throughout the remainder of this Guide + that being able to use the power of a + real scripting language + can greatly simplify the solutions + to complex requirements of real-world builds. + + + +
+ +
+ &SCons; Functions Are Order-Independent + + + + One important way in which the &SConstruct; + file is not exactly like a normal Python script, + and is more like a &Makefile;, + is that the order in which + the &SCons; functions are called in + the &SConstruct; file + does not + affect the order in which &SCons; + actually builds the programs and object files + you want it to build. + In programming parlance, + the &SConstruct; file is + declarative, + meaning you tell &SCons; what you want done + and let it figure out the order in which to do it, + rather than strictly imperative, + where you specify explicitly the order in + which to do things. + + + In other words, when you call the &b-link-Program; builder + (or any other builder method), + you're not telling &SCons; to build + the program at the instant the builder method is called. + Instead, you're telling &SCons; to build the program + that you want, for example, + a program built from a file named &hello_c;, + and it's up to &SCons; to build that program + (and any other files) whenever it's necessary. + (We'll learn more about how + &SCons; decides when building or rebuilding a file + is necessary in , below.) + + + + + + &SCons; reflects this distinction between + calling a builder method like &b-Program; + and actually building the program + by printing the status messages that indicate + when it's "just reading" the &SConstruct; file, + and when it's actually building the target files. + This is to make it clear when &SCons; is + executing the Python statements that make up the &SConstruct; file, + and when &SCons; is actually executing the + commands or other actions to + build the necessary files. + + + + + + Let's clarify this with an example. + Python has a print statement that + prints a string of characters to the screen. + If we put print statements around + our calls to the &b-Program; builder method: + + + + + print "Calling Program('hello.c')" + Program('hello.c') + print "Calling Program('goodbye.c')" + Program('goodbye.c') + print "Finished calling Program()" + + + + + Then when we execute &SCons;, + we see the output from the print + statements in between the messages about + reading the &SConscript; files, + indicating that that is when the + Python statements are being executed: + + + + + % scons + scons: Reading SConscript files ... + Calling Program('hello.c') + Calling Program('goodbye.c') + Finished calling Program() + scons: done reading SConscript files. + scons: Building targets ... + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + cc -o hello.o -c hello.c + cc -o hello hello.o + scons: done building targets. + + + + + Notice also that &SCons; built the &goodbye; program first, + even though the "reading &SConscript;" output + shows that we called Program('hello.c') + first in the &SConstruct; file. + + + +
+ +
+ +
+ Making the &SCons; Output Less Verbose + + + + You've already seen how &SCons; prints + some messages about what it's doing, + surrounding the actual commands used to build the software: + + + + + C:\>scons + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + scons: done reading SConscript files. + scons: Building targets ... + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:hello.exe hello.obj + scons: done building targets. + + + + + These messages emphasize the + order in which &SCons; does its work: + all of the configuration files + (generically referred to as &SConscript; files) + are read and executed first, + and only then are the target files built. + Among other benefits, these messages help to distinguish between + errors that occur while the configuration files are read, + and errors that occur while targets are being built. + + + + + + One drawback, of course, is that these messages clutter the output. + Fortunately, they're easily disabled by using + the &Q; option when invoking &SCons;: + + + + + C:\>scons -Q + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + cl /Fohello.obj /c hello.c /nologo + link /nologo /OUT:hello.exe hello.obj + + + + + Because we want this User's Guide to focus + on what &SCons; is actually doing, + we're going to use the &Q; option + to remove these messages from the + output of all the remaining examples in this Guide. + + + +
diff --git a/doc/user/sourcecode.in b/doc/user/sourcecode.in new file mode 100644 index 0000000..5cbbe1a --- /dev/null +++ b/doc/user/sourcecode.in @@ -0,0 +1,162 @@ + + + + + + + XXX + + + +
+ Fetching Source Code From BitKeeper + + + + XXX + + + + + + env = Environment() + env.SourceCode('.', env.BitKeeper()) + env.Program('hello.c') + + + s.hello.c + + + + + scons -Q + + +
+ +
+ Fetching Source Code From CVS + + + + XXX + + + + + + env = Environment() + env.SourceCode('.', env.CVS('/usr/local/CVS')) + env.Program('hello.c') + + + + + scons -Q + + +
+ +
+ Fetching Source Code From RCS + + + + XXX + + + + + + env = Environment() + env.SourceCode('.', env.RCS()) + env.Program('hello.c') + + + hello.c,v + + + + + scons -Q + + +
+ +
+ Fetching Source Code From SCCS + + + + XXX + + + + + + env = Environment() + env.SourceCode('.', env.SCCS()) + env.Program('hello.c') + + + s.hello.c + + + + + scons -Q + + +
+ + diff --git a/doc/user/sourcecode.xml b/doc/user/sourcecode.xml new file mode 100644 index 0000000..aebcd20 --- /dev/null +++ b/doc/user/sourcecode.xml @@ -0,0 +1,157 @@ + + + + + + + XXX + + + +
+ Fetching Source Code From BitKeeper + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.BitKeeper()) + env.Program('hello.c') + + + + % scons -Q + bk get hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Fetching Source Code From CVS + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.CVS('/usr/local/CVS')) + env.Program('hello.c') + + + + % scons -Q + cvs -d /usr/local/CVS co hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Fetching Source Code From RCS + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.RCS()) + env.Program('hello.c') + + + + % scons -Q + co hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Fetching Source Code From SCCS + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.SCCS()) + env.Program('hello.c') + + + + % scons -Q + sccs get hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ + diff --git a/doc/user/tasks.in b/doc/user/tasks.in new file mode 100644 index 0000000..14775c8 --- /dev/null +++ b/doc/user/tasks.in @@ -0,0 +1,108 @@ + + + +There is a common set of simple tasks that many build configurations rely +on as they become more complex. Most build tools have special +purpose constructs for performing these tasks, but since &SConscript; +files are &Python; scripts, you can use more flexible built-in &Python; +services to perform these tasks. This appendix lists a number of these +tasks and how to implement them in &Python;. + + + +Wildcard globbing to create a list of filenames + +files = Glob(wildcard) + + + + +Filename extension substitution + +import os.path +filename = os.path.splitext(filename)[0]+extension + + + + +Appending a path prefix to a list of filenames + +import os.path +filenames = [os.path.join(prefix, x) for x in filenames] + + +or in Python 1.5.2: + + +import os.path +new_filenames = [] +for x in filenames: + new_filenames.append(os.path.join(prefix, x)) + + + + +Substituting a path prefix with another one + +if filename.find(old_prefix) == 0: + filename = filename.replace(old_prefix, new_prefix) + + +or in Python 1.5.2: + + +import string +if string.find(filename, old_prefix) == 0: + filename = string.replace(filename, old_prefix, new_prefix) + + + + +Filtering a filename list to exclude/retain only a specific set +of extensions + +import os.path +filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] + + +or in Python 1.5.2: + + +import os.path +new_filenames = [] +for x in filenames: + if os.path.splitext(x)[1] in extensions: + new_filenames.append(x) + + + + +The "backtick function": run a shell command and capture the +output +import os +output = os.popen(command).read() + + diff --git a/doc/user/tasks.xml b/doc/user/tasks.xml new file mode 100644 index 0000000..14775c8 --- /dev/null +++ b/doc/user/tasks.xml @@ -0,0 +1,108 @@ + + + +There is a common set of simple tasks that many build configurations rely +on as they become more complex. Most build tools have special +purpose constructs for performing these tasks, but since &SConscript; +files are &Python; scripts, you can use more flexible built-in &Python; +services to perform these tasks. This appendix lists a number of these +tasks and how to implement them in &Python;. + + + +Wildcard globbing to create a list of filenames + +files = Glob(wildcard) + + + + +Filename extension substitution + +import os.path +filename = os.path.splitext(filename)[0]+extension + + + + +Appending a path prefix to a list of filenames + +import os.path +filenames = [os.path.join(prefix, x) for x in filenames] + + +or in Python 1.5.2: + + +import os.path +new_filenames = [] +for x in filenames: + new_filenames.append(os.path.join(prefix, x)) + + + + +Substituting a path prefix with another one + +if filename.find(old_prefix) == 0: + filename = filename.replace(old_prefix, new_prefix) + + +or in Python 1.5.2: + + +import string +if string.find(filename, old_prefix) == 0: + filename = string.replace(filename, old_prefix, new_prefix) + + + + +Filtering a filename list to exclude/retain only a specific set +of extensions + +import os.path +filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] + + +or in Python 1.5.2: + + +import os.path +new_filenames = [] +for x in filenames: + if os.path.splitext(x)[1] in extensions: + new_filenames.append(x) + + + + +The "backtick function": run a shell command and capture the +output +import os +output = os.popen(command).read() + + diff --git a/doc/user/tools.in b/doc/user/tools.in new file mode 100644 index 0000000..cc0628e --- /dev/null +++ b/doc/user/tools.in @@ -0,0 +1,38 @@ + + + + +This appendix contains descriptions of all of the +Tools modules that are +available "out of the box" in this version of SCons. + + + + + +&tools-gen; + + diff --git a/doc/user/tools.xml b/doc/user/tools.xml new file mode 100644 index 0000000..cc0628e --- /dev/null +++ b/doc/user/tools.xml @@ -0,0 +1,38 @@ + + + + +This appendix contains descriptions of all of the +Tools modules that are +available "out of the box" in this version of SCons. + + + + + +&tools-gen; + + diff --git a/doc/user/troubleshoot.in b/doc/user/troubleshoot.in new file mode 100644 index 0000000..c729149 --- /dev/null +++ b/doc/user/troubleshoot.in @@ -0,0 +1,875 @@ + + + + + The experience of configuring any + software build tool to build a large code base + usually, at some point, + involves trying to figure out why + the tool is behaving a certain way, + and how to get it to behave the way you want. + &SCons; is no different. + This appendix contains a number of + different ways in which you can + get some additional insight into &SCons' behavior. + + + + + + Note that we're always interested in trying to + improve how you can troubleshoot configuration problems. + If you run into a problem that has + you scratching your head, + and which there just doesn't seem to be a good way to debug, + odds are pretty good that someone else will run into + the same problem, too. + If so, please let the SCons development team know + (preferably by filing a bug report + or feature request at our project pages at tigris.org) + so that we can use your feedback + to try to come up with a better way to help you, + and others, get the necessary insight into &SCons; behavior + to help identify and fix configuration issues. + + + +
+ Why is That Target Being Rebuilt? the &debug-explain; Option + + + + Let's look at a simple example of + a misconfigured build + that causes a target to be rebuilt + every time &SCons; is run: + + + + + + # Intentionally misspell the output file name in the + # command used to create the file: + Command('file.out', 'file.in', 'cp $SOURCE file.oout') + + + file.in + + + + + + (Note to Windows users: The POSIX &cp; command + copies the first file named on the command line + to the second file. + In our example, it copies the &file_in; file + to the &file_out; file.) + + + + + + Now if we run &SCons; multiple times on this example, + we see that it re-runs the &cp; + command every time: + + + + + scons -Q + scons -Q + scons -Q + + + + + In this example, + the underlying cause is obvious: + we've intentionally misspelled the output file name + in the &cp; command, + so the command doesn't actually + build the &file_out; file that we've told &SCons; to expect. + But if the problem weren't obvious, + it would be helpful + to specify the &debug-explain; option + on the command line + to have &SCons; tell us very specifically + why it's decided to rebuild the target: + + + + + scons -Q --debug=explain + + + + + If this had been a more complicated example + involving a lot of build output, + having &SCons; tell us that + it's trying to rebuild the target file + because it doesn't exist + would be an important clue + that something was wrong with + the command that we invoked to build it. + + + + + + The &debug-explain; option also comes in handy + to help figure out what input file changed. + Given a simple configuration that builds + a program from three source files, + changing one of the source files + and rebuilding with the &debug-explain; + option shows very specifically + why &SCons; rebuilds the files that it does: + + + + + + Program('prog', ['file1.c', 'file2.c', 'file3.c']) + + + file1.c + + + file2.c + + + file3.c + + + + + scons -Q + edit file2.c + scons -Q --debug=explain + + + + + This becomes even more helpful + in identifying when a file is rebuilt + due to a change in an implicit dependency, + such as an incuded .h file. + If the file1.c + and file3.c files + in our example + both included a &hello_h; file, + then changing that included file + and re-running &SCons; with the &debug-explain; option + will pinpoint that it's the change to the included file + that starts the chain of rebuilds: + + + + + + Program('prog', ['file1.c', 'file2.c', 'file3.c'], CPPPATH='.') + + + #include <hello.h> + file1.c + + + file2.c + + + #include <hello.h> + file3.c + + + #define string "world" + + + + + scons -Q + edit hello.h + scons -Q --debug=explain + + + + + (Note that the &debug-explain; option will only tell you + why &SCons; decided to rebuild necessary targets. + It does not tell you what files it examined + when deciding not + to rebuild a target file, + which is often a more valuable question to answer.) + + + +
+ +
+ What's in That Construction Environment? the &Dump; Method + + + + When you create a construction environment, + &SCons; populates it + with construction variables that are set up + for various compilers, linkers and utilities + that it finds on your system. + Although this is usually helpful and what you want, + it might be frustrating if &SCons; + doesn't set certain variables that you + expect to be set. + In situations like this, + it's sometimes helpful to use the + construction environment &Dump; method + to print all or some of + the construction variables. + Note that the &Dump; method + returns + the representation of the variables + in the environment + for you to print (or otherwise manipulate): + + + + + + env = Environment() + print env.Dump() + + + + + + On a POSIX system with gcc installed, + this might generate: + + + + + scons + + + + + On a Windows system with Visual C++ + the output might look like: + + + + + scons + + + + + The construction environments in these examples have + actually been restricted to just gcc and Visual C++, + respectively. + In a real-life situation, + the construction environments will + likely contain a great many more variables. + Also note that we've massaged the example output above + to make the memory address of all objects a constant 0x700000. + In reality, you would see a different hexadecimal + number for each object. + + + + + + To make it easier to see just what you're + interested in, + the &Dump; method allows you to + specify a specific constrcution variable + that you want to disply. + For example, + it's not unusual to want to verify + the external environment used to execute build commands, + to make sure that the PATH and other + environment variables are set up the way they should be. + You can do this as follows: + + + + + + env = Environment() + print env.Dump('ENV') + + + + + + Which might display the following when executed on a POSIX system: + + + + + scons + + + + + And the following when executed on a Windows system: + + + + + scons + + +
+ +
+ + What Dependencies Does &SCons; Know About? the &tree; Option + + + + Sometimes the best way to try to figure out what + &SCons; is doing is simply to take a look at the + dependency graph that it constructs + based on your &SConscript; files. + The --tree option + will display all or part of the + &SCons; dependency graph in an + "ASCII art" graphical format + that shows the dependency hierarchy. + + + + + + For example, given the following input &SConstruct; file: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + inc.h + + + + + + Running &SCons; with the --tree=all + option yields: + + + + + scons -Q --tree=all + + + + + The tree will also be printed when the + -n (no execute) option is used, + which allows you to examine the dependency graph + for a configuration without actually + rebuilding anything in the tree. + + + + + + The --tree option only prints + the dependency graph for the specified targets + (or the default target(s) if none are specified on the command line). + So if you specify a target like f2.o + on the command line, + the --tree option will only + print the dependency graph for that file: + + + + + scons -Q --tree=all f2.o + + + + + This is, of course, useful for + restricting the output from a very large + build configuration to just a + portion in which you're interested. + Multiple targets are fine, + in which case a tree will be printed + for each specified target: + + + + + scons -Q --tree=all f1.o f3.o + + + + + The status argument may be used + to tell &SCons; to print status information about + each file in the dependency graph: + + + + + scons -Q --tree=status + + + + + Note that --tree=all,status is equivalent; + the all + is assumed if only status is present. + As an alternative to all, + you can specify --tree=derived + to have &SCons; only print derived targets + in the tree output, + skipping source files + (like .c and .h files): + + + + + scons -Q --tree=derived + + + + + You can use the status + modifier with derived as well: + + + + + scons -Q --tree=derived,status + + + + + Note that the order of the --tree= + arguments doesn't matter; + --tree=status,derived is + completely equivalent. + + + + + + The default behavior of the --tree option + is to repeat all of the dependencies each time the library dependency + (or any other dependency file) is encountered in the tree. + If certain target files share other target files, + such as two programs that use the same library: + + + + + + env = Environment(CPPPATH = ['.'], + LIBS = ['foo'], + LIBPATH = ['.']) + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Program('prog1.c') + env.Program('prog2.c') + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + inc.h + + + + + + Then there can be a lot of repetition in the + --tree= output: + + + + + scons -Q --tree=all + + + + + In a large configuration with many internal libraries + and include files, + this can very quickly lead to huge output trees. + To help make this more manageable, + a prune modifier may + be added to the option list, + in which case &SCons; + will print the name of a target that has + already been visited during the tree-printing + in [square brackets] + as an indication that the dependencies + of the target file may be found + by looking farther up the tree: + + + + + scons -Q --tree=prune + + + + + Like the status keyword, + the prune argument by itself + is equivalent to --tree=all,prune. + + + +
+ +
+ + How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option + + + + Sometimes it's useful to look at the + pre-substitution string + that &SCons; uses to generate + the command lines it executes. + This can be done with the &debug-presub; option: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', 'prog.c') + + + prog.c + + + + + + + % scons -Q --debug=presub + Building prog.o with action: + $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES + cc -o prog.o -c -I. prog.c + Building prog with action: + $SMART_LINKCOM + cc -o prog prog.o + + +
+ +
+ + Where is &SCons; Searching for Libraries? the &debug-findlibs; Option + + + + To get some insight into what library names + &SCons; is searching for, + and in which directories it is searching, + Use the --debug=findlibs option. + Given the following input &SConstruct; file: + + + + + + env = Environment(LIBPATH = ['libs1', 'libs2']) + env.Program('prog.c', LIBS=['foo', 'bar']) + + + prog.c + + + libs1/libfoo.a + + + libs2/libbar.a + + + + + + And the libraries libfoo.a + and libbar.a + in libs1 and libs2, + respectively, + use of the --debug=findlibs option yields: + + + + + scons -Q --debug=findlibs + + +
+ + + +
+ + Where is &SCons; Blowing Up? the &debug-stacktrace; Option + + + + In general, &SCons; tries to keep its error + messages short and informative. + That means we usually try to avoid showing + the stack traces that are familiar + to experienced Python programmers, + since they usually contain much more + information than is useful to most people. + + + + + + For example, the following &SConstruct file: + + + + + + Program('prog.c') + + + + + + Generates the following error if the + prog.c file + does not exist: + + + + + scons -Q + + + + + In this case, + the error is pretty obvious. + But if it weren't, + and you wanted to try to get more information + about the error, + the &debug-stacktrace; option + would show you exactly where in the &SCons; source code + the problem occurs: + + + + + scons -Q --debug=stacktrace + + + + + Of course, if you do need to dive into the &SCons; source code, + we'd like to know if, or how, + the error messages or troubleshooting options + could have been improved to avoid that. + Not everyone has the necessary time or + Python skill to dive into the source code, + and we'd like to improve &SCons; + for those people as well... + + + +
+ +
+ + How is &SCons; Making Its Decisions? the &taskmastertrace; Option + + + + The internal &SCons; subsystem that handles walking + the dependency graph + and controls the decision-making about what to rebuild + is the Taskmaster. + &SCons; supports a --taskmastertrace + option that tells the Taskmaster to print + information about the children (dependencies) + of the various Nodes on its walk down the graph, + which specific dependent Nodes are being evaluated, + and in what order. + + + + + + The --taskmastertrace option + takes as an argument the name of a file in + which to put the trace output, + with - (a single hyphen) + indicating that the trace messages + should be printed to the standard output: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog.c') + + + #include "inc.h" + prog.c + + + #define STRING "one" + + + + + scons -Q --taskmastertrace=- prog + + + + + The --taskmastertrace option + doesn't provide information about the actual + calculations involved in deciding if a file is up-to-date, + but it does show all of the dependencies + it knows about for each Node, + and the order in which those dependencies are evaluated. + This can be useful as an alternate way to determine + whether or not your &SCons; configuration, + or the implicit dependency scan, + has actually identified all the correct dependencies + you want it to. + + + +
+ + + + diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml new file mode 100644 index 0000000..6bd53f9 --- /dev/null +++ b/doc/user/troubleshoot.xml @@ -0,0 +1,1313 @@ + + + + + The experience of configuring any + software build tool to build a large code base + usually, at some point, + involves trying to figure out why + the tool is behaving a certain way, + and how to get it to behave the way you want. + &SCons; is no different. + This appendix contains a number of + different ways in which you can + get some additional insight into &SCons;' behavior. + + + + + + Note that we're always interested in trying to + improve how you can troubleshoot configuration problems. + If you run into a problem that has + you scratching your head, + and which there just doesn't seem to be a good way to debug, + odds are pretty good that someone else will run into + the same problem, too. + If so, please let the SCons development team know + (preferably by filing a bug report + or feature request at our project pages at tigris.org) + so that we can use your feedback + to try to come up with a better way to help you, + and others, get the necessary insight into &SCons; behavior + to help identify and fix configuration issues. + + + +
+ Why is That Target Being Rebuilt? the &debug-explain; Option + + + + Let's look at a simple example of + a misconfigured build + that causes a target to be rebuilt + every time &SCons; is run: + + + + + # Intentionally misspell the output file name in the + # command used to create the file: + Command('file.out', 'file.in', 'cp $SOURCE file.oout') + + + + + (Note to Windows users: The POSIX &cp; command + copies the first file named on the command line + to the second file. + In our example, it copies the &file_in; file + to the &file_out; file.) + + + + + + Now if we run &SCons; multiple times on this example, + we see that it re-runs the &cp; + command every time: + + + + + % scons -Q + cp file.in file.oout + % scons -Q + cp file.in file.oout + % scons -Q + cp file.in file.oout + + + + + In this example, + the underlying cause is obvious: + we've intentionally misspelled the output file name + in the &cp; command, + so the command doesn't actually + build the &file_out; file that we've told &SCons; to expect. + But if the problem weren't obvious, + it would be helpful + to specify the &debug-explain; option + on the command line + to have &SCons; tell us very specifically + why it's decided to rebuild the target: + + + + + % scons -Q --debug=explain + scons: building `file.out' because it doesn't exist + cp file.in file.oout + + + + + If this had been a more complicated example + involving a lot of build output, + having &SCons; tell us that + it's trying to rebuild the target file + because it doesn't exist + would be an important clue + that something was wrong with + the command that we invoked to build it. + + + + + + The &debug-explain; option also comes in handy + to help figure out what input file changed. + Given a simple configuration that builds + a program from three source files, + changing one of the source files + and rebuilding with the &debug-explain; + option shows very specifically + why &SCons; rebuilds the files that it does: + + + + + + + % scons -Q + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o file3.o -c file3.c + cc -o prog file1.o file2.o file3.o + % edit file2.c + [CHANGE THE CONTENTS OF file2.c] + % scons -Q --debug=explain + scons: rebuilding `file2.o' because `file2.c' changed + cc -o file2.o -c file2.c + scons: rebuilding `prog' because `file2.o' changed + cc -o prog file1.o file2.o file3.o + + + + + This becomes even more helpful + in identifying when a file is rebuilt + due to a change in an implicit dependency, + such as an incuded .h file. + If the file1.c + and file3.c files + in our example + both included a &hello_h; file, + then changing that included file + and re-running &SCons; with the &debug-explain; option + will pinpoint that it's the change to the included file + that starts the chain of rebuilds: + + + + + + + % scons -Q + cc -o file1.o -c -I. file1.c + cc -o file2.o -c -I. file2.c + cc -o file3.o -c -I. file3.c + cc -o prog file1.o file2.o file3.o + % edit hello.h + [CHANGE THE CONTENTS OF hello.h] + % scons -Q --debug=explain + scons: rebuilding `file1.o' because `hello.h' changed + cc -o file1.o -c -I. file1.c + scons: rebuilding `file3.o' because `hello.h' changed + cc -o file3.o -c -I. file3.c + scons: rebuilding `prog' because: + `file1.o' changed + `file3.o' changed + cc -o prog file1.o file2.o file3.o + + + + + (Note that the &debug-explain; option will only tell you + why &SCons; decided to rebuild necessary targets. + It does not tell you what files it examined + when deciding not + to rebuild a target file, + which is often a more valuable question to answer.) + + + +
+ +
+ What's in That Construction Environment? the &Dump; Method + + + + When you create a construction environment, + &SCons; populates it + with construction variables that are set up + for various compilers, linkers and utilities + that it finds on your system. + Although this is usually helpful and what you want, + it might be frustrating if &SCons; + doesn't set certain variables that you + expect to be set. + In situations like this, + it's sometimes helpful to use the + construction environment &Dump; method + to print all or some of + the construction variables. + Note that the &Dump; method + returns + the representation of the variables + in the environment + for you to print (or otherwise manipulate): + + + + + env = Environment() + print env.Dump() + + + + + On a POSIX system with gcc installed, + this might generate: + + + + + % scons + scons: Reading SConscript files ... + { 'BUILDERS': {'_InternalInstall': <function InstallBuilderWrapper at 0x700000>, '_InternalInstallAs': <function InstallAsBuilderWrapper at 0x700000>}, + 'CONFIGUREDIR': '#/.sconf_temp', + 'CONFIGURELOG': '#/config.log', + 'CPPSUFFIXES': [ '.c', + '.C', + '.cxx', + '.cpp', + '.c++', + '.cc', + '.h', + '.H', + '.hxx', + '.hpp', + '.hh', + '.F', + '.fpp', + '.FPP', + '.m', + '.mm', + '.S', + '.spp', + '.SPP'], + 'DSUFFIXES': ['.d'], + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}, + 'ESCAPE': <function escape at 0x700000>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'HOST_ARCH': None, + 'HOST_OS': None, + 'IDLSUFFIXES': ['.idl', '.IDL'], + 'INSTALL': <function copyFunc at 0x700000>, + 'LIBPREFIX': 'lib', + 'LIBPREFIXES': ['$LIBPREFIX'], + 'LIBSUFFIX': '.a', + 'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'], + 'MAXLINELENGTH': 128072, + 'OBJPREFIX': '', + 'OBJSUFFIX': '.o', + 'PLATFORM': 'posix', + 'PROGPREFIX': '', + 'PROGSUFFIX': '', + 'PSPAWN': <function piped_env_spawn at 0x700000>, + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'SCANNERS': [], + 'SHELL': 'sh', + 'SHLIBPREFIX': '$LIBPREFIX', + 'SHLIBSUFFIX': '.so', + 'SHOBJPREFIX': '$OBJPREFIX', + 'SHOBJSUFFIX': '$OBJSUFFIX', + 'SPAWN': <function spawnvpe_spawn at 0x700000>, + 'TARGET_ARCH': None, + 'TARGET_OS': None, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0x700000>, + 'TEMPFILEPREFIX': '@', + 'TOOLS': ['install', 'install'], + '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '__RPATH': '$_RPATH', + '_concat': <function _concat at 0x700000>, + '_defines': <function _defines at 0x700000>, + '_stripixes': <function _stripixes at 0x700000>} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + On a Windows system with Visual C++ + the output might look like: + + + + + C:\>scons + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + { 'BUILDERS': {'_InternalInstall': <function InstallBuilderWrapper at 0x700000>, 'Object': <SCons.Builder.CompositeBuilder instance at 0x700000>, 'PCH': <SCons.Builder.BuilderBase instance at 0x700000>, 'RES': <SCons.Builder.BuilderBase instance at 0x700000>, 'SharedObject': <SCons.Builder.CompositeBuilder instance at 0x700000>, 'StaticObject': <SCons.Builder.CompositeBuilder instance at 0x700000>, '_InternalInstallAs': <function InstallAsBuilderWrapper at 0x700000>}, + 'CC': 'cl', + 'CCCOM': <SCons.Action.FunctionAction instance at 0x700000>, + 'CCFLAGS': ['/nologo'], + 'CCPCHFLAGS': ['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'], + 'CCPDBFLAGS': ['${(PDB and "/Z7") or ""}'], + 'CFILESUFFIX': '.c', + 'CFLAGS': [], + 'CONFIGUREDIR': '#/.sconf_temp', + 'CONFIGURELOG': '#/config.log', + 'CPPDEFPREFIX': '/D', + 'CPPDEFSUFFIX': '', + 'CPPSUFFIXES': [ '.c', + '.C', + '.cxx', + '.cpp', + '.c++', + '.cc', + '.h', + '.H', + '.hxx', + '.hpp', + '.hh', + '.F', + '.fpp', + '.FPP', + '.m', + '.mm', + '.S', + '.spp', + '.SPP'], + 'CXX': '$CC', + 'CXXCOM': '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM', + 'CXXFILESUFFIX': '.cc', + 'CXXFLAGS': ['$(', '/TP', '$)'], + 'DSUFFIXES': ['.d'], + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'ENV': { 'PATH': 'C:/WINDOWS\\System32', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:/WINDOWS'}, + 'ESCAPE': <function escape at 0x700000>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'HOST_ARCH': '', + 'HOST_OS': 'win32', + 'IDLSUFFIXES': ['.idl', '.IDL'], + 'INCPREFIX': '/I', + 'INCSUFFIX': '', + 'INSTALL': <function copyFunc at 0x700000>, + 'LIBPREFIX': '', + 'LIBPREFIXES': ['$LIBPREFIX'], + 'LIBSUFFIX': '.lib', + 'LIBSUFFIXES': ['$LIBSUFFIX'], + 'MAXLINELENGTH': 2048, + 'MSVC_SETUP_RUN': True, + 'OBJPREFIX': '', + 'OBJSUFFIX': '.obj', + 'PCHCOM': '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS', + 'PCHPDBFLAGS': ['${(PDB and "/Yd") or ""}'], + 'PLATFORM': 'win32', + 'PROGPREFIX': '', + 'PROGSUFFIX': '.exe', + 'PSPAWN': <function piped_spawn at 0x700000>, + 'RC': 'rc', + 'RCCOM': <SCons.Action.FunctionAction instance at 0x700000>, + 'RCFLAGS': [], + 'RCSUFFIXES': ['.rc', '.rc2'], + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, + 'SCANNERS': [], + 'SHCC': '$CC', + 'SHCCCOM': <SCons.Action.FunctionAction instance at 0x700000>, + 'SHCCFLAGS': ['$CCFLAGS'], + 'SHCFLAGS': ['$CFLAGS'], + 'SHCXX': '$CXX', + 'SHCXXCOM': '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM', + 'SHCXXFLAGS': ['$CXXFLAGS'], + 'SHELL': None, + 'SHLIBPREFIX': '', + 'SHLIBSUFFIX': '.dll', + 'SHOBJPREFIX': '$OBJPREFIX', + 'SHOBJSUFFIX': '$OBJSUFFIX', + 'SPAWN': <function spawn at 0x700000>, + 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1, + 'TARGET_ARCH': '', + 'TARGET_OS': 'win32', + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0x700000>, + 'TEMPFILEPREFIX': '@', + 'TOOLS': ['msvc', 'install', 'install'], + '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS', + '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '_MSVC_OUTPUT_FLAG': <function msvc_output_flag at 0x700000>, + '_concat': <function _concat at 0x700000>, + '_defines': <function _defines at 0x700000>, + '_stripixes': <function _stripixes at 0x700000>} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + The construction environments in these examples have + actually been restricted to just gcc and Visual C++, + respectively. + In a real-life situation, + the construction environments will + likely contain a great many more variables. + Also note that we've massaged the example output above + to make the memory address of all objects a constant 0x700000. + In reality, you would see a different hexadecimal + number for each object. + + + + + + To make it easier to see just what you're + interested in, + the &Dump; method allows you to + specify a specific constrcution variable + that you want to disply. + For example, + it's not unusual to want to verify + the external environment used to execute build commands, + to make sure that the PATH and other + environment variables are set up the way they should be. + You can do this as follows: + + + + + env = Environment() + print env.Dump('ENV') + + + + + Which might display the following when executed on a POSIX system: + + + + + % scons + scons: Reading SConscript files ... + {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + And the following when executed on a Windows system: + + + + + C:\>scons + scons: Reading SConscript files ... + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + { 'PATH': 'C:/WINDOWS\\System32', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:/WINDOWS'} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + +
+ +
+ + What Dependencies Does &SCons; Know About? the &tree; Option + + + + Sometimes the best way to try to figure out what + &SCons; is doing is simply to take a look at the + dependency graph that it constructs + based on your &SConscript; files. + The --tree option + will display all or part of the + &SCons; dependency graph in an + "ASCII art" graphical format + that shows the dependency hierarchy. + + + + + + For example, given the following input &SConstruct; file: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) + + + + + Running &SCons; with the --tree=all + option yields: + + + + + % scons -Q --tree=all + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + +-. + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-prog + +-f1.o + | +-f1.c + | +-inc.h + +-f2.o + | +-f2.c + | +-inc.h + +-f3.o + +-f3.c + +-inc.h + + + + + The tree will also be printed when the + -n (no execute) option is used, + which allows you to examine the dependency graph + for a configuration without actually + rebuilding anything in the tree. + + + + + + The --tree option only prints + the dependency graph for the specified targets + (or the default target(s) if none are specified on the command line). + So if you specify a target like f2.o + on the command line, + the --tree option will only + print the dependency graph for that file: + + + + + % scons -Q --tree=all f2.o + cc -o f2.o -c -I. f2.c + +-f2.o + +-f2.c + +-inc.h + + + + + This is, of course, useful for + restricting the output from a very large + build configuration to just a + portion in which you're interested. + Multiple targets are fine, + in which case a tree will be printed + for each specified target: + + + + + % scons -Q --tree=all f1.o f3.o + cc -o f1.o -c -I. f1.c + +-f1.o + +-f1.c + +-inc.h + cc -o f3.o -c -I. f3.c + +-f3.o + +-f3.c + +-inc.h + + + + + The status argument may be used + to tell &SCons; to print status information about + each file in the dependency graph: + + + + + % scons -Q --tree=status + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + E = exists + R = exists in repository only + b = implicit builder + B = explicit builder + S = side effect + P = precious + A = always build + C = current + N = no clean + H = no cache + + [E b ]+-. + [E C ] +-SConstruct + [E C ] +-f1.c + [E B C ] +-f1.o + [E C ] | +-f1.c + [E C ] | +-inc.h + [E C ] +-f2.c + [E B C ] +-f2.o + [E C ] | +-f2.c + [E C ] | +-inc.h + [E C ] +-f3.c + [E B C ] +-f3.o + [E C ] | +-f3.c + [E C ] | +-inc.h + [E C ] +-inc.h + [E B C ] +-prog + [E B C ] +-f1.o + [E C ] | +-f1.c + [E C ] | +-inc.h + [E B C ] +-f2.o + [E C ] | +-f2.c + [E C ] | +-inc.h + [E B C ] +-f3.o + [E C ] +-f3.c + [E C ] +-inc.h + + + + + Note that --tree=all,status is equivalent; + the all + is assumed if only status is present. + As an alternative to all, + you can specify --tree=derived + to have &SCons; only print derived targets + in the tree output, + skipping source files + (like .c and .h files): + + + + + % scons -Q --tree=derived + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + +-. + +-f1.o + +-f2.o + +-f3.o + +-prog + +-f1.o + +-f2.o + +-f3.o + + + + + You can use the status + modifier with derived as well: + + + + + % scons -Q --tree=derived,status + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + E = exists + R = exists in repository only + b = implicit builder + B = explicit builder + S = side effect + P = precious + A = always build + C = current + N = no clean + H = no cache + + [E b ]+-. + [E B C ] +-f1.o + [E B C ] +-f2.o + [E B C ] +-f3.o + [E B C ] +-prog + [E B C ] +-f1.o + [E B C ] +-f2.o + [E B C ] +-f3.o + + + + + Note that the order of the --tree= + arguments doesn't matter; + --tree=status,derived is + completely equivalent. + + + + + + The default behavior of the --tree option + is to repeat all of the dependencies each time the library dependency + (or any other dependency file) is encountered in the tree. + If certain target files share other target files, + such as two programs that use the same library: + + + + + env = Environment(CPPPATH = ['.'], + LIBS = ['foo'], + LIBPATH = ['.']) + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Program('prog1.c') + env.Program('prog2.c') + + + + + Then there can be a lot of repetition in the + --tree= output: + + + + + % scons -Q --tree=all + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog1.o -c -I. prog1.c + cc -o prog1 prog1.o -L. -lfoo + cc -o prog2.o -c -I. prog2.c + cc -o prog2 prog2.o -L. -lfoo + +-. + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog1 + | +-prog1.o + | | +-prog1.c + | | +-inc.h + | +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog1.c + +-prog1.o + | +-prog1.c + | +-inc.h + +-prog2 + | +-prog2.o + | | +-prog2.c + | | +-inc.h + | +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog2.c + +-prog2.o + +-prog2.c + +-inc.h + + + + + In a large configuration with many internal libraries + and include files, + this can very quickly lead to huge output trees. + To help make this more manageable, + a prune modifier may + be added to the option list, + in which case &SCons; + will print the name of a target that has + already been visited during the tree-printing + in [square brackets] + as an indication that the dependencies + of the target file may be found + by looking farther up the tree: + + + + + % scons -Q --tree=prune + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog1.o -c -I. prog1.c + cc -o prog1 prog1.o -L. -lfoo + cc -o prog2.o -c -I. prog2.c + cc -o prog2 prog2.o -L. -lfoo + +-. + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-libfoo.a + | +-[f1.o] + | +-[f2.o] + | +-[f3.o] + +-prog1 + | +-prog1.o + | | +-prog1.c + | | +-inc.h + | +-[libfoo.a] + +-prog1.c + +-[prog1.o] + +-prog2 + | +-prog2.o + | | +-prog2.c + | | +-inc.h + | +-[libfoo.a] + +-prog2.c + +-[prog2.o] + + + + + Like the status keyword, + the prune argument by itself + is equivalent to --tree=all,prune. + + + +
+ +
+ + How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option + + + + Sometimes it's useful to look at the + pre-substitution string + that &SCons; uses to generate + the command lines it executes. + This can be done with the &debug-presub; option: + + + + + + + + + % scons -Q --debug=presub + Building prog.o with action: + $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES + cc -o prog.o -c -I. prog.c + Building prog with action: + $SMART_LINKCOM + cc -o prog prog.o + + +
+ +
+ + Where is &SCons; Searching for Libraries? the &debug-findlibs; Option + + + + To get some insight into what library names + &SCons; is searching for, + and in which directories it is searching, + Use the --debug=findlibs option. + Given the following input &SConstruct; file: + + + + + env = Environment(LIBPATH = ['libs1', 'libs2']) + env.Program('prog.c', LIBS=['foo', 'bar']) + + + + + And the libraries libfoo.a + and libbar.a + in libs1 and libs2, + respectively, + use of the --debug=findlibs option yields: + + + + + % scons -Q --debug=findlibs + findlibs: looking for 'libfoo.a' in 'libs1' ... + findlibs: ... FOUND 'libfoo.a' in 'libs1' + findlibs: looking for 'libfoo.so' in 'libs1' ... + findlibs: looking for 'libfoo.so' in 'libs2' ... + findlibs: looking for 'libbar.a' in 'libs1' ... + findlibs: looking for 'libbar.a' in 'libs2' ... + findlibs: ... FOUND 'libbar.a' in 'libs2' + findlibs: looking for 'libbar.so' in 'libs1' ... + findlibs: looking for 'libbar.so' in 'libs2' ... + cc -o prog.o -c prog.c + cc -o prog prog.o -Llibs1 -Llibs2 -lfoo -lbar + + +
+ + + +
+ + Where is &SCons; Blowing Up? the &debug-stacktrace; Option + + + + In general, &SCons; tries to keep its error + messages short and informative. + That means we usually try to avoid showing + the stack traces that are familiar + to experienced Python programmers, + since they usually contain much more + information than is useful to most people. + + + + + + For example, the following &SConstruct; file: + + + + + Program('prog.c') + + + + + Generates the following error if the + prog.c file + does not exist: + + + + + % scons -Q + scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. + + + + + In this case, + the error is pretty obvious. + But if it weren't, + and you wanted to try to get more information + about the error, + the &debug-stacktrace; option + would show you exactly where in the &SCons; source code + the problem occurs: + + + + + % scons -Q --debug=stacktrace + scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. + scons: internal stack trace: + File "bootstrap/src/engine/SCons/Job.py", line 197, in start + File "bootstrap/src/engine/SCons/Script/Main.py", line 167, in prepare + File "bootstrap/src/engine/SCons/Taskmaster.py", line 190, in prepare + File "bootstrap/src/engine/SCons/Executor.py", line 397, in prepare + + + + + Of course, if you do need to dive into the &SCons; source code, + we'd like to know if, or how, + the error messages or troubleshooting options + could have been improved to avoid that. + Not everyone has the necessary time or + Python skill to dive into the source code, + and we'd like to improve &SCons; + for those people as well... + + + +
+ +
+ + How is &SCons; Making Its Decisions? the &taskmastertrace; Option + + + + The internal &SCons; subsystem that handles walking + the dependency graph + and controls the decision-making about what to rebuild + is the Taskmaster. + &SCons; supports a --taskmastertrace + option that tells the Taskmaster to print + information about the children (dependencies) + of the various Nodes on its walk down the graph, + which specific dependent Nodes are being evaluated, + and in what order. + + + + + + The --taskmastertrace option + takes as an argument the name of a file in + which to put the trace output, + with - (a single hyphen) + indicating that the trace messages + should be printed to the standard output: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog.c') + + + + % scons -Q --taskmastertrace=- prog + + Taskmaster: Looking for a node to evaluate + Taskmaster: Considering node <no_state 0 'prog'> and its children: + Taskmaster: <no_state 0 'prog.o'> + Taskmaster: adjusted ref count: <pending 1 'prog'>, child 'prog.o' + Taskmaster: Considering node <no_state 0 'prog.o'> and its children: + Taskmaster: <no_state 0 'prog.c'> + Taskmaster: <no_state 0 'inc.h'> + Taskmaster: adjusted ref count: <pending 1 'prog.o'>, child 'prog.c' + Taskmaster: adjusted ref count: <pending 2 'prog.o'>, child 'inc.h' + Taskmaster: Considering node <no_state 0 'prog.c'> and its children: + Taskmaster: Evaluating <pending 0 'prog.c'> + + Task.make_ready_current(): node <pending 0 'prog.c'> + Task.prepare(): node <up_to_date 0 'prog.c'> + Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> + Task.postprocess(): node <up_to_date 0 'prog.c'> + Task.postprocess(): removing <up_to_date 0 'prog.c'> + Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> + + Taskmaster: Looking for a node to evaluate + Taskmaster: Considering node <no_state 0 'inc.h'> and its children: + Taskmaster: Evaluating <pending 0 'inc.h'> + + Task.make_ready_current(): node <pending 0 'inc.h'> + Task.prepare(): node <up_to_date 0 'inc.h'> + Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> + Task.postprocess(): node <up_to_date 0 'inc.h'> + Task.postprocess(): removing <up_to_date 0 'inc.h'> + Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> + + Taskmaster: Looking for a node to evaluate + Taskmaster: Considering node <pending 0 'prog.o'> and its children: + Taskmaster: <up_to_date 0 'prog.c'> + Taskmaster: <up_to_date 0 'inc.h'> + Taskmaster: Evaluating <pending 0 'prog.o'> + + Task.make_ready_current(): node <pending 0 'prog.o'> + Task.prepare(): node <executing 0 'prog.o'> + Task.execute(): node <executing 0 'prog.o'> + cc -o prog.o -c -I. prog.c + Task.executed_with_callbacks(): node <executing 0 'prog.o'> + Task.postprocess(): node <executed 0 'prog.o'> + Task.postprocess(): removing <executed 0 'prog.o'> + Task.postprocess(): adjusted parent ref count <pending 0 'prog'> + + Taskmaster: Looking for a node to evaluate + Taskmaster: Considering node <pending 0 'prog'> and its children: + Taskmaster: <executed 0 'prog.o'> + Taskmaster: Evaluating <pending 0 'prog'> + + Task.make_ready_current(): node <pending 0 'prog'> + Task.prepare(): node <executing 0 'prog'> + Task.execute(): node <executing 0 'prog'> + cc -o prog prog.o + Task.executed_with_callbacks(): node <executing 0 'prog'> + Task.postprocess(): node <executed 0 'prog'> + + Taskmaster: Looking for a node to evaluate + Taskmaster: No candidate anymore. + + + + + The --taskmastertrace option + doesn't provide information about the actual + calculations involved in deciding if a file is up-to-date, + but it does show all of the dependencies + it knows about for each Node, + and the order in which those dependencies are evaluated. + This can be useful as an alternate way to determine + whether or not your &SCons; configuration, + or the implicit dependency scan, + has actually identified all the correct dependencies + you want it to. + + + +
+ + + + diff --git a/doc/user/variables.in b/doc/user/variables.in new file mode 100644 index 0000000..85fb9cd --- /dev/null +++ b/doc/user/variables.in @@ -0,0 +1,56 @@ + + + + +This appendix contains descriptions of all of the +construction variables that are potentially +available "out of the box" in this version of SCons. +Whether or not setting a construction variable +in a construction environment +will actually have an effect depends on +whether any of the Tools and/or Builders +that use the variable have been +included in the construction environment. + + + + + +In this appendix, we have +appended the initial $ +(dollar sign) to the beginning of each +variable name when it appears in the text, +but left off the dollar sign +in the left-hand column +where the name appears for each entry. + + + + + +&variables-gen; + + diff --git a/doc/user/variables.xml b/doc/user/variables.xml new file mode 100644 index 0000000..85fb9cd --- /dev/null +++ b/doc/user/variables.xml @@ -0,0 +1,56 @@ + + + + +This appendix contains descriptions of all of the +construction variables that are potentially +available "out of the box" in this version of SCons. +Whether or not setting a construction variable +in a construction environment +will actually have an effect depends on +whether any of the Tools and/or Builders +that use the variable have been +included in the construction environment. + + + + + +In this appendix, we have +appended the initial $ +(dollar sign) to the beginning of each +variable name when it appears in the text, +but left off the dollar sign +in the left-hand column +where the name appears for each entry. + + + + + +&variables-gen; + + diff --git a/doc/user/variants.in b/doc/user/variants.in new file mode 100644 index 0000000..696e174 --- /dev/null +++ b/doc/user/variants.in @@ -0,0 +1,151 @@ + + + + + + + The &variant_dir; keyword argument of + the &SConscript; function provides everything + we need to show how easy it is to create + variant builds using &SCons;. + Suppose, for example, that we want to + build a program for both Windows and Linux platforms, + but that we want to build it in a shared directory + with separate side-by-side build directories + for the Windows and Linux versions of the program. + + + + + + platform = ARGUMENTS.get('OS', Platform()) + + include = "#export/$PLATFORM/include" + lib = "#export/$PLATFORM/lib" + bin = "#export/$PLATFORM/bin" + + env = Environment(PLATFORM = platform, + BINDIR = bin, + INCDIR = include, + LIBDIR = lib, + CPPPATH = [include], + LIBPATH = [lib], + LIBS = 'world') + + Export('env') + + env.SConscript('src/SConscript', variant_dir='build/$PLATFORM') + + + + + + Import('env') + SConscript('hello/SConscript') + SConscript('world/SConscript') + + + Import('env') + hello = env.Program('hello.c') + env.Install('$BINDIR', hello) + + + #include "world.h" + int main(int argc, char *argv[]) { printf "hello.c\n"; world(); } + + + Import('env') + world = env.Library('world.c') + env.Install('$LIBDIR', world) + env.Install('$INCDIR', 'world.h') + + + #define STRING "world.h" + extern int world(); + + + int world() { printf "world.c\n"; } + + + + + + This SConstruct file, + when run on a Linux system, yields: + + + + + scons -Q OS=linux + + + + + The same SConstruct file on Windows would build: + + + + + scons -Q OS=windows + + + diff --git a/doc/user/variants.xml b/doc/user/variants.xml new file mode 100644 index 0000000..016202a --- /dev/null +++ b/doc/user/variants.xml @@ -0,0 +1,146 @@ + + + + + + + The &variant_dir; keyword argument of + the &SConscript; function provides everything + we need to show how easy it is to create + variant builds using &SCons;. + Suppose, for example, that we want to + build a program for both Windows and Linux platforms, + but that we want to build it in a shared directory + with separate side-by-side build directories + for the Windows and Linux versions of the program. + + + + + platform = ARGUMENTS.get('OS', Platform()) + + include = "#export/$PLATFORM/include" + lib = "#export/$PLATFORM/lib" + bin = "#export/$PLATFORM/bin" + + env = Environment(PLATFORM = platform, + BINDIR = bin, + INCDIR = include, + LIBDIR = lib, + CPPPATH = [include], + LIBPATH = [lib], + LIBS = 'world') + + Export('env') + + env.SConscript('src/SConscript', variant_dir='build/$PLATFORM') + + + + + This SConstruct file, + when run on a Linux system, yields: + + + + + % scons -Q OS=linux + Install file: "build/linux/world/world.h" as "export/linux/include/world.h" + cc -o build/linux/hello/hello.o -c -Iexport/linux/include build/linux/hello/hello.c + cc -o build/linux/world/world.o -c -Iexport/linux/include build/linux/world/world.c + ar rc build/linux/world/libworld.a build/linux/world/world.o + ranlib build/linux/world/libworld.a + Install file: "build/linux/world/libworld.a" as "export/linux/lib/libworld.a" + cc -o build/linux/hello/hello build/linux/hello/hello.o -Lexport/linux/lib -lworld + Install file: "build/linux/hello/hello" as "export/linux/bin/hello" + + + + + The same SConstruct file on Windows would build: + + + + + C:\>scons -Q OS=windows + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + + scons: warning: No installed VCs + File "<stdin>", line 67, in __call__ + + scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly + File "<stdin>", line 67, in __call__ + Install file: "build/windows/world/world.h" as "export/windows/include/world.h" + cl /Fobuild\windows\hello\hello.obj /c build\windows\hello\hello.c /nologo /Iexport\windows\include + cl /Fobuild\windows\world\world.obj /c build\windows\world\world.c /nologo /Iexport\windows\include + lib /nologo /OUT:build\windows\world\world.lib build\windows\world\world.obj + Install file: "build/windows/world/world.lib" as "export/windows/lib/world.lib" + link /nologo /OUT:build\windows\hello\hello.exe /LIBPATH:export\windows\lib world.lib build\windows\hello\hello.obj + Install file: "build/windows/hello/hello.exe" as "export/windows/bin/hello.exe" + + + -- cgit v1.2.3