summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Falavigna <dktrkranz@debian.org>2010-01-02 20:56:27 +0100
committerLuca Falavigna <dktrkranz@debian.org>2010-01-02 20:56:27 +0100
commit72c578fd4b0b4a5a43e18594339ac4ff26c376dc (patch)
treecadaf3abe37a1066ceae933bc8fe7b75c85f56d2
parent548ed1064f327bccc6e538806740d41ea2d928a1 (diff)
Imported Upstream version 1.2.0.d20091224upstream/1.2.0.d20091224
-rw-r--r--LICENSE20
-rw-r--r--SConstruct1389
-rw-r--r--bin/Command.py143
-rw-r--r--bin/SConsDoc.py336
-rwxr-xr-xbin/ae-cvs-ci204
-rwxr-xr-xbin/ae-svn-ci240
-rw-r--r--bin/ae2cvs579
-rw-r--r--bin/calibrate.py81
-rw-r--r--bin/caller-tree.py96
-rw-r--r--bin/docdiff16
-rw-r--r--bin/docrun16
-rw-r--r--bin/docupdate16
-rw-r--r--bin/files106
-rw-r--r--bin/import-test.py104
-rw-r--r--bin/install_python.py141
-rw-r--r--bin/install_scons.py216
-rw-r--r--bin/linecount.py123
-rw-r--r--bin/makedocs18
-rw-r--r--bin/memlogs.py49
-rw-r--r--bin/memoicmp.py78
-rw-r--r--bin/objcounts.py109
-rw-r--r--bin/restore.sh90
-rw-r--r--bin/rsync-sourceforge32
-rw-r--r--bin/scons-cdist272
-rw-r--r--bin/scons-diff.py195
-rw-r--r--bin/scons-proc.py283
-rwxr-xr-xbin/scons-review.sh24
-rw-r--r--bin/scons-test.py247
-rw-r--r--bin/scons-unzip.py76
-rw-r--r--bin/scons_dev_master.py215
-rw-r--r--bin/sconsexamples.py512
-rw-r--r--bin/sconsoutput.py827
-rw-r--r--bin/sfsum148
-rwxr-xr-xbin/svn-bisect.py72
-rw-r--r--bin/time-scons.py355
-rw-r--r--bin/timebuild65
-rw-r--r--bin/xml_export225
-rw-r--r--bin/xml_export-LICENSE52
-rw-r--r--bin/xml_export-README56
-rwxr-xr-xbin/xmlagenda.py93
-rw-r--r--config299
-rw-r--r--doc/MANIFEST1
-rw-r--r--doc/SConscript530
-rw-r--r--doc/design/MANIFEST14
-rw-r--r--doc/design/acks.xml179
-rw-r--r--doc/design/bground.xml86
-rw-r--r--doc/design/copyright.xml39
-rw-r--r--doc/design/engine.fig179
-rw-r--r--doc/design/engine.jpgbin0 -> 41228 bytes
-rw-r--r--doc/design/engine.xml1964
-rw-r--r--doc/design/goals.xml216
-rw-r--r--doc/design/install.xml28
-rw-r--r--doc/design/intro.xml111
-rw-r--r--doc/design/issues.xml195
-rw-r--r--doc/design/main.xml158
-rw-r--r--doc/design/native.xml364
-rw-r--r--doc/design/overview.xml498
-rw-r--r--doc/design/scons.mod429
-rw-r--r--doc/developer/MANIFEST9
-rw-r--r--doc/developer/architecture.xml40
-rw-r--r--doc/developer/branches.xml40
-rw-r--r--doc/developer/copyright.xml32
-rw-r--r--doc/developer/cycle.xml40
-rw-r--r--doc/developer/main.xml110
-rw-r--r--doc/developer/packaging.xml40
-rw-r--r--doc/developer/preface.xml175
-rw-r--r--doc/developer/sourcetree.xml40
-rw-r--r--doc/developer/testing.xml40
-rw-r--r--doc/man/MANIFEST2
-rw-r--r--doc/man/scons-time.11017
-rw-r--r--doc/man/scons.110149
-rw-r--r--doc/man/sconsign.1208
-rw-r--r--doc/python10/MANIFEST16
-rw-r--r--doc/python10/abstract.xml32
-rw-r--r--doc/python10/acks.xml27
-rw-r--r--doc/python10/arch.eps134
-rw-r--r--doc/python10/arch.fig35
-rw-r--r--doc/python10/arch.jpgbin0 -> 22004 bytes
-rw-r--r--doc/python10/builder.eps325
-rw-r--r--doc/python10/builder.fig128
-rw-r--r--doc/python10/builder.jpgbin0 -> 53107 bytes
-rw-r--r--doc/python10/copyright.xml32
-rw-r--r--doc/python10/design.xml898
-rw-r--r--doc/python10/future.xml170
-rw-r--r--doc/python10/install.xml179
-rw-r--r--doc/python10/intro.xml212
-rw-r--r--doc/python10/job-task.eps238
-rw-r--r--doc/python10/job-task.fig90
-rw-r--r--doc/python10/job-task.jpgbin0 -> 25895 bytes
-rw-r--r--doc/python10/main.xml221
-rw-r--r--doc/python10/node.eps351
-rw-r--r--doc/python10/node.fig165
-rw-r--r--doc/python10/node.jpgbin0 -> 61105 bytes
-rw-r--r--doc/python10/process.xml353
-rw-r--r--doc/python10/scanner.eps168
-rw-r--r--doc/python10/scanner.fig57
-rw-r--r--doc/python10/scanner.jpgbin0 -> 26788 bytes
-rw-r--r--doc/python10/scons.mod428
-rw-r--r--doc/python10/sig.eps147
-rw-r--r--doc/python10/sig.fig44
-rw-r--r--doc/python10/sig.jpgbin0 -> 13913 bytes
-rw-r--r--doc/reference/Alias.xml41
-rw-r--r--doc/reference/CFile.xml41
-rw-r--r--doc/reference/CXXFile.xml41
-rw-r--r--doc/reference/Command.xml73
-rw-r--r--doc/reference/Install.xml41
-rw-r--r--doc/reference/InstallAs.xml41
-rw-r--r--doc/reference/Library.xml152
-rw-r--r--doc/reference/MANIFEST21
-rw-r--r--doc/reference/Object.xml71
-rw-r--r--doc/reference/PCH.xml41
-rw-r--r--doc/reference/PDF.xml41
-rw-r--r--doc/reference/PostScript.xml41
-rw-r--r--doc/reference/Program.xml77
-rw-r--r--doc/reference/RES.xml41
-rw-r--r--doc/reference/SharedLibrary.xml41
-rw-r--r--doc/reference/SharedObject.xml41
-rw-r--r--doc/reference/StaticLibrary.xml41
-rw-r--r--doc/reference/StaticObject.xml41
-rw-r--r--doc/reference/copyright.xml32
-rw-r--r--doc/reference/errors.xml41
-rw-r--r--doc/reference/main.xml207
-rw-r--r--doc/reference/preface.xml85
-rw-r--r--doc/scons.mod521
-rw-r--r--doc/user/MANIFEST50
-rw-r--r--doc/user/README21
-rw-r--r--doc/user/SCons-win32-install-1.jpgbin0 -> 28857 bytes
-rw-r--r--doc/user/SCons-win32-install-2.jpgbin0 -> 27131 bytes
-rw-r--r--doc/user/SCons-win32-install-3.jpgbin0 -> 25137 bytes
-rw-r--r--doc/user/SCons-win32-install-4.jpgbin0 -> 19549 bytes
-rw-r--r--doc/user/actions.in404
-rw-r--r--doc/user/actions.xml404
-rw-r--r--doc/user/add-method.in127
-rw-r--r--doc/user/add-method.xml135
-rw-r--r--doc/user/alias.in102
-rw-r--r--doc/user/alias.xml112
-rw-r--r--doc/user/ant.in52
-rw-r--r--doc/user/ant.xml52
-rw-r--r--doc/user/build-install.in719
-rw-r--r--doc/user/build-install.xml719
-rw-r--r--doc/user/builders-built-in.in963
-rw-r--r--doc/user/builders-built-in.xml949
-rw-r--r--doc/user/builders-commands.in156
-rw-r--r--doc/user/builders-commands.xml146
-rw-r--r--doc/user/builders-writing.in1087
-rw-r--r--doc/user/builders-writing.xml953
-rw-r--r--doc/user/builders.in57
-rw-r--r--doc/user/builders.xml57
-rw-r--r--doc/user/caching.in502
-rw-r--r--doc/user/caching.xml506
-rw-r--r--doc/user/command-line.in2327
-rw-r--r--doc/user/command-line.xml2243
-rw-r--r--doc/user/cons.pl720
-rw-r--r--doc/user/copyright.in32
-rw-r--r--doc/user/copyright.xml32
-rw-r--r--doc/user/depends.in1816
-rw-r--r--doc/user/depends.xml1776
-rw-r--r--doc/user/environments.in1678
-rw-r--r--doc/user/environments.xml1684
-rw-r--r--doc/user/errors.in41
-rw-r--r--doc/user/errors.xml41
-rw-r--r--doc/user/example.in41
-rw-r--r--doc/user/example.xml41
-rw-r--r--doc/user/factories.in507
-rw-r--r--doc/user/factories.xml466
-rw-r--r--doc/user/file-removal.in223
-rw-r--r--doc/user/file-removal.xml202
-rw-r--r--doc/user/hierarchy.in794
-rw-r--r--doc/user/hierarchy.xml746
-rw-r--r--doc/user/install.in247
-rw-r--r--doc/user/install.xml237
-rw-r--r--doc/user/java.in657
-rw-r--r--doc/user/java.xml433
-rw-r--r--doc/user/less-simple.in623
-rw-r--r--doc/user/less-simple.xml608
-rw-r--r--doc/user/libraries.in445
-rw-r--r--doc/user/libraries.xml451
-rw-r--r--doc/user/main.in385
-rw-r--r--doc/user/main.xml385
-rw-r--r--doc/user/make.in121
-rw-r--r--doc/user/make.xml121
-rw-r--r--doc/user/mergeflags.in137
-rw-r--r--doc/user/mergeflags.xml138
-rw-r--r--doc/user/misc.in606
-rw-r--r--doc/user/misc.xml565
-rw-r--r--doc/user/nodes.in386
-rw-r--r--doc/user/nodes.xml401
-rw-r--r--doc/user/output.in681
-rw-r--r--doc/user/output.xml703
-rw-r--r--doc/user/parseconfig.in114
-rw-r--r--doc/user/parseconfig.xml132
-rw-r--r--doc/user/parseflags.in184
-rw-r--r--doc/user/parseflags.xml199
-rw-r--r--doc/user/preface.in426
-rw-r--r--doc/user/preface.xml426
-rw-r--r--doc/user/python.in154
-rw-r--r--doc/user/python.xml154
-rw-r--r--doc/user/repositories.in641
-rw-r--r--doc/user/repositories.xml595
-rw-r--r--doc/user/run.in375
-rw-r--r--doc/user/run.xml375
-rw-r--r--doc/user/scanners.in331
-rw-r--r--doc/user/scanners.xml317
-rw-r--r--doc/user/sconf.in486
-rw-r--r--doc/user/sconf.xml486
-rw-r--r--doc/user/separate.in540
-rw-r--r--doc/user/separate.xml520
-rw-r--r--doc/user/sideeffect.in216
-rw-r--r--doc/user/sideeffect.xml211
-rw-r--r--doc/user/simple.in517
-rw-r--r--doc/user/simple.xml587
-rw-r--r--doc/user/sourcecode.in162
-rw-r--r--doc/user/sourcecode.xml157
-rw-r--r--doc/user/tasks.in108
-rw-r--r--doc/user/tasks.xml108
-rw-r--r--doc/user/tools.in38
-rw-r--r--doc/user/tools.xml38
-rw-r--r--doc/user/troubleshoot.in875
-rw-r--r--doc/user/troubleshoot.xml1313
-rw-r--r--doc/user/variables.in56
-rw-r--r--doc/user/variables.xml56
-rw-r--r--doc/user/variants.in151
-rw-r--r--doc/user/variants.xml146
-rw-r--r--src/CHANGES.txt5176
-rw-r--r--src/LICENSE.txt20
-rw-r--r--src/MANIFEST.in0
-rw-r--r--src/README.txt273
-rw-r--r--src/RELEASE.txt1016
-rw-r--r--src/engine/MANIFEST-xml.in99
-rw-r--r--src/engine/MANIFEST.in187
-rw-r--r--src/engine/README.txt167
-rw-r--r--src/engine/SCons/Action.py1240
-rw-r--r--src/engine/SCons/Action.xml107
-rw-r--r--src/engine/SCons/ActionTests.py2013
-rw-r--r--src/engine/SCons/Builder.py868
-rw-r--r--src/engine/SCons/BuilderTests.py1650
-rw-r--r--src/engine/SCons/CacheDir.py217
-rw-r--r--src/engine/SCons/CacheDirTests.py297
-rw-r--r--src/engine/SCons/Conftest.py794
-rw-r--r--src/engine/SCons/Debug.py237
-rw-r--r--src/engine/SCons/Defaults.py485
-rw-r--r--src/engine/SCons/Defaults.xml472
-rw-r--r--src/engine/SCons/DefaultsTests.py94
-rw-r--r--src/engine/SCons/Environment.py2327
-rw-r--r--src/engine/SCons/Environment.xml187
-rw-r--r--src/engine/SCons/EnvironmentTests.py3987
-rw-r--r--src/engine/SCons/Errors.py207
-rw-r--r--src/engine/SCons/ErrorsTests.py109
-rw-r--r--src/engine/SCons/Executor.py636
-rw-r--r--src/engine/SCons/ExecutorTests.py466
-rw-r--r--src/engine/SCons/Job.py435
-rw-r--r--src/engine/SCons/JobTests.py539
-rw-r--r--src/engine/SCons/Memoize.py292
-rw-r--r--src/engine/SCons/MemoizeTests.py198
-rw-r--r--src/engine/SCons/Node/Alias.py153
-rw-r--r--src/engine/SCons/Node/AliasTests.py130
-rw-r--r--src/engine/SCons/Node/FS.py3220
-rw-r--r--src/engine/SCons/Node/FSTests.py3520
-rw-r--r--src/engine/SCons/Node/NodeTests.py1317
-rw-r--r--src/engine/SCons/Node/Python.py128
-rw-r--r--src/engine/SCons/Node/PythonTests.py130
-rw-r--r--src/engine/SCons/Node/__init__.py1341
-rw-r--r--src/engine/SCons/Options/BoolOption.py50
-rw-r--r--src/engine/SCons/Options/EnumOption.py50
-rw-r--r--src/engine/SCons/Options/ListOption.py50
-rw-r--r--src/engine/SCons/Options/PackageOption.py50
-rw-r--r--src/engine/SCons/Options/PathOption.py76
-rw-r--r--src/engine/SCons/Options/__init__.py74
-rw-r--r--src/engine/SCons/PathList.py232
-rw-r--r--src/engine/SCons/PathListTests.py170
-rw-r--r--src/engine/SCons/Platform/PlatformTests.py126
-rw-r--r--src/engine/SCons/Platform/__init__.py236
-rw-r--r--src/engine/SCons/Platform/__init__.xml183
-rw-r--r--src/engine/SCons/Platform/aix.py70
-rw-r--r--src/engine/SCons/Platform/cygwin.py55
-rw-r--r--src/engine/SCons/Platform/darwin.py46
-rw-r--r--src/engine/SCons/Platform/hpux.py46
-rw-r--r--src/engine/SCons/Platform/irix.py44
-rw-r--r--src/engine/SCons/Platform/os2.py58
-rw-r--r--src/engine/SCons/Platform/posix.py264
-rw-r--r--src/engine/SCons/Platform/posix.xml51
-rw-r--r--src/engine/SCons/Platform/sunos.py50
-rw-r--r--src/engine/SCons/Platform/sunos.xml30
-rw-r--r--src/engine/SCons/Platform/win32.py386
-rw-r--r--src/engine/SCons/Platform/win32.xml14
-rw-r--r--src/engine/SCons/SConf.py1038
-rw-r--r--src/engine/SCons/SConfTests.py759
-rw-r--r--src/engine/SCons/SConsign.py381
-rw-r--r--src/engine/SCons/SConsignTests.py397
-rw-r--r--src/engine/SCons/Scanner/C.py132
-rw-r--r--src/engine/SCons/Scanner/CTests.py468
-rw-r--r--src/engine/SCons/Scanner/D.py74
-rw-r--r--src/engine/SCons/Scanner/Dir.py111
-rw-r--r--src/engine/SCons/Scanner/DirTests.py140
-rw-r--r--src/engine/SCons/Scanner/Fortran.py320
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py543
-rw-r--r--src/engine/SCons/Scanner/IDL.py48
-rw-r--r--src/engine/SCons/Scanner/IDLTests.py453
-rw-r--r--src/engine/SCons/Scanner/LaTeX.py345
-rw-r--r--src/engine/SCons/Scanner/LaTeXTests.py162
-rw-r--r--src/engine/SCons/Scanner/Prog.py103
-rw-r--r--src/engine/SCons/Scanner/ProgTests.py262
-rw-r--r--src/engine/SCons/Scanner/RC.py55
-rw-r--r--src/engine/SCons/Scanner/RCTests.py168
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py611
-rw-r--r--src/engine/SCons/Scanner/__init__.py415
-rw-r--r--src/engine/SCons/Script/Interactive.py386
-rw-r--r--src/engine/SCons/Script/Main.py1360
-rw-r--r--src/engine/SCons/Script/MainTests.py53
-rw-r--r--src/engine/SCons/Script/SConsOptions.py944
-rw-r--r--src/engine/SCons/Script/SConscript.py642
-rw-r--r--src/engine/SCons/Script/SConscriptTests.py34
-rw-r--r--src/engine/SCons/Script/__init__.py414
-rw-r--r--src/engine/SCons/Sig.py63
-rw-r--r--src/engine/SCons/Subst.py911
-rw-r--r--src/engine/SCons/SubstTests.py1242
-rw-r--r--src/engine/SCons/Taskmaster.py1030
-rw-r--r--src/engine/SCons/TaskmasterTests.py1138
-rw-r--r--src/engine/SCons/Tool/386asm.py61
-rw-r--r--src/engine/SCons/Tool/386asm.xml25
-rw-r--r--src/engine/SCons/Tool/BitKeeper.py65
-rw-r--r--src/engine/SCons/Tool/BitKeeper.xml58
-rw-r--r--src/engine/SCons/Tool/CVS.py73
-rw-r--r--src/engine/SCons/Tool/CVS.xml66
-rw-r--r--src/engine/SCons/Tool/FortranCommon.py247
-rw-r--r--src/engine/SCons/Tool/JavaCommon.py324
-rw-r--r--src/engine/SCons/Tool/JavaCommonTests.py580
-rw-r--r--src/engine/SCons/Tool/MSCommon/__init__.py56
-rw-r--r--src/engine/SCons/Tool/MSCommon/arch.py61
-rw-r--r--src/engine/SCons/Tool/MSCommon/common.py195
-rw-r--r--src/engine/SCons/Tool/MSCommon/netframework.py84
-rw-r--r--src/engine/SCons/Tool/MSCommon/sdk.py321
-rw-r--r--src/engine/SCons/Tool/MSCommon/vc.py367
-rw-r--r--src/engine/SCons/Tool/MSCommon/vc.py.bak394
-rw-r--r--src/engine/SCons/Tool/MSCommon/vs.py497
-rw-r--r--src/engine/SCons/Tool/Perforce.py104
-rw-r--r--src/engine/SCons/Tool/Perforce.xml47
-rw-r--r--src/engine/SCons/Tool/PharLapCommon.py138
-rw-r--r--src/engine/SCons/Tool/PharLapCommonTests.py68
-rw-r--r--src/engine/SCons/Tool/RCS.py64
-rw-r--r--src/engine/SCons/Tool/RCS.xml61
-rw-r--r--src/engine/SCons/Tool/SCCS.py64
-rw-r--r--src/engine/SCons/Tool/SCCS.xml58
-rw-r--r--src/engine/SCons/Tool/Subversion.py71
-rw-r--r--src/engine/SCons/Tool/Subversion.xml47
-rw-r--r--src/engine/SCons/Tool/ToolTests.py84
-rw-r--r--src/engine/SCons/Tool/__init__.py675
-rw-r--r--src/engine/SCons/Tool/__init__.xml367
-rw-r--r--src/engine/SCons/Tool/aixc++.py82
-rw-r--r--src/engine/SCons/Tool/aixc++.xml19
-rw-r--r--src/engine/SCons/Tool/aixcc.py74
-rw-r--r--src/engine/SCons/Tool/aixcc.xml18
-rw-r--r--src/engine/SCons/Tool/aixf77.py80
-rw-r--r--src/engine/SCons/Tool/aixf77.xml17
-rw-r--r--src/engine/SCons/Tool/aixlink.py76
-rw-r--r--src/engine/SCons/Tool/aixlink.xml19
-rw-r--r--src/engine/SCons/Tool/applelink.py71
-rw-r--r--src/engine/SCons/Tool/applelink.xml114
-rw-r--r--src/engine/SCons/Tool/ar.py63
-rw-r--r--src/engine/SCons/Tool/ar.xml82
-rw-r--r--src/engine/SCons/Tool/as.py78
-rw-r--r--src/engine/SCons/Tool/as.xml88
-rw-r--r--src/engine/SCons/Tool/bcc32.py82
-rw-r--r--src/engine/SCons/Tool/bcc32.xml32
-rw-r--r--src/engine/SCons/Tool/c++.py99
-rw-r--r--src/engine/SCons/Tool/c++.xml102
-rw-r--r--src/engine/SCons/Tool/cc.py114
-rw-r--r--src/engine/SCons/Tool/cc.xml161
-rw-r--r--src/engine/SCons/Tool/cvf.py58
-rw-r--r--src/engine/SCons/Tool/cvf.xml30
-rw-r--r--src/engine/SCons/Tool/default.py50
-rw-r--r--src/engine/SCons/Tool/default.xml12
-rw-r--r--src/engine/SCons/Tool/dmd.py224
-rw-r--r--src/engine/SCons/Tool/dmd.xml53
-rw-r--r--src/engine/SCons/Tool/dvi.py64
-rw-r--r--src/engine/SCons/Tool/dvi.xml67
-rw-r--r--src/engine/SCons/Tool/dvipdf.py125
-rw-r--r--src/engine/SCons/Tool/dvipdf.xml51
-rw-r--r--src/engine/SCons/Tool/dvips.py94
-rw-r--r--src/engine/SCons/Tool/dvips.xml81
-rw-r--r--src/engine/SCons/Tool/f77.py62
-rw-r--r--src/engine/SCons/Tool/f77.xml265
-rw-r--r--src/engine/SCons/Tool/f90.py62
-rw-r--r--src/engine/SCons/Tool/f90.xml251
-rw-r--r--src/engine/SCons/Tool/f95.py63
-rw-r--r--src/engine/SCons/Tool/f95.xml252
-rw-r--r--src/engine/SCons/Tool/filesystem.py98
-rw-r--r--src/engine/SCons/Tool/fortran.py63
-rw-r--r--src/engine/SCons/Tool/fortran.xml302
-rw-r--r--src/engine/SCons/Tool/g++.py90
-rw-r--r--src/engine/SCons/Tool/g++.xml18
-rw-r--r--src/engine/SCons/Tool/g77.py73
-rw-r--r--src/engine/SCons/Tool/g77.xml13
-rw-r--r--src/engine/SCons/Tool/gas.py53
-rw-r--r--src/engine/SCons/Tool/gas.xml15
-rw-r--r--src/engine/SCons/Tool/gcc.py80
-rw-r--r--src/engine/SCons/Tool/gcc.xml16
-rw-r--r--src/engine/SCons/Tool/gfortran.py64
-rw-r--r--src/engine/SCons/Tool/gfortran.xml25
-rw-r--r--src/engine/SCons/Tool/gnulink.py63
-rw-r--r--src/engine/SCons/Tool/gnulink.xml16
-rw-r--r--src/engine/SCons/Tool/gs.py81
-rw-r--r--src/engine/SCons/Tool/gs.xml47
-rw-r--r--src/engine/SCons/Tool/hpc++.py85
-rw-r--r--src/engine/SCons/Tool/hpc++.xml11
-rw-r--r--src/engine/SCons/Tool/hpcc.py53
-rw-r--r--src/engine/SCons/Tool/hpcc.xml18
-rw-r--r--src/engine/SCons/Tool/hplink.py77
-rw-r--r--src/engine/SCons/Tool/hplink.xml16
-rw-r--r--src/engine/SCons/Tool/icc.py59
-rw-r--r--src/engine/SCons/Tool/icc.xml30
-rw-r--r--src/engine/SCons/Tool/icl.py52
-rw-r--r--src/engine/SCons/Tool/icl.xml12
-rw-r--r--src/engine/SCons/Tool/ifl.py72
-rw-r--r--src/engine/SCons/Tool/ifl.xml24
-rw-r--r--src/engine/SCons/Tool/ifort.py90
-rw-r--r--src/engine/SCons/Tool/ifort.xml26
-rw-r--r--src/engine/SCons/Tool/ilink.py59
-rw-r--r--src/engine/SCons/Tool/ilink.xml23
-rw-r--r--src/engine/SCons/Tool/ilink32.py60
-rw-r--r--src/engine/SCons/Tool/ilink32.xml23
-rw-r--r--src/engine/SCons/Tool/install.py229
-rw-r--r--src/engine/SCons/Tool/install.xml52
-rw-r--r--src/engine/SCons/Tool/intelc.py490
-rw-r--r--src/engine/SCons/Tool/intelc.xml33
-rw-r--r--src/engine/SCons/Tool/ipkg.py71
-rw-r--r--src/engine/SCons/Tool/jar.py110
-rw-r--r--src/engine/SCons/Tool/jar.xml110
-rw-r--r--src/engine/SCons/Tool/javac.py234
-rw-r--r--src/engine/SCons/Tool/javac.xml224
-rw-r--r--src/engine/SCons/Tool/javah.py138
-rw-r--r--src/engine/SCons/Tool/javah.xml100
-rw-r--r--src/engine/SCons/Tool/latex.py79
-rw-r--r--src/engine/SCons/Tool/latex.xml71
-rw-r--r--src/engine/SCons/Tool/lex.py99
-rw-r--r--src/engine/SCons/Tool/lex.xml50
-rw-r--r--src/engine/SCons/Tool/link.py121
-rw-r--r--src/engine/SCons/Tool/link.xml175
-rw-r--r--src/engine/SCons/Tool/linkloc.py112
-rw-r--r--src/engine/SCons/Tool/linkloc.xml31
-rw-r--r--src/engine/SCons/Tool/m4.py63
-rw-r--r--src/engine/SCons/Tool/m4.xml61
-rw-r--r--src/engine/SCons/Tool/masm.py77
-rw-r--r--src/engine/SCons/Tool/masm.xml26
-rw-r--r--src/engine/SCons/Tool/midl.py90
-rw-r--r--src/engine/SCons/Tool/midl.xml68
-rw-r--r--src/engine/SCons/Tool/mingw.py159
-rw-r--r--src/engine/SCons/Tool/mingw.xml38
-rw-r--r--src/engine/SCons/Tool/mslib.py64
-rw-r--r--src/engine/SCons/Tool/mslib.xml23
-rw-r--r--src/engine/SCons/Tool/mslink.py266
-rw-r--r--src/engine/SCons/Tool/mslink.xml237
-rw-r--r--src/engine/SCons/Tool/mssdk.py50
-rw-r--r--src/engine/SCons/Tool/mssdk.xml50
-rw-r--r--src/engine/SCons/Tool/msvc.py257
-rw-r--r--src/engine/SCons/Tool/msvc.xml316
-rw-r--r--src/engine/SCons/Tool/msvs.py1439
-rw-r--r--src/engine/SCons/Tool/msvs.xml640
-rw-r--r--src/engine/SCons/Tool/msvsTests.py757
-rw-r--r--src/engine/SCons/Tool/mwcc.py208
-rw-r--r--src/engine/SCons/Tool/mwcc.xml53
-rw-r--r--src/engine/SCons/Tool/mwld.py107
-rw-r--r--src/engine/SCons/Tool/mwld.xml27
-rw-r--r--src/engine/SCons/Tool/nasm.py72
-rw-r--r--src/engine/SCons/Tool/nasm.xml23
-rw-r--r--src/engine/SCons/Tool/packaging.xml73
-rw-r--r--src/engine/SCons/Tool/packaging/__init__.py314
-rw-r--r--src/engine/SCons/Tool/packaging/__init__.xml659
-rw-r--r--src/engine/SCons/Tool/packaging/ipk.py185
-rw-r--r--src/engine/SCons/Tool/packaging/msi.py526
-rw-r--r--src/engine/SCons/Tool/packaging/rpm.py367
-rw-r--r--src/engine/SCons/Tool/packaging/src_tarbz2.py43
-rw-r--r--src/engine/SCons/Tool/packaging/src_targz.py43
-rw-r--r--src/engine/SCons/Tool/packaging/src_zip.py43
-rw-r--r--src/engine/SCons/Tool/packaging/tarbz2.py44
-rw-r--r--src/engine/SCons/Tool/packaging/targz.py44
-rw-r--r--src/engine/SCons/Tool/packaging/zip.py44
-rw-r--r--src/engine/SCons/Tool/pdf.py78
-rw-r--r--src/engine/SCons/Tool/pdf.xml49
-rw-r--r--src/engine/SCons/Tool/pdflatex.py83
-rw-r--r--src/engine/SCons/Tool/pdflatex.xml49
-rw-r--r--src/engine/SCons/Tool/pdftex.py108
-rw-r--r--src/engine/SCons/Tool/pdftex.xml53
-rw-r--r--src/engine/SCons/Tool/qt.py336
-rw-r--r--src/engine/SCons/Tool/qt.xml314
-rw-r--r--src/engine/SCons/Tool/rmic.py121
-rw-r--r--src/engine/SCons/Tool/rmic.xml93
-rw-r--r--src/engine/SCons/Tool/rpcgen.py70
-rw-r--r--src/engine/SCons/Tool/rpcgen.xml137
-rw-r--r--src/engine/SCons/Tool/rpm.py132
-rw-r--r--src/engine/SCons/Tool/sgiar.py68
-rw-r--r--src/engine/SCons/Tool/sgiar.xml24
-rw-r--r--src/engine/SCons/Tool/sgic++.py58
-rw-r--r--src/engine/SCons/Tool/sgic++.xml18
-rw-r--r--src/engine/SCons/Tool/sgicc.py53
-rw-r--r--src/engine/SCons/Tool/sgicc.xml18
-rw-r--r--src/engine/SCons/Tool/sgilink.py63
-rw-r--r--src/engine/SCons/Tool/sgilink.xml17
-rw-r--r--src/engine/SCons/Tool/sunar.py67
-rw-r--r--src/engine/SCons/Tool/sunar.xml25
-rw-r--r--src/engine/SCons/Tool/sunc++.py142
-rw-r--r--src/engine/SCons/Tool/sunc++.xml19
-rw-r--r--src/engine/SCons/Tool/suncc.py58
-rw-r--r--src/engine/SCons/Tool/suncc.xml17
-rw-r--r--src/engine/SCons/Tool/sunf77.py63
-rw-r--r--src/engine/SCons/Tool/sunf77.xml19
-rw-r--r--src/engine/SCons/Tool/sunf90.py64
-rw-r--r--src/engine/SCons/Tool/sunf90.xml19
-rw-r--r--src/engine/SCons/Tool/sunf95.py64
-rw-r--r--src/engine/SCons/Tool/sunf95.xml19
-rw-r--r--src/engine/SCons/Tool/sunlink.py77
-rw-r--r--src/engine/SCons/Tool/sunlink.xml16
-rw-r--r--src/engine/SCons/Tool/swig.py186
-rw-r--r--src/engine/SCons/Tool/swig.xml212
-rw-r--r--src/engine/SCons/Tool/tar.py73
-rw-r--r--src/engine/SCons/Tool/tar.xml95
-rw-r--r--src/engine/SCons/Tool/tex.py792
-rw-r--r--src/engine/SCons/Tool/tex.xml126
-rw-r--r--src/engine/SCons/Tool/textfile.py175
-rw-r--r--src/engine/SCons/Tool/textfile.xml205
-rw-r--r--src/engine/SCons/Tool/tlib.py53
-rw-r--r--src/engine/SCons/Tool/tlib.xml22
-rw-r--r--src/engine/SCons/Tool/wix.py100
-rw-r--r--src/engine/SCons/Tool/yacc.py131
-rw-r--r--src/engine/SCons/Tool/yacc.xml115
-rw-r--r--src/engine/SCons/Tool/zip.py100
-rw-r--r--src/engine/SCons/Tool/zip.xml110
-rw-r--r--src/engine/SCons/Util.py1645
-rw-r--r--src/engine/SCons/UtilTests.py802
-rw-r--r--src/engine/SCons/Variables/BoolVariable.py91
-rw-r--r--src/engine/SCons/Variables/BoolVariableTests.py127
-rw-r--r--src/engine/SCons/Variables/EnumVariable.py107
-rw-r--r--src/engine/SCons/Variables/EnumVariableTests.py204
-rw-r--r--src/engine/SCons/Variables/ListVariable.py139
-rw-r--r--src/engine/SCons/Variables/ListVariableTests.py134
-rw-r--r--src/engine/SCons/Variables/PackageVariable.py109
-rw-r--r--src/engine/SCons/Variables/PackageVariableTests.py124
-rw-r--r--src/engine/SCons/Variables/PathVariable.py147
-rw-r--r--src/engine/SCons/Variables/PathVariableTests.py237
-rw-r--r--src/engine/SCons/Variables/VariablesTests.py663
-rw-r--r--src/engine/SCons/Variables/__init__.py317
-rw-r--r--src/engine/SCons/Warnings.py228
-rw-r--r--src/engine/SCons/WarningsTests.py135
-rw-r--r--src/engine/SCons/__init__.py49
-rw-r--r--src/engine/SCons/compat/__init__.py302
-rw-r--r--src/engine/SCons/compat/_scons_UserString.py98
-rw-r--r--src/engine/SCons/compat/_scons_hashlib.py91
-rw-r--r--src/engine/SCons/compat/_scons_itertools.py124
-rw-r--r--src/engine/SCons/compat/_scons_optparse.py1725
-rw-r--r--src/engine/SCons/compat/_scons_platform.py237
-rw-r--r--src/engine/SCons/compat/_scons_sets.py583
-rw-r--r--src/engine/SCons/compat/_scons_sets15.py176
-rw-r--r--src/engine/SCons/compat/_scons_shlex.py325
-rw-r--r--src/engine/SCons/compat/_scons_subprocess.py1296
-rw-r--r--src/engine/SCons/compat/_scons_textwrap.py382
-rw-r--r--src/engine/SCons/compat/builtins.py187
-rw-r--r--src/engine/SCons/cpp.py598
-rw-r--r--src/engine/SCons/cppTests.py715
-rw-r--r--src/engine/SCons/dblite.py248
-rw-r--r--src/engine/SCons/exitfuncs.py77
-rw-r--r--src/engine/setup.cfg2
-rw-r--r--src/engine/setup.py74
-rw-r--r--src/os_spawnv_fix.diff83
-rw-r--r--src/script/MANIFEST.in3
-rw-r--r--src/script/README.txt169
-rw-r--r--src/script/scons-post-install.py82
-rw-r--r--src/script/scons-time.py1529
-rw-r--r--src/script/scons.bat31
-rw-r--r--src/script/scons.py184
-rw-r--r--src/script/sconsign.py508
-rw-r--r--src/script/setup.cfg2
-rw-r--r--src/script/setup.py55
-rw-r--r--src/setup.cfg6
-rw-r--r--src/setup.py427
-rw-r--r--src/test_aegistests.py83
-rw-r--r--src/test_files.py106
-rw-r--r--src/test_interrupts.py146
-rw-r--r--src/test_pychecker.py161
-rw-r--r--src/test_setup.py335
-rw-r--r--src/test_strings.py280
580 files changed, 169411 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..67f1906
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 0000000..1a7f0ea
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,1389 @@
+#
+# SConstruct file to build scons packages during development.
+#
+# See the README file for an overview of how SCons is built and tested.
+#
+
+# When this gets changed, you must also change the copyright_years string
+# in QMTest/TestSCons.py so the test scripts look for the right string.
+copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009'
+
+# This gets inserted into the man pages to reflect the month of release.
+month_year = 'December 2009'
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import distutils.util
+import fnmatch
+import os
+import os.path
+import re
+import stat
+import string
+import sys
+import tempfile
+
+project = 'scons'
+default_version = '1.2.0'
+copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
+
+platform = distutils.util.get_platform()
+
+SConsignFile()
+
+#
+# An internal "whereis" routine to figure out if a given program
+# is available on this system.
+#
+def whereis(file):
+ exts = ['']
+ if platform == "win32":
+ exts += ['.exe']
+ for dir in string.split(os.environ['PATH'], os.pathsep):
+ f = os.path.join(dir, file)
+ for ext in exts:
+ f_ext = f + ext
+ if os.path.isfile(f_ext):
+ try:
+ st = os.stat(f_ext)
+ except:
+ continue
+ if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
+ return f_ext
+ return None
+
+#
+# We let the presence or absence of various utilities determine whether
+# or not we bother to build certain pieces of things. This should allow
+# people to still do SCons packaging work even if they don't have all
+# of the utilities installed (e.g. RPM).
+#
+dh_builddeb = whereis('dh_builddeb')
+fakeroot = whereis('fakeroot')
+gzip = whereis('gzip')
+rpmbuild = whereis('rpmbuild') or whereis('rpm')
+hg = os.path.exists('.hg') and whereis('hg')
+svn = os.path.exists('.svn') and whereis('svn')
+unzip = whereis('unzip')
+zip = whereis('zip')
+
+#
+# Now grab the information that we "build" into the files.
+#
+date = ARGUMENTS.get('DATE')
+if not date:
+ import time
+ date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
+
+developer = ARGUMENTS.get('DEVELOPER')
+if not developer:
+ for variable in ['USERNAME', 'LOGNAME', 'USER']:
+ developer = os.environ.get(variable)
+ if developer:
+ break
+
+build_system = ARGUMENTS.get('BUILD_SYSTEM')
+if not build_system:
+ import socket
+ build_system = string.split(socket.gethostname(), '.')[0]
+
+version = ARGUMENTS.get('VERSION', '')
+if not version:
+ version = default_version
+
+hg_status_lines = []
+svn_status_lines = []
+
+if hg:
+ cmd = "%s status --all 2> /dev/null" % hg
+ hg_status_lines = os.popen(cmd, "r").readlines()
+
+if svn:
+ cmd = "%s status --verbose 2> /dev/null" % svn
+ svn_status_lines = os.popen(cmd, "r").readlines()
+
+revision = ARGUMENTS.get('REVISION', '')
+def generate_build_id(revision):
+ return revision
+
+if not revision and hg:
+ hg_heads = os.popen("%s heads 2> /dev/null" % hg, "r").read()
+ cs = re.search('changeset:\s+(\S+)', hg_heads)
+ if cs:
+ revision = cs.group(1)
+ b = re.search('branch:\s+(\S+)', hg_heads)
+ if b:
+ revision = b.group(1) + ':' + revision
+ def generate_build_id(revision):
+ result = revision
+ if filter(lambda l: l[0] in 'AMR!', hg_status_lines):
+ result = result + '[MODIFIED]'
+ return result
+
+if not revision and svn:
+ svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read()
+ m = re.search('Revision: (\d+)', svn_info)
+ if m:
+ revision = m.group(1)
+ def generate_build_id(revision):
+ result = 'r' + revision
+ if filter(lambda l: l[0] in 'ACDMR', svn_status_lines):
+ result = result + '[MODIFIED]'
+ return result
+
+
+checkpoint = ARGUMENTS.get('CHECKPOINT', '')
+if checkpoint:
+ if checkpoint == 'd':
+ import time
+ checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
+ elif checkpoint == 'r':
+ checkpoint = 'r' + revision
+ version = version + '.' + checkpoint
+
+
+build_id = ARGUMENTS.get('BUILD_ID')
+if build_id is None:
+ if revision:
+ build_id = generate_build_id(revision)
+ else:
+ build_id = ''
+
+python_ver = sys.version[0:3]
+
+# Re-exporting LD_LIBRARY_PATH is necessary if the Python version was
+# built with the --enable-shared option.
+
+ENV = { 'PATH' : os.environ['PATH'] }
+for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
+ if os.environ.has_key(key):
+ ENV[key] = os.environ[key]
+
+build_dir = ARGUMENTS.get('BUILDDIR', 'build')
+if not os.path.isabs(build_dir):
+ build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
+
+command_line_variables = [
+ ("BUILDDIR=", "The directory in which to build the packages. " +
+ "The default is the './build' subdirectory."),
+
+ ("BUILD_ID=", "An identifier for the specific build." +
+ "The default is the Subversion revision number."),
+
+ ("BUILD_SYSTEM=", "The system on which the packages were built. " +
+ "The default is whatever hostname is returned " +
+ "by socket.gethostname()."),
+
+ ("CHECKPOINT=", "The specific checkpoint release being packaged, " +
+ "which will be appended to the VERSION string. " +
+ "A value of CHECKPOINT=d will generate a string " +
+ "of 'd' plus today's date in the format YYYMMDD. " +
+ "A value of CHECKPOINT=r will generate a " +
+ "string of 'r' plus the Subversion revision " +
+ "number. Any other CHECKPOINT= string will be " +
+ "used as is. There is no default value."),
+
+ ("DATE=", "The date string representing when the packaging " +
+ "build occurred. The default is the day and time " +
+ "the SConstruct file was invoked, in the format " +
+ "YYYY/MM/DD HH:MM:SS."),
+
+ ("DEVELOPER=", "The developer who created the packages. " +
+ "The default is the first set environment " +
+ "variable from the list $USERNAME, $LOGNAME, $USER."),
+
+ ("REVISION=", "The revision number of the source being built. " +
+ "The default is the Subversion revision returned " +
+ "'svn info', with an appended string of " +
+ "'[MODIFIED]' if there are any changes in the " +
+ "working copy."),
+
+ ("VERSION=", "The SCons version being packaged. The default " +
+ "is the hard-coded value '%s' " % default_version +
+ "from this SConstruct file."),
+]
+
+Default('.', build_dir)
+
+packaging_flavors = [
+ ('deb', "A .deb package. (This is currently not supported.)"),
+
+ ('rpm', "A RedHat Package Manager file."),
+
+ ('tar-gz', "The normal .tar.gz file for end-user installation."),
+
+ ('src-tar-gz', "A .tar.gz file containing all the source " +
+ "(including tests and documentation)."),
+
+ ('local-tar-gz', "A .tar.gz file for dropping into other software " +
+ "for local use."),
+
+ ('zip', "The normal .zip file for end-user installation."),
+
+ ('src-zip', "A .zip file containing all the source " +
+ "(including tests and documentation)."),
+
+ ('local-zip', "A .zip file for dropping into other software " +
+ "for local use."),
+]
+
+test_deb_dir = os.path.join(build_dir, "test-deb")
+test_rpm_dir = os.path.join(build_dir, "test-rpm")
+test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz")
+test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz")
+test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
+test_zip_dir = os.path.join(build_dir, "test-zip")
+test_src_zip_dir = os.path.join(build_dir, "test-src-zip")
+test_local_zip_dir = os.path.join(build_dir, "test-local-zip")
+
+unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz")
+unpack_zip_dir = os.path.join(build_dir, "unpack-zip")
+
+if platform == "win32":
+ tar_hflag = ''
+ python_project_subinst_dir = os.path.join("Lib", "site-packages", project)
+ project_script_subinst_dir = 'Scripts'
+else:
+ tar_hflag = 'h'
+ python_project_subinst_dir = os.path.join("lib", project)
+ project_script_subinst_dir = 'bin'
+
+
+
+import textwrap
+
+indent_fmt = ' %-26s '
+
+Help("""\
+The following aliases build packages of various types, and unpack the
+contents into build/test-$PACKAGE subdirectories, which can be used by the
+runtest.py -p option to run tests against what's been actually packaged:
+
+""")
+
+aliases = packaging_flavors + [('doc', 'The SCons documentation.')]
+aliases.sort()
+
+for alias, help_text in aliases:
+ tw = textwrap.TextWrapper(
+ width = 78,
+ initial_indent = indent_fmt % alias,
+ subsequent_indent = indent_fmt % '' + ' ',
+ )
+ Help(tw.fill(help_text) + '\n')
+
+Help("""
+The following command-line variables can be set:
+
+""")
+
+for variable, help_text in command_line_variables:
+ tw = textwrap.TextWrapper(
+ width = 78,
+ initial_indent = indent_fmt % variable,
+ subsequent_indent = indent_fmt % '' + ' ',
+ )
+ Help(tw.fill(help_text) + '\n')
+
+
+
+zcat = 'gzip -d -c'
+
+#
+# Figure out if we can handle .zip files.
+#
+zipit = None
+unzipit = None
+try:
+ import zipfile
+
+ def zipit(env, target, source):
+ print "Zipping %s:" % str(target[0])
+ def visit(arg, dirname, names):
+ for name in names:
+ path = os.path.join(dirname, name)
+ if os.path.isfile(path):
+ arg.write(path)
+ zf = zipfile.ZipFile(str(target[0]), 'w')
+ olddir = os.getcwd()
+ os.chdir(env['CD'])
+ try: os.path.walk(env['PSV'], visit, zf)
+ finally: os.chdir(olddir)
+ zf.close()
+
+ def unzipit(env, target, source):
+ print "Unzipping %s:" % str(source[0])
+ zf = zipfile.ZipFile(str(source[0]), 'r')
+ for name in zf.namelist():
+ dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
+ dir = os.path.dirname(dest)
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ print dest,name
+ # if the file exists, then delete it before writing
+ # to it so that we don't end up trying to write to a symlink:
+ if os.path.isfile(dest) or os.path.islink(dest):
+ os.unlink(dest)
+ if not os.path.isdir(dest):
+ open(dest, 'wb').write(zf.read(name))
+
+except:
+ if unzip and zip:
+ zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
+ unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
+
+def SCons_revision(target, source, env):
+ """Interpolate specific values from the environment into a file.
+
+ This is used to copy files into a tree that gets packaged up
+ into the source file package.
+ """
+ t = str(target[0])
+ s = source[0].rstr()
+ contents = open(s, 'rb').read()
+ # Note: We construct the __*__ substitution strings here
+ # so that they don't get replaced when this file gets
+ # copied into the tree for packaging.
+ contents = string.replace(contents, '__BUILD' + '__', env['BUILD'])
+ contents = string.replace(contents, '__BUILDSYS' + '__', env['BUILDSYS'])
+ contents = string.replace(contents, '__COPYRIGHT' + '__', env['COPYRIGHT'])
+ contents = string.replace(contents, '__DATE' + '__', env['DATE'])
+ contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER'])
+ contents = string.replace(contents, '__FILE' + '__', str(source[0]))
+ contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
+ contents = string.replace(contents, '__REVISION' + '__', env['REVISION'])
+ contents = string.replace(contents, '__VERSION' + '__', env['VERSION'])
+ contents = string.replace(contents, '__NULL' + '__', '')
+ open(t, 'wb').write(contents)
+ os.chmod(t, os.stat(s)[0])
+
+revbuilder = Builder(action = Action(SCons_revision,
+ varlist=['COPYRIGHT', 'VERSION']))
+
+def soelim(target, source, env):
+ """
+ Interpolate files included in [gnt]roff source files using the
+ .so directive.
+
+ This behaves somewhat like the soelim(1) wrapper around groff, but
+ makes us independent of whether the actual underlying implementation
+ includes an soelim() command or the corresponding command-line option
+ to groff(1). The key behavioral difference is that this doesn't
+ recursively include .so files from the include file. Not yet, anyway.
+ """
+ t = str(target[0])
+ s = str(source[0])
+ dir, f = os.path.split(s)
+ tfp = open(t, 'w')
+ sfp = open(s, 'r')
+ for line in sfp.readlines():
+ if line[:4] in ['.so ', "'so "]:
+ sofile = os.path.join(dir, line[4:-1])
+ tfp.write(open(sofile, 'r').read())
+ else:
+ tfp.write(line)
+ sfp.close()
+ tfp.close()
+
+def soscan(node, env, path):
+ c = node.get_text_contents()
+ return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
+
+soelimbuilder = Builder(action = Action(soelim),
+ source_scanner = Scanner(soscan))
+
+# When copying local files from a Repository (Aegis),
+# just make copies, don't symlink them.
+SetOption('duplicate', 'copy')
+
+env = Environment(
+ ENV = ENV,
+
+ BUILD = build_id,
+ BUILDDIR = build_dir,
+ BUILDSYS = build_system,
+ COPYRIGHT = copyright,
+ DATE = date,
+ DEVELOPER = developer,
+ DISTDIR = os.path.join(build_dir, 'dist'),
+ MONTH_YEAR = month_year,
+ REVISION = revision,
+ VERSION = version,
+ DH_COMPAT = 2,
+
+ TAR_HFLAG = tar_hflag,
+
+ ZIP = zip,
+ ZIPFLAGS = '-r',
+ UNZIP = unzip,
+ UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR',
+
+ ZCAT = zcat,
+
+ RPMBUILD = rpmbuild,
+ RPM2CPIO = 'rpm2cpio',
+
+ TEST_DEB_DIR = test_deb_dir,
+ TEST_RPM_DIR = test_rpm_dir,
+ TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
+ TEST_SRC_ZIP_DIR = test_src_zip_dir,
+ TEST_TAR_GZ_DIR = test_tar_gz_dir,
+ TEST_ZIP_DIR = test_zip_dir,
+
+ UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir,
+ UNPACK_ZIP_DIR = unpack_zip_dir,
+
+ BUILDERS = { 'SCons_revision' : revbuilder,
+ 'SOElim' : soelimbuilder },
+
+ PYTHON = '"%s"' % sys.executable,
+ PYTHONFLAGS = '-tt',
+ )
+
+Version_values = [Value(version), Value(build_id)]
+
+#
+# Define SCons packages.
+#
+# In the original, more complicated packaging scheme, we were going
+# to have separate packages for:
+#
+# python-scons only the build engine
+# scons-script only the script
+# scons the script plus the build engine
+#
+# We're now only delivering a single "scons" package, but this is still
+# "built" as two sub-packages (the build engine and the script), so
+# the definitions remain here, even though we're not using them for
+# separate packages.
+#
+
+python_scons = {
+ 'pkg' : 'python-' + project,
+ 'src_subdir' : 'engine',
+ 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
+ 'rpm_dir' : '/usr/lib/scons',
+
+ 'debian_deps' : [
+ 'debian/changelog',
+ 'debian/control',
+ 'debian/copyright',
+ 'debian/dirs',
+ 'debian/docs',
+ 'debian/postinst',
+ 'debian/prerm',
+ 'debian/rules',
+ ],
+
+ 'files' : [ 'LICENSE.txt',
+ 'README.txt',
+ 'setup.cfg',
+ 'setup.py',
+ ],
+
+ 'filemap' : {
+ 'LICENSE.txt' : '../LICENSE.txt'
+ },
+
+ 'buildermap' : {},
+
+ 'extra_rpm_files' : [],
+
+ 'explicit_deps' : {
+ 'SCons/__init__.py' : Version_values,
+ },
+}
+
+# Figure out the name of a .egg-info file that might be generated
+# as part of the RPM package. There are two complicating factors.
+#
+# First, the RPM spec file we generate will just execute "python", not
+# necessarily the one in sys.executable. If *that* version of python has
+# a distutils that knows about Python eggs, then setup.py will generate a
+# .egg-info file, so we have to execute any distutils logic in a subshell.
+#
+# Second, we can't just have the subshell check for the existence of the
+# distutils.command.install_egg_info module and generate the expected
+# file name by hand, the way we used to, because different systems can
+# have slightly different .egg-info naming conventions. (Specifically,
+# Ubuntu overrides the default behavior to remove the Python version
+# string from the .egg-info file name.) The right way to do this is to
+# actually call into the install_egg_info() class to have it generate
+# the expected name for us.
+#
+# This is all complicated enough that we do it by writing an in-line
+# script to a temporary file and then feeding it to a separate invocation
+# of "python" to tell us the actual name of the generated .egg-info file.
+
+print_egg_info_name = """
+try:
+ from distutils.dist import Distribution
+ from distutils.command.install_egg_info import install_egg_info
+except ImportError:
+ pass
+else:
+ dist = Distribution({'name' : "scons", 'version' : '%s'})
+ i = install_egg_info(dist)
+ i.finalize_options()
+ import os.path
+ print os.path.split(i.outputs[0])[1]
+""" % version
+
+try:
+ fd, tfname = tempfile.mkstemp()
+ tfp = os.fdopen(fd, "w")
+ tfp.write(print_egg_info_name)
+ tfp.close()
+ egg_info_file = os.popen("python %s" % tfname).read()[:-1]
+ if egg_info_file:
+ python_scons['extra_rpm_files'].append(egg_info_file)
+finally:
+ try:
+ os.unlink(tfname)
+ except EnvironmentError:
+ pass
+
+#
+# The original packaging scheme would have have required us to push
+# the Python version number into the package name (python1.5-scons,
+# python2.0-scons, etc.), which would have required a definition
+# like the following. Leave this here in case we ever decide to do
+# this in the future, but note that this would require some modification
+# to src/engine/setup.py before it would really work.
+#
+#python2_scons = {
+# 'pkg' : 'python2-' + project,
+# 'src_subdir' : 'engine',
+# 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'),
+#
+# 'debian_deps' : [
+# 'debian/changelog',
+# 'debian/control',
+# 'debian/copyright',
+# 'debian/dirs',
+# 'debian/docs',
+# 'debian/postinst',
+# 'debian/prerm',
+# 'debian/rules',
+# ],
+#
+# 'files' : [
+# 'LICENSE.txt',
+# 'README.txt',
+# 'setup.cfg',
+# 'setup.py',
+# ],
+# 'filemap' : {
+# 'LICENSE.txt' : '../LICENSE.txt',
+# },
+# 'buildermap' : {},
+#}
+#
+
+scons_script = {
+ 'pkg' : project + '-script',
+ 'src_subdir' : 'script',
+ 'inst_subdir' : 'bin',
+ 'rpm_dir' : '/usr/bin',
+
+ 'debian_deps' : [
+ 'debian/changelog',
+ 'debian/control',
+ 'debian/copyright',
+ 'debian/dirs',
+ 'debian/docs',
+ 'debian/postinst',
+ 'debian/prerm',
+ 'debian/rules',
+ ],
+
+ 'files' : [
+ 'LICENSE.txt',
+ 'README.txt',
+ 'setup.cfg',
+ 'setup.py',
+ ],
+
+ 'filemap' : {
+ 'LICENSE.txt' : '../LICENSE.txt',
+ 'scons' : 'scons.py',
+ 'sconsign' : 'sconsign.py',
+ 'scons-time' : 'scons-time.py',
+ },
+
+ 'buildermap' : {},
+
+ 'extra_rpm_files' : [
+ 'scons-' + version,
+ 'sconsign-' + version,
+ 'scons-time-' + version,
+ ],
+
+ 'explicit_deps' : {
+ 'scons' : Version_values,
+ 'sconsign' : Version_values,
+ },
+}
+
+scons = {
+ 'pkg' : project,
+
+ 'debian_deps' : [
+ 'debian/changelog',
+ 'debian/control',
+ 'debian/copyright',
+ 'debian/dirs',
+ 'debian/docs',
+ 'debian/postinst',
+ 'debian/prerm',
+ 'debian/rules',
+ ],
+
+ 'files' : [
+ 'CHANGES.txt',
+ 'LICENSE.txt',
+ 'README.txt',
+ 'RELEASE.txt',
+ 'os_spawnv_fix.diff',
+ 'scons.1',
+ 'sconsign.1',
+ 'scons-time.1',
+ 'script/scons.bat',
+ #'script/scons-post-install.py',
+ 'setup.cfg',
+ 'setup.py',
+ ],
+
+ 'filemap' : {
+ 'scons.1' : '$BUILDDIR/doc/man/scons.1',
+ 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
+ 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
+ },
+
+ 'buildermap' : {
+ 'scons.1' : env.SOElim,
+ 'sconsign.1' : env.SOElim,
+ 'scons-time.1' : env.SOElim,
+ },
+
+ 'subpkgs' : [ python_scons, scons_script ],
+
+ 'subinst_dirs' : {
+ 'python-' + project : python_project_subinst_dir,
+ project + '-script' : project_script_subinst_dir,
+ },
+}
+
+scripts = ['scons', 'sconsign', 'scons-time']
+
+src_deps = []
+src_files = []
+
+for p in [ scons ]:
+ #
+ # Initialize variables with the right directories for this package.
+ #
+ pkg = p['pkg']
+ pkg_version = "%s-%s" % (pkg, version)
+
+
+ src = 'src'
+ if p.has_key('src_subdir'):
+ src = os.path.join(src, p['src_subdir'])
+
+ build = os.path.join(build_dir, pkg)
+
+ tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
+ platform_tar_gz = os.path.join(build,
+ 'dist',
+ "%s.%s.tar.gz" % (pkg_version, platform))
+ zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
+ platform_zip = os.path.join(build,
+ 'dist',
+ "%s.%s.zip" % (pkg_version, platform))
+ if platform == "win-amd64":
+ win32_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version)
+ else:
+ win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
+
+ #
+ # Update the environment with the relevant information
+ # for this package.
+ #
+ # We can get away with calling setup.py using a directory path
+ # like this because we put a preamble in it that will chdir()
+ # to the directory in which setup.py exists.
+ #
+ setup_py = os.path.join(build, 'setup.py')
+ env.Replace(PKG = pkg,
+ PKG_VERSION = pkg_version,
+ SETUP_PY = '"%s"' % setup_py)
+ Local(setup_py)
+
+ #
+ # Read up the list of source files from our MANIFEST.in.
+ # This list should *not* include LICENSE.txt, MANIFEST,
+ # README.txt, or setup.py. Make a copy of the list for the
+ # destination files.
+ #
+ manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
+ src_files = map(lambda x: x[:-1],
+ open(manifest_in).readlines())
+ raw_files = src_files[:]
+ dst_files = src_files[:]
+ rpm_files = []
+
+ MANIFEST_in_list = []
+
+ if p.has_key('subpkgs'):
+ #
+ # This package includes some sub-packages. Read up their
+ # MANIFEST.in files, and add them to our source and destination
+ # file lists, modifying them as appropriate to add the
+ # specified subdirs.
+ #
+ for sp in p['subpkgs']:
+ ssubdir = sp['src_subdir']
+ isubdir = p['subinst_dirs'][sp['pkg']]
+ MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
+ MANIFEST_in_list.append(MANIFEST_in)
+ files = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
+ raw_files.extend(files)
+ src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), files))
+ for f in files:
+ r = os.path.join(sp['rpm_dir'], f)
+ rpm_files.append(r)
+ if f[-3:] == ".py":
+ rpm_files.append(r + 'c')
+ for f in sp.get('extra_rpm_files', []):
+ r = os.path.join(sp['rpm_dir'], f)
+ rpm_files.append(r)
+ files = map(lambda x, i=isubdir: os.path.join(i, x), files)
+ dst_files.extend(files)
+ for k, f in sp['filemap'].items():
+ if f:
+ k = os.path.join(ssubdir, k)
+ p['filemap'][k] = os.path.join(ssubdir, f)
+ for f, deps in sp['explicit_deps'].items():
+ f = os.path.join(build, ssubdir, f)
+ env.Depends(f, deps)
+
+ #
+ # Now that we have the "normal" source files, add those files
+ # that are standard for each distribution. Note that we don't
+ # add these to dst_files, because they don't get installed.
+ # And we still have the MANIFEST to add.
+ #
+ src_files.extend(p['files'])
+
+ #
+ # Now run everything in src_file through the sed command we
+ # concocted to expand SConstruct, 1.2.0.d20091224, etc.
+ #
+ for b in src_files:
+ s = p['filemap'].get(b, b)
+ if not s[0] == '$' and not os.path.isabs(s):
+ s = os.path.join(src, s)
+ builder = p['buildermap'].get(b, env.SCons_revision)
+ x = builder(os.path.join(build, b), s)
+ Local(x)
+
+ #
+ # NOW, finally, we can create the MANIFEST, which we do
+ # by having Python spit out the contents of the src_files
+ # array we've carefully created. After we've added
+ # MANIFEST itself to the array, of course.
+ #
+ src_files.append("MANIFEST")
+ MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
+
+ def write_src_files(target, source, **kw):
+ global src_files
+ src_files.sort()
+ f = open(str(target[0]), 'wb')
+ for file in src_files:
+ f.write(file + "\n")
+ f.close()
+ return 0
+ env.Command(os.path.join(build, 'MANIFEST'),
+ MANIFEST_in_list,
+ write_src_files)
+
+ #
+ # Now go through and arrange to create whatever packages we can.
+ #
+ build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
+ apply(Local, build_src_files, {})
+
+ distutils_formats = []
+
+ distutils_targets = [ win32_exe ]
+
+ dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
+ Local(dist_distutils_targets)
+ AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
+
+ if not gzip:
+ print "gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg)
+ else:
+
+ distutils_formats.append('gztar')
+
+ src_deps.append(tar_gz)
+
+ distutils_targets.extend([ tar_gz, platform_tar_gz ])
+
+ dist_tar_gz = env.Install('$DISTDIR', tar_gz)
+ dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
+ Local(dist_tar_gz, dist_platform_tar_gz)
+ AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
+ AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
+
+ #
+ # Unpack the tar.gz archive created by the distutils into
+ # build/unpack-tar-gz/scons-{version}.
+ #
+ # We'd like to replace the last three lines with the following:
+ #
+ # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
+ #
+ # but that gives heartburn to Cygwin's tar, so work around it
+ # with separate zcat-tar-rm commands.
+ #
+ unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
+ os.path.join(u, pv, x),
+ src_files)
+ env.Command(unpack_tar_gz_files, dist_tar_gz, [
+ Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
+ "$ZCAT $SOURCES > .temp",
+ "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
+ Delete(".temp"),
+ ])
+
+ #
+ # Run setup.py in the unpacked subdirectory to "install" everything
+ # into our build/test subdirectory. The runtest.py script will set
+ # PYTHONPATH so that the tests only look under build/test-{package},
+ # and under etc (for the testing modules TestCmd.py, TestSCons.py,
+ # and unittest.py). This makes sure that our tests pass with what
+ # we really packaged, not because of something hanging around in
+ # the development directory.
+ #
+ # We can get away with calling setup.py using a directory path
+ # like this because we put a preamble in it that will chdir()
+ # to the directory in which setup.py exists.
+ #
+ dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
+ env.Command(dfiles, unpack_tar_gz_files, [
+ Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
+ Delete("$TEST_TAR_GZ_DIR"),
+ '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
+ os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
+ ])
+
+ #
+ # Generate portage files for submission to Gentoo Linux.
+ #
+ gentoo = os.path.join(build, 'gentoo')
+ ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
+ digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
+ env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
+ def Digestify(target, source, env):
+ import md5
+ def hexdigest(s):
+ """Return a signature as a string of hex characters.
+ """
+ # NOTE: This routine is a method in the Python 2.0 interface
+ # of the native md5 module, but we want SCons to operate all
+ # the way back to at least Python 1.5.2, which doesn't have it.
+ h = string.hexdigits
+ r = ''
+ for c in s:
+ i = ord(c)
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+ return r
+ src = source[0].rfile()
+ contents = open(str(src)).read()
+ sig = hexdigest(md5.new(contents).digest())
+ bytes = os.stat(str(src))[6]
+ open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
+ src.name,
+ bytes))
+ env.Command(digest, tar_gz, Digestify)
+
+ if not zipit:
+ print "zip not found; skipping .zip package for %s." % pkg
+ else:
+
+ distutils_formats.append('zip')
+
+ src_deps.append(zip)
+
+ distutils_targets.extend([ zip, platform_zip ])
+
+ dist_zip = env.Install('$DISTDIR', zip)
+ dist_platform_zip = env.Install('$DISTDIR', platform_zip)
+ Local(dist_zip, dist_platform_zip)
+ AddPostAction(dist_zip, Chmod(dist_zip, 0644))
+ AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
+
+ #
+ # Unpack the zip archive created by the distutils into
+ # build/unpack-zip/scons-{version}.
+ #
+ unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
+ os.path.join(u, pv, x),
+ src_files)
+
+ env.Command(unpack_zip_files, dist_zip, [
+ Delete(os.path.join(unpack_zip_dir, pkg_version)),
+ unzipit,
+ ])
+
+ #
+ # Run setup.py in the unpacked subdirectory to "install" everything
+ # into our build/test subdirectory. The runtest.py script will set
+ # PYTHONPATH so that the tests only look under build/test-{package},
+ # and under etc (for the testing modules TestCmd.py, TestSCons.py,
+ # and unittest.py). This makes sure that our tests pass with what
+ # we really packaged, not because of something hanging around in
+ # the development directory.
+ #
+ # We can get away with calling setup.py using a directory path
+ # like this because we put a preamble in it that will chdir()
+ # to the directory in which setup.py exists.
+ #
+ dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
+ env.Command(dfiles, unpack_zip_files, [
+ Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
+ Delete("$TEST_ZIP_DIR"),
+ '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
+ os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
+ ])
+
+ if not rpmbuild:
+ msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
+ AlwaysBuild(Alias('rpm', [], msg))
+ else:
+ topdir = os.path.join(build, 'build',
+ 'bdist.' + platform, 'rpm')
+
+ buildroot = os.path.join(build_dir, 'rpm-buildroot')
+
+ BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
+ RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
+ SOURCESdir = os.path.join(topdir, 'SOURCES')
+ SPECSdir = os.path.join(topdir, 'SPECS')
+ SRPMSdir = os.path.join(topdir, 'SRPMS')
+
+ specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
+ specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
+ sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
+ noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
+ src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
+
+ def spec_function(target, source, env):
+ """Generate the RPM .spec file from the template file.
+
+ This fills in the %files portion of the .spec file with a
+ list generated from our MANIFEST(s), so we don't have to
+ maintain multiple lists.
+ """
+ c = open(str(source[0]), 'rb').read()
+ c = string.replace(c, '__VERSION' + '__', env['VERSION'])
+ c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES'])
+ open(str(target[0]), 'wb').write(c)
+
+ rpm_files.sort()
+ rpm_files_str = string.join(rpm_files, "\n") + "\n"
+ rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
+ rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
+ rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
+
+ env.InstallAs(sourcefile, tar_gz)
+ Local(sourcefile)
+
+ targets = [ noarch_rpm, src_rpm ]
+ cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
+ if not os.path.isdir(BUILDdir):
+ cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
+ t = env.Command(targets, specfile, cmd)
+ env.Depends(t, sourcefile)
+
+ dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
+ dist_src_rpm = env.Install('$DISTDIR', src_rpm)
+ Local(dist_noarch_rpm, dist_src_rpm)
+ AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
+ AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
+
+ dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
+ dst_files)
+ env.Command(dfiles,
+ dist_noarch_rpm,
+ "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
+
+ if dh_builddeb and fakeroot:
+ # Our Debian packaging builds directly into build/dist,
+ # so we don't need to Install() the .debs.
+ deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
+ for d in p['debian_deps']:
+ b = env.SCons_revision(os.path.join(build, d), d)
+ env.Depends(deb, b)
+ Local(b)
+ env.Command(deb, build_src_files, [
+ "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
+ ])
+
+ old = os.path.join('lib', 'scons', '')
+ new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
+ def xxx(s, old=old, new=new):
+ if s[:len(old)] == old:
+ s = new + s[len(old):]
+ return os.path.join('usr', s)
+ dfiles = map(lambda x, t=test_deb_dir: os.path.join(t, x),
+ map(xxx, dst_files))
+ env.Command(dfiles,
+ deb,
+ "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
+
+
+ #
+ # Use the Python distutils to generate the appropriate packages.
+ #
+ commands = [
+ Delete(os.path.join(build, 'build', 'lib')),
+ Delete(os.path.join(build, 'build', 'scripts')),
+ ]
+
+ if distutils_formats:
+ commands.append(Delete(os.path.join(build,
+ 'build',
+ 'bdist.' + platform,
+ 'dumb')))
+ for format in distutils_formats:
+ commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
+
+ commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \
+ string.join(distutils_formats, ','))
+
+ commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
+
+ env.Command(distutils_targets, build_src_files, commands)
+
+ #
+ # Now create local packages for people who want to let people
+ # build their SCons-buildable packages without having to
+ # install SCons.
+ #
+ s_l_v = '%s-local-%s' % (pkg, version)
+
+ local = pkg + '-local'
+ build_dir_local = os.path.join(build_dir, local)
+ build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
+
+ dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
+ dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
+ AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
+ AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
+
+ commands = [
+ Delete(build_dir_local),
+ '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
+ (build_dir_local, build_dir_local_slv),
+ ]
+
+ for script in scripts:
+ #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
+ local_script = os.path.join(build_dir_local, script)
+ commands.append(Move(local_script + '.py', local_script))
+
+ rf = filter(lambda x: not x in scripts, raw_files)
+ rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
+ for script in scripts:
+ rf.append("%s.py" % script)
+ local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf)
+
+ env.Command(local_targets, build_src_files, commands)
+
+ scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
+ l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
+ local_targets.append(l)
+ Local(l)
+
+ scons_README = os.path.join(build_dir_local, 'scons-README')
+ l = env.SCons_revision(scons_README, 'README-local')
+ local_targets.append(l)
+ Local(l)
+
+ if gzip:
+ env.Command(dist_local_tar_gz,
+ local_targets,
+ "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
+
+ unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
+ os.path.join(d, x),
+ rf)
+ commands = [Delete(test_local_tar_gz_dir),
+ Mkdir(test_local_tar_gz_dir),
+ "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
+
+ env.Command(unpack_targets, dist_local_tar_gz, commands)
+
+ if zipit:
+ env.Command(dist_local_zip, local_targets, zipit,
+ CD = build_dir_local, PSV = '.')
+
+ unpack_targets = map(lambda x, d=test_local_zip_dir:
+ os.path.join(d, x),
+ rf)
+ commands = [Delete(test_local_zip_dir),
+ Mkdir(test_local_zip_dir),
+ unzipit]
+
+ env.Command(unpack_targets, dist_local_zip, unzipit,
+ UNPACK_ZIP_DIR = test_local_zip_dir)
+
+#
+#
+#
+Export('build_dir', 'env')
+
+SConscript('QMTest/SConscript')
+
+#
+#
+#
+files = [
+ 'runtest.py',
+]
+
+def copy(target, source, env):
+ t = str(target[0])
+ s = str(source[0])
+ open(t, 'wb').write(open(s, 'rb').read())
+
+for file in files:
+ # Guarantee that real copies of these files always exist in
+ # build/. If there's a symlink there, then this is an Aegis
+ # build and we blow them away now so that they'll get "built" later.
+ p = os.path.join(build_dir, file)
+ if os.path.islink(p):
+ os.unlink(p)
+ if not os.path.isabs(p):
+ p = '#' + p
+ sp = env.Command(p, file, copy)
+ Local(sp)
+
+#
+# Documentation.
+#
+Export('build_dir', 'env', 'whereis')
+
+SConscript('doc/SConscript')
+
+#
+# If we're running in a Subversion working directory, pack up a complete
+# source archive from the project files and files in the change.
+#
+
+sfiles = None
+if hg_status_lines:
+ slines = filter(lambda l: l[0] in 'ACM', hg_status_lines)
+ sfiles = map(lambda l: l.split()[-1], slines)
+elif svn_status_lines:
+ slines = filter(lambda l: l[0] in ' MA', svn_status_lines)
+ sentries = map(lambda l: l.split()[-1], slines)
+ sfiles = filter(os.path.isfile, sentries)
+else:
+ "Not building in a Mercurial or Subversion tree; skipping building src package."
+
+if sfiles:
+ remove_patterns = [
+ '.hgt/*',
+ '.svnt/*',
+ '*.aeignore',
+ '*.cvsignore',
+ '*.hgignore',
+ 'www/*',
+ ]
+
+ for p in remove_patterns:
+ sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles)
+
+ if sfiles:
+ ps = "%s-src" % project
+ psv = "%s-%s" % (ps, version)
+ b_ps = os.path.join(build_dir, ps)
+ b_psv = os.path.join(build_dir, psv)
+ b_psv_stamp = b_psv + '-stamp'
+
+ src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
+ src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
+
+ Local(src_tar_gz, src_zip)
+
+ for file in sfiles:
+ env.SCons_revision(os.path.join(b_ps, file), file)
+
+ b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
+ cmds = [
+ Delete(b_psv),
+ Copy(b_psv, b_ps),
+ Touch("$TARGET"),
+ ]
+
+ env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
+
+ apply(Local, b_ps_files, {})
+
+ if gzip:
+
+ env.Command(src_tar_gz, b_psv_stamp,
+ "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
+
+ #
+ # Unpack the archive into build/unpack/scons-{version}.
+ #
+ unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
+ os.path.join(u, psv, x),
+ sfiles)
+
+ #
+ # We'd like to replace the last three lines with the following:
+ #
+ # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
+ #
+ # but that gives heartburn to Cygwin's tar, so work around it
+ # with separate zcat-tar-rm commands.
+ env.Command(unpack_tar_gz_files, src_tar_gz, [
+ Delete(os.path.join(unpack_tar_gz_dir, psv)),
+ "$ZCAT $SOURCES > .temp",
+ "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
+ Delete(".temp"),
+ ])
+
+ #
+ # Run setup.py in the unpacked subdirectory to "install" everything
+ # into our build/test subdirectory. The runtest.py script will set
+ # PYTHONPATH so that the tests only look under build/test-{package},
+ # and under etc (for the testing modules TestCmd.py, TestSCons.py,
+ # and unittest.py). This makes sure that our tests pass with what
+ # we really packaged, not because of something hanging around in
+ # the development directory.
+ #
+ # We can get away with calling setup.py using a directory path
+ # like this because we put a preamble in it that will chdir()
+ # to the directory in which setup.py exists.
+ #
+ dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
+ dst_files)
+ scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
+ ENV = env.Dictionary('ENV').copy()
+ ENV['SCONS_LIB_DIR'] = scons_lib_dir
+ ENV['USERNAME'] = developer
+ env.Command(dfiles, unpack_tar_gz_files,
+ [
+ Delete(os.path.join(unpack_tar_gz_dir,
+ psv,
+ 'build',
+ 'scons',
+ 'build')),
+ Delete("$TEST_SRC_TAR_GZ_DIR"),
+ 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
+ (os.path.join(unpack_tar_gz_dir, psv),
+ os.path.join('src', 'script', 'scons.py'),
+ os.path.join('build', 'scons')),
+ '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
+ os.path.join(unpack_tar_gz_dir,
+ psv,
+ 'build',
+ 'scons',
+ 'setup.py'),
+ ],
+ ENV = ENV)
+
+ if zipit:
+
+ env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
+
+ #
+ # Unpack the archive into build/unpack/scons-{version}.
+ #
+ unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
+ os.path.join(u, psv, x),
+ sfiles)
+
+ env.Command(unpack_zip_files, src_zip, [
+ Delete(os.path.join(unpack_zip_dir, psv)),
+ unzipit
+ ])
+
+ #
+ # Run setup.py in the unpacked subdirectory to "install" everything
+ # into our build/test subdirectory. The runtest.py script will set
+ # PYTHONPATH so that the tests only look under build/test-{package},
+ # and under etc (for the testing modules TestCmd.py, TestSCons.py,
+ # and unittest.py). This makes sure that our tests pass with what
+ # we really packaged, not because of something hanging around in
+ # the development directory.
+ #
+ # We can get away with calling setup.py using a directory path
+ # like this because we put a preamble in it that will chdir()
+ # to the directory in which setup.py exists.
+ #
+ dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
+ dst_files)
+ scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
+ ENV = env.Dictionary('ENV').copy()
+ ENV['SCONS_LIB_DIR'] = scons_lib_dir
+ ENV['USERNAME'] = developer
+ env.Command(dfiles, unpack_zip_files,
+ [
+ Delete(os.path.join(unpack_zip_dir,
+ psv,
+ 'build',
+ 'scons',
+ 'build')),
+ Delete("$TEST_SRC_ZIP_DIR"),
+ 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
+ (os.path.join(unpack_zip_dir, psv),
+ os.path.join('src', 'script', 'scons.py'),
+ os.path.join('build', 'scons')),
+ '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
+ os.path.join(unpack_zip_dir,
+ psv,
+ 'build',
+ 'scons',
+ 'setup.py'),
+ ],
+ ENV = ENV)
+
+for pf, help_text in packaging_flavors:
+ Alias(pf, [
+ os.path.join(build_dir, 'test-'+pf),
+ os.path.join(build_dir, 'QMTest'),
+ os.path.join(build_dir, 'runtest.py'),
+ ])
diff --git a/bin/Command.py b/bin/Command.py
new file mode 100644
index 0000000..efaa356
--- /dev/null
+++ b/bin/Command.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+#
+# XXX Python script template
+#
+# XXX Describe what the script does here.
+#
+
+import getopt
+import os
+import shlex
+import sys
+
+class Usage(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+
+class CommandRunner:
+ """
+ Representation of a command to be executed.
+ """
+
+ def __init__(self, dictionary={}):
+ self.subst_dictionary(dictionary)
+
+ def subst_dictionary(self, dictionary):
+ self._subst_dictionary = dictionary
+
+ def subst(self, string, dictionary=None):
+ """
+ Substitutes (via the format operator) the values in the specified
+ dictionary into the specified command.
+
+ The command can be an (action, string) tuple. In all cases, we
+ perform substitution on strings and don't worry if something isn't
+ a string. (It's probably a Python function to be executed.)
+ """
+ if dictionary is None:
+ dictionary = self._subst_dictionary
+ if dictionary:
+ try:
+ string = string % dictionary
+ except TypeError:
+ pass
+ return string
+
+ def do_display(self, string):
+ if type(string) == type(()):
+ func = string[0]
+ args = string[1:]
+ s = '%s(%s)' % (func.__name__, ', '.join(map(repr, args)))
+ else:
+ s = self.subst(string)
+ if not s.endswith('\n'):
+ s += '\n'
+ sys.stdout.write(s)
+ sys.stdout.flush()
+
+ def do_not_display(self, string):
+ pass
+
+ def do_execute(self, command):
+ if type(command) == type(''):
+ command = self.subst(command)
+ cmdargs = shlex.split(command)
+ if cmdargs[0] == 'cd':
+ command = (os.chdir,) + tuple(cmdargs[1:])
+ elif cmdargs[0] == 'mkdir':
+ command = (os.mkdir,) + tuple(cmdargs[1:])
+ if type(command) == type(()):
+ func = command[0]
+ args = command[1:]
+ return func(*args)
+ else:
+ return os.system(command)
+
+ def do_not_execute(self, command):
+ pass
+
+ display = do_display
+ execute = do_execute
+
+ def run(self, command, display=None):
+ """
+ Runs this command, displaying it first.
+
+ The actual display() and execute() methods we call may be
+ overridden if we're printing but not executing, or vice versa.
+ """
+ if display is None:
+ display = command
+ self.display(display)
+ return self.execute(command)
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ short_options = 'hnq'
+ long_options = ['help', 'no-exec', 'quiet']
+
+ helpstr = """\
+Usage: script-template.py [-hnq]
+
+ -h, --help Print this help and exit
+ -n, --no-exec No execute, just print command lines
+ -q, --quiet Quiet, don't print command lines
+"""
+
+ try:
+ try:
+ opts, args = getopt.getopt(argv[1:], short_options, long_options)
+ except getopt.error, msg:
+ raise Usage(msg)
+
+ for o, a in opts:
+ if o in ('-h', '--help'):
+ print helpstr
+ sys.exit(0)
+ elif o in ('-n', '--no-exec'):
+ Command.execute = Command.do_not_execute
+ elif o in ('-q', '--quiet'):
+ Command.display = Command.do_not_display
+ except Usage, err:
+ sys.stderr.write(err.msg)
+ sys.stderr.write('use -h to get help')
+ return 2
+
+ commands = [
+ ]
+
+ for command in [ Command(c) for c in commands ]:
+ status = command.run(command)
+ if status:
+ sys.exit(status)
+
+if __name__ == "__main__":
+ sys.exit(main())
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py
new file mode 100644
index 0000000..d3a043b
--- /dev/null
+++ b/bin/SConsDoc.py
@@ -0,0 +1,336 @@
+#!/usr/bin/env python
+#
+# Module for handling SCons documentation processing.
+#
+
+__doc__ = """
+This module parses home-brew XML files that document various things
+in SCons. Right now, it handles Builders, construction variables,
+and Tools, but we expect it to get extended in the future.
+
+In general, you can use any DocBook tag in the input, and this module
+just adds processing various home-brew tags to try to make life a
+little easier.
+
+Builder example:
+
+ <builder name="VARIABLE">
+ <summary>
+ This is the summary description of an SCons Tool.
+ It will get placed in the man page,
+ and in the appropriate User's Guide appendix.
+ The name of any builder may be interpolated
+ anywhere in the document by specifying the
+ &b-VARIABLE;
+ element. It need not be on a line by itself.
+
+ Unlike normal XML, blank lines are significant in these
+ descriptions and serve to separate paragraphs.
+ They'll get replaced in DocBook output with appropriate tags
+ to indicate a new paragraph.
+
+ <example>
+ print "this is example code, it will be offset and indented"
+ </example>
+ </summary>
+ </builder>
+
+Construction variable example:
+
+ <cvar name="VARIABLE">
+ <summary>
+ This is the summary description of a construction variable.
+ It will get placed in the man page,
+ and in the appropriate User's Guide appendix.
+ The name of any construction variable may be interpolated
+ anywhere in the document by specifying the
+ &t-VARIABLE;
+ element. It need not be on a line by itself.
+
+ Unlike normal XML, blank lines are significant in these
+ descriptions and serve to separate paragraphs.
+ They'll get replaced in DocBook output with appropriate tags
+ to indicate a new paragraph.
+
+ <example>
+ print "this is example code, it will be offset and indented"
+ </example>
+ </summary>
+ </cvar>
+
+Tool example:
+
+ <tool name="VARIABLE">
+ <summary>
+ This is the summary description of an SCons Tool.
+ It will get placed in the man page,
+ and in the appropriate User's Guide appendix.
+ The name of any tool may be interpolated
+ anywhere in the document by specifying the
+ &t-VARIABLE;
+ element. It need not be on a line by itself.
+
+ Unlike normal XML, blank lines are significant in these
+ descriptions and serve to separate paragraphs.
+ They'll get replaced in DocBook output with appropriate tags
+ to indicate a new paragraph.
+
+ <example>
+ print "this is example code, it will be offset and indented"
+ </example>
+ </summary>
+ </tool>
+"""
+
+import os.path
+import imp
+import sys
+import xml.sax.handler
+
+class Item:
+ def __init__(self, name):
+ self.name = name
+ self.sort_name = name.lower()
+ if self.sort_name[0] == '_':
+ self.sort_name = self.sort_name[1:]
+ self.summary = []
+ self.sets = None
+ self.uses = None
+ def cmp_name(self, name):
+ if name[0] == '_':
+ name = name[1:]
+ return name.lower()
+ def __cmp__(self, other):
+ return cmp(self.sort_name, other.sort_name)
+
+class Builder(Item):
+ pass
+
+class Tool(Item):
+ def __init__(self, name):
+ Item.__init__(self, name)
+ self.entity = self.name.replace('+', 'X')
+
+class ConstructionVariable(Item):
+ pass
+
+class Chunk:
+ def __init__(self, tag, body=None):
+ self.tag = tag
+ if not body:
+ body = []
+ self.body = body
+ def __str__(self):
+ body = ''.join(self.body)
+ return "<%s>%s</%s>\n" % (self.tag, body, self.tag)
+ def append(self, data):
+ self.body.append(data)
+
+class Summary:
+ def __init__(self):
+ self.body = []
+ self.collect = []
+ def append(self, data):
+ self.collect.append(data)
+ def end_para(self):
+ text = ''.join(self.collect)
+ paras = text.split('\n\n')
+ if paras == ['\n']:
+ return
+ if paras[0] == '':
+ self.body.append('\n')
+ paras = paras[1:]
+ paras[0] = '\n' + paras[0]
+ if paras[-1] == '':
+ paras = paras[:-1]
+ paras[-1] = paras[-1] + '\n'
+ last = '\n'
+ else:
+ last = None
+ sep = None
+ for p in paras:
+ c = Chunk("para", p)
+ if sep:
+ self.body.append(sep)
+ self.body.append(c)
+ sep = '\n'
+ if last:
+ self.body.append(last)
+ def begin_chunk(self, chunk):
+ self.end_para()
+ self.collect = chunk
+ def end_chunk(self):
+ self.body.append(self.collect)
+ self.collect = []
+
+class SConsDocHandler(xml.sax.handler.ContentHandler,
+ xml.sax.handler.ErrorHandler):
+ def __init__(self):
+ self._start_dispatch = {}
+ self._end_dispatch = {}
+ keys = self.__class__.__dict__.keys()
+ start_tag_method_names = filter(lambda k: k[:6] == 'start_', keys)
+ end_tag_method_names = filter(lambda k: k[:4] == 'end_', keys)
+ for method_name in start_tag_method_names:
+ tag = method_name[6:]
+ self._start_dispatch[tag] = getattr(self, method_name)
+ for method_name in end_tag_method_names:
+ tag = method_name[4:]
+ self._end_dispatch[tag] = getattr(self, method_name)
+ self.stack = []
+ self.collect = []
+ self.current_object = []
+ self.builders = {}
+ self.tools = {}
+ self.cvars = {}
+
+ def startElement(self, name, attrs):
+ try:
+ start_element_method = self._start_dispatch[name]
+ except KeyError:
+ self.characters('<%s>' % name)
+ else:
+ start_element_method(attrs)
+
+ def endElement(self, name):
+ try:
+ end_element_method = self._end_dispatch[name]
+ except KeyError:
+ self.characters('</%s>' % name)
+ else:
+ end_element_method()
+
+ #
+ #
+ def characters(self, chars):
+ self.collect.append(chars)
+
+ def begin_collecting(self, chunk):
+ self.collect = chunk
+ def end_collecting(self):
+ self.collect = []
+
+ def begin_chunk(self):
+ pass
+ def end_chunk(self):
+ pass
+
+ #
+ #
+ #
+
+ def begin_xxx(self, obj):
+ self.stack.append(self.current_object)
+ self.current_object = obj
+ def end_xxx(self):
+ self.current_object = self.stack.pop()
+
+ #
+ #
+ #
+ def start_scons_doc(self, attrs):
+ pass
+ def end_scons_doc(self):
+ pass
+
+ def start_builder(self, attrs):
+ name = attrs.get('name')
+ try:
+ builder = self.builders[name]
+ except KeyError:
+ builder = Builder(name)
+ self.builders[name] = builder
+ self.begin_xxx(builder)
+ def end_builder(self):
+ self.end_xxx()
+
+ def start_tool(self, attrs):
+ name = attrs.get('name')
+ try:
+ tool = self.tools[name]
+ except KeyError:
+ tool = Tool(name)
+ self.tools[name] = tool
+ self.begin_xxx(tool)
+ def end_tool(self):
+ self.end_xxx()
+
+ def start_cvar(self, attrs):
+ name = attrs.get('name')
+ try:
+ cvar = self.cvars[name]
+ except KeyError:
+ cvar = ConstructionVariable(name)
+ self.cvars[name] = cvar
+ self.begin_xxx(cvar)
+ def end_cvar(self):
+ self.end_xxx()
+
+ def start_summary(self, attrs):
+ summary = Summary()
+ self.current_object.summary = summary
+ self.begin_xxx(summary)
+ self.begin_collecting(summary)
+ def end_summary(self):
+ self.current_object.end_para()
+ self.end_xxx()
+
+ def start_example(self, attrs):
+ example = Chunk("programlisting")
+ self.current_object.begin_chunk(example)
+ def end_example(self):
+ self.current_object.end_chunk()
+
+ def start_uses(self, attrs):
+ self.begin_collecting([])
+ def end_uses(self):
+ self.current_object.uses = ''.join(self.collect).split()
+ self.current_object.uses.sort()
+ self.end_collecting()
+
+ def start_sets(self, attrs):
+ self.begin_collecting([])
+ def end_sets(self):
+ self.current_object.sets = ''.join(self.collect).split()
+ self.current_object.sets.sort()
+ self.end_collecting()
+
+ # Stuff for the ErrorHandler portion.
+ def error(self, exception):
+ linenum = exception._linenum - self.preamble_lines
+ sys.stderr.write('%s:%d:%d: %s (error)\n' % (self.filename, linenum, exception._colnum, ''.join(exception.args)))
+
+ def fatalError(self, exception):
+ linenum = exception._linenum - self.preamble_lines
+ sys.stderr.write('%s:%d:%d: %s (fatalError)\n' % (self.filename, linenum, exception._colnum, ''.join(exception.args)))
+
+ def set_file_info(self, filename, preamble_lines):
+ self.filename = filename
+ self.preamble_lines = preamble_lines
+
+# lifted from Ka-Ping Yee's way cool pydoc module.
+def importfile(path):
+ """Import a Python source file or compiled file given its path."""
+ magic = imp.get_magic()
+ file = open(path, 'r')
+ if file.read(len(magic)) == magic:
+ kind = imp.PY_COMPILED
+ else:
+ kind = imp.PY_SOURCE
+ file.close()
+ filename = os.path.basename(path)
+ name, ext = os.path.splitext(filename)
+ file = open(path, 'r')
+ try:
+ module = imp.load_module(name, file, path, (ext, 'r', kind))
+ except ImportError, e:
+ sys.stderr.write("Could not import %s: %s\n" % (path, e))
+ return None
+ file.close()
+ return module
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/ae-cvs-ci b/bin/ae-cvs-ci
new file mode 100755
index 0000000..47c8073
--- /dev/null
+++ b/bin/ae-cvs-ci
@@ -0,0 +1,204 @@
+#
+# aegis - project change supervisor
+# Copyright (C) 2004 Peter Miller;
+# All rights reserved.
+#
+# As a specific exception to the GPL, you are allowed to copy
+# this source file into your own project and modify it, without
+# releasing your project under the GPL, unless there is some other
+# file or condition which would require it.
+#
+# MANIFEST: shell script to commit changes to CVS
+#
+# It is assumed that your CVSROOT and CVS_RSH environment variables have
+# already been set appropriately.
+#
+# This script is expected to be run as by integrate_pass_notify_command
+# and as such the baseline has already assumed the shape asked for by
+# the change.
+#
+# integrate_pass_notify_command =
+# "$bin/ae-cvs-ci $project $change";
+#
+# Alternatively, you may wish to tailor this script to the individual
+# needs of your project. Make it a source file, e.g. "etc/ae-cvs-ci.sh"
+# and then use the following:
+#
+# integrate_pass_notify_command =
+# "$sh ${s etc/ae-cvs-ci} $project $change";
+#
+
+USAGE="Usage: $0 <project> <change>"
+
+PRINT="echo"
+EXECUTE="eval"
+
+while getopts "hnq" FLAG
+do
+ case ${FLAG} in
+ h )
+ echo "${USAGE}"
+ exit 0
+ ;;
+ n )
+ EXECUTE=":"
+ ;;
+ q )
+ PRINT=":"
+ ;;
+ * )
+ echo "$0: unknown option ${FLAG}" >&2
+ exit 1
+ ;;
+ esac
+done
+
+shift `expr ${OPTIND} - 1`
+
+case $# in
+2)
+ project=$1
+ change=$2
+ ;;
+*)
+ echo "${USAGE}" 1>&2
+ exit 1
+ ;;
+esac
+
+here=`pwd`
+
+AEGIS_PROJECT=$project
+export AEGIS_PROJECT
+AEGIS_CHANGE=$change
+export AEGIS_CHANGE
+
+module=`echo $project | sed 's|[.].*||'`
+
+baseline=`aegis -cd -bl`
+
+if test X${TMPDIR} = X; then TMPDIR=/var/tmp; fi
+
+TMP=${TMPDIR}/ae-cvs-ci.$$
+mkdir ${TMP}
+cd ${TMP}
+
+PWD=`pwd`
+if test X${PWD} != X${TMP}; then
+ echo "$0: ended up in ${PWD}, not ${TMP}" >&2
+ exit 1
+fi
+
+fail()
+{
+ set +x
+ cd $here
+ rm -rf ${TMP}
+ echo "FAILED" 1>&2
+ exit 1
+}
+trap "fail" 1 2 3 15
+
+Command()
+{
+ ${PRINT} "$*"
+ ${EXECUTE} "$*"
+}
+
+#
+# Create a new CVS work area.
+#
+# Note: this assumes the module is checked-out into a directory of the
+# same name. Is there a way to ask CVS where is is going to put a
+# modules, so we can always get the "cd" right?
+#
+${PRINT} cvs co $module
+${EXECUTE} cvs co $module > LOG 2>&1
+if test $? -ne 0; then cat LOG; fail; fi
+${EXECUTE} cd $module
+
+#
+# Now we need to extract the sources from Aegis and drop them into the
+# CVS work area. There are two ways to do this.
+#
+# The first way is to use the generated tarball.
+# This has the advantage that it has the Makefile.in file in it, and
+# will work immediately.
+#
+# The second way is to use aetar, which will give exact sources, and
+# omit all derived files. This will *not* include the Makefile.in,
+# and so will not be readily compilable.
+#
+# gunzip < $baseline/export/${project}.tar.gz | tardy -rp ${project} | tar xf -
+aetar -send -comp-alg=gzip -o - | tar xzf -
+
+#
+# If any new directories have been created we will need to add them
+# to CVS before we can add the new files which we know are in them,
+# or they would not have been created. Do this only if the -n option
+# isn't used, because if it is, we won't have actually checked out the
+# source and we'd erroneously report that all of them need to be added.
+#
+if test "X${EXECUTE}" != "X:"
+then
+ find . \( -name CVS -o -name Attic \) -prune -o -type d -print |
+ xargs --max-args=1 |
+ while read dir
+ do
+ if [ ! -d "$dir/CVS" ]
+ then
+ Command cvs add "$dir"
+ fi
+ done
+fi
+
+#
+# Use the Aegis meta-data to perform some CVS commands that CVS can't
+# figure out for itself.
+#
+aegis -l cf -unf | sed 's| -> [0-9][0-9.]*||' |
+while read usage action rev filename
+do
+ if test "x$filename" = "x"
+ then
+ filename="$rev"
+ fi
+ case $action in
+ create)
+ Command cvs add $filename
+ ;;
+ remove)
+ Command rm -f $filename
+ Command cvs remove $filename
+ ;;
+ *)
+ ;;
+ esac
+done
+
+#
+# Extract the brief description. We'd like to do this using aesub
+# or something, like so:
+#
+# message=`aesub '${version} - ${change description}'`
+#
+# but the expansion of ${change description} has a lame hard-coded max of
+# 80 characters, so we have to do this by hand. (This has the slight
+# benefit of preserving backslashes in front of any double-quotes in
+# the text; that will have to be handled if we go back to using aesub.)
+#
+description=`aegis -ca -l | sed -n 's/brief_description = "\(.*\)";$/\1/p'`
+version=`aesub '${version}'`
+message="$version - $description"
+
+#
+# Now commit all the changes.
+#
+Command cvs -q commit -m \"$message\"
+
+#
+# All done. Clean up and go home.
+#
+cd $here
+rm -rf ${TMP}
+exit 0
diff --git a/bin/ae-svn-ci b/bin/ae-svn-ci
new file mode 100755
index 0000000..301d890
--- /dev/null
+++ b/bin/ae-svn-ci
@@ -0,0 +1,240 @@
+#
+# aegis - project change supervisor
+# Copyright (C) 2004 Peter Miller;
+# All rights reserved.
+#
+# As a specific exception to the GPL, you are allowed to copy
+# this source file into your own project and modify it, without
+# releasing your project under the GPL, unless there is some other
+# file or condition which would require it.
+#
+# MANIFEST: shell script to commit changes to Subversion
+#
+# This script is expected to be run by the integrate_pass_notify_command
+# and as such the baseline has already assumed the shape asked for by
+# the change.
+#
+# integrate_pass_notify_command =
+# "$bin/ae-svn-ci $project $change http://svn.site.com/svn/trunk --username svn_user";
+#
+# Alternatively, you may wish to tailor this script to the individual
+# needs of your project. Make it a source file, e.g. "etc/ae-svn-ci.sh"
+# and then use the following:
+#
+# integrate_pass_notify_command =
+# "$sh ${s etc/ae-svn-ci} $project $change http://svn.site.com/svn/trunk --username svn_user";
+#
+
+USAGE="Usage: $0 [-hnq] <project> <change> <url> [<co_options>]"
+
+PRINT="echo"
+EXECUTE="eval"
+
+while getopts "hnq" FLAG
+do
+ case ${FLAG} in
+ h )
+ echo "${USAGE}"
+ exit 0
+ ;;
+ n )
+ EXECUTE=":"
+ ;;
+ q )
+ PRINT=":"
+ ;;
+ * )
+ echo "$0: unknown option ${FLAG}" >&2
+ exit 1
+ ;;
+ esac
+done
+
+shift `expr ${OPTIND} - 1`
+
+case $# in
+[012])
+ echo "${USAGE}" 1>&2
+ exit 1
+ ;;
+*)
+ project=$1
+ change=$2
+ svn_url=$3
+ shift 3
+ svn_co_flags=$*
+ ;;
+esac
+
+here=`pwd`
+
+AEGIS_PROJECT=$project
+export AEGIS_PROJECT
+AEGIS_CHANGE=$change
+export AEGIS_CHANGE
+
+module=`echo $project | sed 's|[.].*||'`
+
+baseline=`aegis -cd -bl`
+
+if test X${TMPDIR} = X; then TMPDIR=/var/tmp; fi
+
+TMP=${TMPDIR}/ae-svn-ci.$$
+mkdir ${TMP}
+cd ${TMP}
+
+PWD=`pwd`
+if test X${PWD} != X${TMP}; then
+ echo "$0: ended up in ${PWD}, not ${TMP}" >&2
+ exit 1
+fi
+
+fail()
+{
+ set +x
+ cd $here
+ rm -rf ${TMP}
+ echo "FAILED" 1>&2
+ exit 1
+}
+trap "fail" 1 2 3 15
+
+Command()
+{
+ ${PRINT} "$*"
+ ${EXECUTE} "$*"
+}
+
+#
+# Create a new Subversion work area.
+#
+# Note: this assumes the module is checked-out into a directory of the
+# same name. Is there a way to ask Subversion where it is going to put a
+# module, so we can always get the "cd" right?
+#
+${PRINT} svn co $svn_url $module $svn_co_flags
+${EXECUTE} svn co $svn_url $module $svn_co_flags > LOG 2>&1
+if test $? -ne 0; then cat LOG; fail; fi
+${EXECUTE} cd $module
+
+#
+# Now we need to extract the sources from Aegis and drop them into the
+# Subversion work area. There are two ways to do this.
+#
+# The first way is to use the generated tarball.
+# This has the advantage that it has the Makefile.in file in it, and
+# will work immediately.
+#
+# The second way is to use aetar, which will give exact sources, and
+# omit all derived files. This will *not* include the Makefile.in,
+# and so will not be readily compilable.
+#
+# gunzip < $baseline/export/${project}.tar.gz | tardy -rp ${project} | tar xf -
+aetar -send -comp-alg=gzip -o - | tar xzf -
+
+#
+# If any new directories have been created we will need to add them
+# to Subversion before we can add the new files which we know are in them,
+# or they would not have been created. Do this only if the -n option
+# isn't used, because if it is, we won't have actually checked out the
+# source and we'd erroneously report that all of them need to be added.
+#
+if test "X${EXECUTE}" != "X:"
+then
+ find . -name .svn -prune -o -type d -print |
+ xargs --max-args=1 |
+ while read dir
+ do
+ if [ ! -d "$dir/.svn" ]
+ then
+ Command svn add -N "$dir"
+ fi
+ done
+fi
+
+#
+# Use the Aegis meta-data to perform some commands that Subversion can't
+# figure out for itself. We use an inline "aer" report script to identify
+# when a remove-create pair are actually due to a move.
+#
+aegis -rpt -nph -f - <<_EOF_ |
+auto cs;
+cs = project[project_name()].state.branch.change[change_number()];
+
+columns({width = 1000;});
+
+auto file, moved;
+for (file in cs.src)
+{
+ if (file.move != "")
+ moved[file.move] = 1;
+}
+
+auto action;
+for (file in cs.src)
+{
+ if (file.action == "remove" && file.move != "")
+ action = "move";
+ else
+ action = file.action;
+ /*
+ * Suppress printing of any files created as the result of a move.
+ * These are printed as the destination when printing the line for
+ * the file that was *removed* as a result of the move.
+ */
+ if (action != "create" || ! moved[file.file_name])
+ print(sprintf("%s %s \\"%s\\" \\"%s\\"", file.usage, action, file.file_name, file.move));
+}
+_EOF_
+while read line
+do
+ eval set -- "$line"
+ usage="$1"
+ action="$2"
+ srcfile="$3"
+ dstfile="$4"
+ case $action in
+ create)
+ Command svn add $srcfile
+ ;;
+ remove)
+ Command rm -f $srcfile
+ Command svn remove $srcfile
+ ;;
+ move)
+ Command mv $dstfile $dstfile.move
+ Command svn move $srcfile $dstfile
+ Command cp $dstfile.move $dstfile
+ Command rm -f $dstfile.move
+ ;;
+ *)
+ ;;
+ esac
+done
+
+#
+# Extract the brief description. We'd like to do this using aesub
+# or something, like so:
+#
+# message=`aesub '${version} - ${change description}'`
+#
+# but the expansion of ${change description} has a lame hard-coded max of
+# 80 characters, so we have to do this by hand. (This has the slight
+# benefit of preserving backslashes in front of any double-quotes in
+# the text; that will have to be handled if we go back to using aesub.)
+#
+description=`aegis -ca -l | sed -n 's/brief_description = "\(.*\)";$/\1/p'`
+version=`aesub '${version}'`
+message="$version - $description"
+
+#
+# Now commit all the changes.
+#
+Command svn commit -m \"$message\"
+
+#
+# All done. Clean up and go home.
+#
+cd $here
+rm -rf ${TMP}
+exit 0
diff --git a/bin/ae2cvs b/bin/ae2cvs
new file mode 100644
index 0000000..e7cb22b
--- /dev/null
+++ b/bin/ae2cvs
@@ -0,0 +1,579 @@
+#! /usr/bin/env perl
+
+$revision = "src/ae2cvs.pl 0.04.D001 2005/08/14 15:13:36 knight";
+
+$copyright = "Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight.";
+
+#
+# All rights reserved. This program is free software; you can
+# redistribute and/or modify under the same terms as Perl itself.
+#
+
+use strict;
+use File::Find;
+use File::Spec;
+use Pod::Usage ();
+
+use vars qw( @add_list @args @cleanup @copy_list @libraries
+ @mkdir_list @remove_list
+ %seen_dir
+ $ae_copy $aedir $aedist
+ $cnum $comment $commit $common $copyright
+ $cvs_command $cvsmod $cvsroot
+ $delta $description $exec $help $indent $infile
+ $proj $pwd $quiet $revision
+ $summary $usedir $usepath );
+
+$aedist = 1;
+$cvsroot = undef;
+$exec = undef;
+$indent = "";
+
+sub version {
+ print "ae2cvs: $revision\n";
+ print "$copyright\n";
+ exit 0;
+}
+
+{
+ use Getopt::Long;
+
+ Getopt::Long::Configure('no_ignore_case');
+
+ my $ret = GetOptions (
+ "aedist" => sub { $aedist = 1 },
+ "aegis" => sub { $aedist = 0 },
+ "change=i" => \$cnum,
+ "d=s" => \$cvsroot,
+ "file=s" => \$infile,
+ "help|?" => \$help,
+ "library=s" => \@libraries,
+ "module=s" => \$cvsmod,
+ "noexecute" => sub { $exec = 0 },
+ "project=s" => \$proj,
+ "quiet" => \$quiet,
+ "usedir=s" => \$usedir,
+ "v|version" => \&version,
+ "x|execute" => sub { $exec++ if ! defined $exec || $exec != 0 },
+ "X|EXECUTE" => sub { $exec = 2 if ! defined $exec || $exec != 0 },
+ );
+
+ Pod::Usage::pod2usage(-verbose => 0) if $help || ! $ret;
+
+ $exec = 0 if ! defined $exec;
+}
+
+$cvs_command = $cvsroot ? "cvs -d $cvsroot -Q" : "cvs -Q";
+
+#
+# Wrap up the $quiet logic in one place.
+#
+sub printit {
+ return if $quiet;
+ my $string = join('', @_);
+ $string =~ s/^/$indent/msg if $indent;
+ print $string;
+}
+
+#
+# Wrappers for executing various builtin Perl functions in
+# accordance with the -n, -q and -x options.
+#
+sub execute {
+ my $cmd = shift;
+ printit "$cmd\n";
+ if (! $exec) {
+ return 1;
+ }
+ ! system($cmd);
+}
+
+sub _copy {
+ my ($source, $dest) = @_;
+ printit "cp $source $dest\n";
+ if ($exec) {
+ use File::Copy;
+ copy($source, $dest);
+ }
+}
+
+sub _chdir {
+ my $dir = shift;
+ printit "cd $dir\n";
+ if ($exec) {
+ chdir($dir) || die "ae2cvs: could not chdir($dir): $!";
+ }
+}
+
+sub _mkdir {
+ my $dir = shift;
+ printit "mkdir $dir\n";
+ if ($exec) {
+ mkdir($dir);
+ }
+}
+
+#
+# Put some input data through an external filter and capture the output.
+#
+sub filter {
+ my ($cmd, $input) = @_;
+
+ use FileHandle;
+ use IPC::Open2;
+
+ my $pid = open2(*READ, *WRITE, $cmd) || die "Cannot exec '$cmd': $!\n";
+ print WRITE $input;
+ close(WRITE);
+ my $output = join('', <READ>);
+ close(READ);
+ return $output;
+}
+
+#
+# Parse a change description, in both 'aegis -l cd" and "aedist" formats.
+#
+# Returns an array containing the project name, the change number
+# (if any), the delta number (if any), the SUMMARY, the DESCRIPTION
+# and the lines describing the files in the change.
+#
+sub parse_change {
+ my $output = shift;
+
+ my ($p, $c, $d, $c_or_d, $sum, $desc, $filesection, @flines);
+
+ # The project name line comes after NAME in "aegis -l cd" format,
+ # and PROJECT in "aedist" format. In both cases, the project name
+ # and the change/delta name are separated a comma.
+ ($p = $output) =~ s/(?:NAME|PROJECT)\n([^\n]*)\n.*/$1/ms;
+ ($p, $c_or_d) = (split(/,/, $p));
+
+ # In "aegis -l cd" format, the project name actually comes after
+ # the string "Project" and is itself enclosed in double quotes.
+ $p =~ s/Project "([^"]*)"/$1/;
+
+ # The change or delta string was the right-hand side of the comma.
+ # "aegis -l cd" format spells it "Change 123." or "Delta 123." while
+ # "aedist" format spells it "change 123."
+ if ($c_or_d =~ /\s*[Cc]hange (\d+).*/) { $c = $1 };
+ if ($c_or_d =~ /\s*[Dd]elta (\d+).*/) { $d = $1 };
+
+ # The SUMMARY line is always followed the DESCRIPTION section.
+ # It seems to always be a single line, but we grab everything in
+ # between just in case.
+ ($sum = $output) =~ s/.*\nSUMMARY\n//ms;
+ $sum =~ s/\nDESCRIPTION\n.*//ms;
+
+ # The DESCRIPTION section is followed ARCHITECTURE in "aegis -l cd"
+ # format and by CAUSE in "aedist" format. Explicitly under it if the
+ # string is only "none," which means they didn't supply a description.
+ ($desc = $output) =~ s/.*\nDESCRIPTION\n//ms;
+ $desc =~ s/\n(ARCHITECTURE|CAUSE)\n.*//ms;
+ chomp($desc);
+ if ($desc eq "none" || $desc eq "none\n") { $desc = undef }
+
+ # The FILES section is followed by HISTORY in "aegis -l cd" format.
+ # It seems to be the last section in "aedist" format, but stripping
+ # a non-existent HISTORY section doesn't hurt.
+ ($filesection = $output) =~ s/.*\nFILES\n//ms;
+ $filesection =~ s/\nHISTORY\n.*//ms;
+
+ @flines = split(/\n/, $filesection);
+
+ ($p, $c, $d, $sum, $desc, \@flines)
+}
+
+#
+#
+#
+$pwd = Cwd::cwd();
+
+#
+# Fetch the file list either from our aedist input
+# or directly from the project itself.
+#
+my @filelines;
+if ($aedist) {
+ local ($/);
+ undef $/;
+ my $infile_redir = "";
+ my $contents;
+ if (! $infile || $infile eq "-") {
+ $contents = join('', <STDIN>);
+ } else {
+ open(FILE, "<$infile") || die "Cannot open '$infile': $!\n";
+ binmode(FILE);
+ $contents = join('', <FILE>);
+ close(FILE);
+ if (! File::Spec->file_name_is_absolute($infile)) {
+ $infile = File::Spec->catfile($pwd, $infile);
+ }
+ $infile_redir = " < $infile";
+ }
+
+ my $output = filter("aedist -l -unf", $contents);
+ my ($p, $c, $d, $s, $desc, $fl) = parse_change($output);
+
+ $proj = $p if ! defined $proj;
+ $summary = $s;
+ $description = $desc;
+ @filelines = @$fl;
+
+ if (! $exec) {
+ printit qq(MYTMP="/tmp/ae2cvs-ae.\$\$"\n),
+ qq(mkdir \$MYTMP\n),
+ qq(cd \$MYTMP\n);
+ printit q(perl -MMIME::Base64 -e 'undef $/; ($c = <>) =~ s/.*\n\n//ms; print decode_base64($c)'),
+ $infile_redir,
+ qq( | zcat),
+ qq( | cpio -i -d --quiet\n);
+ $aedir = '$MYTMP';
+ push(@cleanup, $aedir);
+ } else {
+ $aedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs-ae.$$");
+ _mkdir($aedir);
+ push(@cleanup, $aedir);
+ _chdir($aedir);
+
+ use MIME::Base64;
+
+ $contents =~ s/.*\n\n//ms;
+ $contents = filter("zcat", decode_base64($contents));
+
+ open(CPIO, "|cpio -i -d --quiet");
+ print CPIO $contents;
+ close(CPIO);
+ }
+
+ $ae_copy = sub {
+ foreach my $dest (@_) {
+ my $source = File::Spec->catfile($aedir, "src", $dest);
+ execute(qq(cp $source $dest));
+ }
+ }
+} else {
+ $cnum = $ENV{AEGIS_CHANGE} if ! defined $cnum;
+ $proj = $ENV{AEGIS_PROJECT} if ! defined $proj;
+
+ $common = "-lib " . join(" -lib ", @libraries) if @libraries;
+ $common = "$common -proj $proj" if $proj;
+
+ my $output = `aegis -l cd $cnum -unf $common`;
+ my ($p, $c, $d, $s, $desc, $fl) = parse_change($output);
+
+ $delta = $d;
+ $summary = $s;
+ $description = $desc;
+ @filelines = @$fl;
+
+ if (! $delta) {
+ print STDERR "ae2cvs: No delta number, exiting.\n";
+ exit 1;
+ }
+
+ $ae_copy = sub {
+ execute(qq(aegis -cp -ind -delta $delta $common @_));
+ }
+}
+
+if (! $usedir) {
+ $usedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs.$$");
+ _mkdir($usedir);
+ push(@cleanup, $usedir);
+}
+
+_chdir($usedir);
+
+$usepath = $usedir;
+if (! File::Spec->file_name_is_absolute($usepath)) {
+ $usepath = File::Spec->catfile($pwd, $usepath);
+}
+
+if (! -d File::Spec->catfile($usedir, "CVS")) {
+ $cvsmod = (split(/\./, $proj))[0] if ! defined $cvsmod;
+
+ execute(qq($cvs_command co $cvsmod));
+
+ _chdir($cvsmod);
+
+ $usepath = File::Spec->catfile($usepath, $cvsmod);
+}
+
+#
+# Figure out what we have to do to accomplish everything.
+#
+foreach (@filelines) {
+ my @arr = split(/\s+/, $_);
+ my $type = shift @arr; # source / test
+ my $act = shift @arr; # modify / create
+ my $file = pop @arr;
+
+ if ($act eq "create" or $act eq "modify") {
+ # XXX Do we really only need to do this for
+ # ($act eq "create") files?
+ my (undef, $dirs, undef) = File::Spec->splitpath($file);
+ my $absdir = $usepath;
+ my $reldir;
+ my $d;
+ foreach $d (File::Spec->splitdir($dirs)) {
+ next if ! $d;
+ $absdir = File::Spec->catdir($absdir, $d);
+ $reldir = $reldir ? File::Spec->catdir($reldir, $d) : $d;
+ if (! -d $absdir && ! $seen_dir{$reldir}) {
+ $seen_dir{$reldir} = 1;
+ push(@mkdir_list, $reldir);
+ }
+ }
+
+ push(@copy_list, $file);
+
+ if ($act eq "create") {
+ push(@add_list, $file);
+ }
+ } elsif ($act eq "remove") {
+ push(@remove_list, $file);
+ } else {
+ print STDERR "Unsure how to '$act' the '$file' file.\n";
+ }
+}
+
+# Now go through and mkdir() the directories,
+# adding them to the CVS tree as we do.
+if (@mkdir_list) {
+ if (! $exec) {
+ printit qq(# The following "mkdir" and "cvs -Q add" calls are not\n),
+ qq(# necessary for any directories that already exist in the\n),
+ qq(# CVS tree but which aren't present locally.\n);
+ }
+ foreach (@mkdir_list) {
+ if (! $exec) {
+ printit qq(if test ! -d $_; then\n);
+ $indent = " ";
+ }
+ _mkdir($_);
+ execute(qq($cvs_command add $_));
+ if (! $exec) {
+ $indent = "";
+ printit qq(fi\n);
+ }
+ }
+ if (! $exec) {
+ printit qq(# End of directory creation.\n);
+ }
+}
+
+# Copy in any files in the change, before we try to "cvs add" them.
+$ae_copy->(@copy_list) if @copy_list;
+
+if (@add_list) {
+ execute(qq($cvs_command add @add_list));
+}
+
+if (@remove_list) {
+ execute(qq(rm -f @remove_list));
+ execute(qq($cvs_command remove @remove_list));
+}
+
+# Last, commit the whole bunch.
+$comment = $summary;
+$comment .= "\n" . $description if $description;
+$commit = qq($cvs_command commit -m '$comment' .);
+if ($exec == 1) {
+ printit qq(# Execute the following to commit the changes:\n),
+ qq(# $commit\n);
+} else {
+ execute($commit);
+}
+
+_chdir($pwd);
+
+#
+# Directory cleanup.
+#
+sub END {
+ my $dir;
+ foreach $dir (@cleanup) {
+ printit "rm -rf $dir\n";
+ if ($exec) {
+ finddepth(sub {
+ # print STDERR "unlink($_)\n" if (!-d $_);
+ # print STDERR "rmdir($_)\n" if (-d $_ && $_ ne ".");
+ unlink($_) if (!-d $_);
+ rmdir($_) if (-d $_ && $_ ne ".");
+ 1;
+ }, $dir);
+ rmdir($dir) || print STDERR "Could not remove $dir: $!\n";
+ }
+ }
+}
+
+__END__;
+
+=head1 NAME
+
+ae2cvs - convert an Aegis change set to CVS commands
+
+=head1 SYNOPSIS
+
+ae2cvs [-aedist|-aegis] [-c change] [-d cvs_root] [-f file] [-l lib]
+ [-m module] [-n] [-p proj] [-q] [-u dir] [-v] [-x] [-X]
+
+ -aedist use aedist format from input (default)
+ -aegis query aegis repository directly
+ -c change change number
+ -d cvs_root CVS root directory
+ -f file read aedist from file ('-' == stdin)
+ -l lib Aegis library directory
+ -m module CVS module
+ -n no execute
+ -p proj project name
+ -q quiet, don't print commands
+ -u dir use dir for CVS checkin
+ -v print version string and exit
+ -x execute the commands, but don't commit;
+ two or more -x options commit changes
+ -X execute the commands and commit changes
+
+=head1 DESCRIPTION
+
+The C<ae2cvs> utility can convert an Aegis change into a set of CVS (and
+other) commands to make the corresponding change(s) to a carbon-copy CVS
+repository. This can be used to keep a front-end CVS repository in sync
+with changes made to an Aegis project, either manually or automatically
+using the C<integrate_pass_notify_command> attribute of the Aegis
+project.
+
+By default, C<ae2cvs> makes no changes to any software, and only prints
+out the necessary commands. These commands can be examined first for
+safety, and then fed to any Bourne shell variant (sh, ksh, or bash) to
+make the actual CVS changes.
+
+An option exists to have C<ae2cvs> execute the commands directly.
+
+=head1 OPTIONS
+
+The C<ae2cvs> utility supports the following options:
+
+=over 4
+
+=item -aedist
+
+Reads an aedist change set.
+By default, the change set is read from standard input,
+or a file specified with the C<-f> option.
+
+=item -aegis
+
+Reads the change directly from the Aegis repository
+by executing the proper C<aegis> commands.
+
+=item -c change
+
+Specify the Aegis change number to be used.
+The value of the C<AEGIS_CHANGE> environment variable
+is used by default.
+
+=item -d cvsroot
+
+Specify the CVS root directory to be used.
+This option is passed explicitly to each executed C<cvs> command.
+The default behavior is to omit any C<-d> options
+and let the executed C<cvs> commands use the
+C<CVSROOT> environment variable as they normally would.
+
+=item -f file
+
+Reads the aedist change set from the specified C<file>,
+or from standard input if C<file> is C<'-'>.
+
+=item -l lib
+
+Specifies an Aegis library directory to be searched for global states
+files and user state files.
+
+=item -m module
+
+Specifies the name of the CVS module to be brought up-to-date.
+The default is to use the Aegis project name,
+minus any branch numbers;
+for example, given an Aegis project name of C<foo-cmd.0.1>,
+the default CVS module name is C<foo-cmd>.
+
+=item -n
+
+No execute. Commands are printed (including a command for a final
+commit of changes), but not executed. This is the default.
+
+=item -p proj
+
+Specifies the name of the Aegis project from which this change is taken.
+The value of the C<AEGIS_PROJECT> environment variable
+is used by default.
+
+=item -q
+
+Quiet. Commands are not printed.
+
+=item -u dir
+
+Use the already checked-out CVS tree that exists at C<dir>
+for the checkins and commits.
+The default is to use a separately-created temporary directory.
+
+=item -v
+
+Print the version string and exit.
+
+=item -x
+
+Execute the commands to bring the CVS repository up to date,
+except for the final commit of the changes. Two or more
+C<-x> options will cause the change to be committed.
+
+=item -X
+
+Execute the commands to bring the CVS repository up to date,
+including the final commit of the changes.
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+=over 4
+
+=item AE2CVS_FLAGS
+
+Specifies any options to be used to initialize
+the C<ae2cvs> utility.
+Options on the command line override these values.
+
+=back
+
+=head1 AUTHOR
+
+Steven Knight (knight at baldmt dot com)
+
+=head1 BUGS
+
+If errors occur during the execution of the Aegis or CVS commands, and
+the -X option is used, a partial change (consisting of those files for
+which the command(s) succeeded) will be committed. It would be safer to
+generate code to detect the error and print a warning.
+
+When a file has been deleted in Aegis, the standard whiteout file can
+cause a regex failure in this script. It doesn't necessarily happen all
+the time, though, so this needs more investigation.
+
+=head1 TODO
+
+Add an explicit test for using ae2cvs in the Aegis
+integrate_pass_notify_command field to support fully keeping a
+repository in sync automatically.
+
+=head1 COPYRIGHT
+
+Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight.
+
+=head1 SEE ALSO
+
+aegis(1), cvs(1)
diff --git a/bin/calibrate.py b/bin/calibrate.py
new file mode 100644
index 0000000..c5d45ce
--- /dev/null
+++ b/bin/calibrate.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import optparse
+import os
+import re
+import subprocess
+import sys
+
+variable_re = re.compile('^VARIABLE: (.*)$', re.M)
+elapsed_re = re.compile('^ELAPSED: (.*)$', re.M)
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ parser = optparse.OptionParser(usage="calibrate.py [-h] [-p PACKAGE], [--min time] [--max time] timings/*/*-run.py")
+ parser.add_option('--min', type='float', default=9.5,
+ help="minimum acceptable execution time (default 9.5)")
+ parser.add_option('--max', type='float', default=10.00,
+ help="maximum acceptable execution time (default 10.00)")
+ parser.add_option('-p', '--package', type="string",
+ help="package type")
+ opts, args = parser.parse_args(argv[1:])
+
+ os.environ['TIMESCONS_CALIBRATE'] = '1'
+
+ for arg in args:
+ if len(args) > 1:
+ print arg + ':'
+
+ command = [sys.executable, 'runtest.py', '--noqmtest']
+ if opts.package:
+ command.extend(['-p', opts.package])
+ command.append(arg)
+
+ run = 1
+ good = 0
+ while good < 3:
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output = p.communicate()[0]
+ vm = variable_re.search(output)
+ em = elapsed_re.search(output)
+ elapsed = float(em.group(1))
+ print "run %3d: %7.3f: %s" % (run, elapsed, ' '.join(vm.groups()))
+ if opts.min < elapsed and elapsed < opts.max:
+ good += 1
+ else:
+ good = 0
+ for v in vm.groups():
+ var, value = v.split('=', 1)
+ value = int((int(value) * opts.max) / elapsed)
+ os.environ[var] = str(value)
+ run += 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/bin/caller-tree.py b/bin/caller-tree.py
new file mode 100644
index 0000000..85bb599
--- /dev/null
+++ b/bin/caller-tree.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+#
+# Quick script to process the *summary* output from SCons.Debug.caller()
+# and print indented calling trees with call counts.
+#
+# The way to use this is to add something like the following to a function
+# for which you want information about who calls it and how many times:
+#
+# from SCons.Debug import caller
+# caller(0, 1, 2, 3, 4, 5)
+#
+# Each integer represents how many stack frames back SCons will go
+# and capture the calling information, so in the above example it will
+# capture the calls six levels up the stack in a central dictionary.
+#
+# At the end of any run where SCons.Debug.caller() is used, SCons will
+# print a summary of the calls and counts that looks like the following:
+#
+# Callers of Node/__init__.py:629(calc_signature):
+# 1 Node/__init__.py:683(calc_signature)
+# Callers of Node/__init__.py:676(gen_binfo):
+# 6 Node/FS.py:2035(current)
+# 1 Node/__init__.py:722(get_bsig)
+#
+# If you cut-and-paste that summary output and feed it to this script
+# on standard input, it will figure out how these entries hook up and
+# print a calling tree for each one looking something like:
+#
+# Node/__init__.py:676(gen_binfo)
+# Node/FS.py:2035(current) 6
+# Taskmaster.py:253(make_ready_current) 18
+# Script/Main.py:201(make_ready) 18
+#
+# Note that you should *not* look at the call-count numbers in the right
+# hand column as the actual number of times each line *was called by*
+# the function on the next line. Rather, it's the *total* number
+# of times each function was found in the call chain for any of the
+# calls to SCons.Debug.caller(). If you're looking at more than one
+# function at the same time, for example, their counts will intermix.
+# So use this to get a *general* idea of who's calling what, not for
+# fine-grained performance tuning.
+
+import sys
+
+class Entry:
+ def __init__(self, file_line_func):
+ self.file_line_func = file_line_func
+ self.called_by = []
+ self.calls = []
+
+AllCalls = {}
+
+def get_call(flf):
+ try:
+ e = AllCalls[flf]
+ except KeyError:
+ e = AllCalls[flf] = Entry(flf)
+ return e
+
+prefix = 'Callers of '
+
+c = None
+for line in sys.stdin.readlines():
+ if line[0] == '#':
+ pass
+ elif line[:len(prefix)] == prefix:
+ c = get_call(line[len(prefix):-2])
+ else:
+ num_calls, flf = line.strip().split()
+ e = get_call(flf)
+ c.called_by.append((e, num_calls))
+ e.calls.append(c)
+
+stack = []
+
+def print_entry(e, level, calls):
+ print '%-72s%6s' % ((' '*2*level) + e.file_line_func, calls)
+ if e in stack:
+ print (' '*2*(level+1))+'RECURSION'
+ print
+ elif e.called_by:
+ stack.append(e)
+ for c in e.called_by:
+ print_entry(c[0], level+1, c[1])
+ stack.pop()
+ else:
+ print
+
+for e in [ e for e in AllCalls.values() if not e.calls ]:
+ print_entry(e, 0, '')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/docdiff b/bin/docdiff
new file mode 100644
index 0000000..c565a04
--- /dev/null
+++ b/bin/docdiff
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if test $# -eq 0; then
+ for f in doc/user/*.in; do
+ xml=doc/user/`basename $f .in`.xml
+ echo $f:
+ python bin/sconsoutput.py $f | diff $DIFFFLAGS $xml -
+ done
+else
+ for a in $*; do
+ f=doc/user/$a.in
+ xml=doc/user/$a.xml
+ echo $f:
+ python bin/sconsoutput.py $f | diff $DIFFFLAGS $xml -
+ done
+fi
diff --git a/bin/docrun b/bin/docrun
new file mode 100644
index 0000000..5ba4215
--- /dev/null
+++ b/bin/docrun
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if test $# -eq 0; then
+ for f in doc/user/*.in; do
+ xml=doc/user/`basename $f .in`.xml
+ echo $f:
+ python bin/sconsoutput.py $f
+ done
+else
+ for a in $*; do
+ f=doc/user/$a.in
+ xml=doc/user/$a.xml
+ echo $f:
+ python bin/sconsoutput.py $f
+ done
+fi
diff --git a/bin/docupdate b/bin/docupdate
new file mode 100644
index 0000000..0e1631b
--- /dev/null
+++ b/bin/docupdate
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if test $# -eq 0; then
+ for f in doc/user/*.in; do
+ xml=doc/user/`basename $f .in`.xml
+ echo $f:
+ python bin/sconsoutput.py $f > $xml
+ done
+else
+ for a in $*; do
+ f=doc/user/$a.in
+ xml=doc/user/$a.xml
+ echo $f:
+ python bin/sconsoutput.py $f > $xml
+ done
+fi
diff --git a/bin/files b/bin/files
new file mode 100644
index 0000000..08b1caa
--- /dev/null
+++ b/bin/files
@@ -0,0 +1,106 @@
+./SCons/Action.py
+./SCons/Builder.py
+./SCons/Conftest.py
+./SCons/Debug.py
+./SCons/Defaults.py
+./SCons/Environment.py
+./SCons/Errors.py
+./SCons/Executor.py
+./SCons/Job.py
+./SCons/Node/Alias.py
+./SCons/Node/FS.py
+./SCons/Node/Python.py
+./SCons/Node/__init__.py
+./SCons/Options/__init__.py
+./SCons/Options/BoolOption.py
+./SCons/Options/EnumOption.py
+./SCons/Options/ListOption.py
+./SCons/Options/PackageOption.py
+./SCons/Options/PathOption.py
+./SCons/Platform/__init__.py
+./SCons/Platform/aix.py
+./SCons/Platform/cygwin.py
+./SCons/Platform/hpux.py
+./SCons/Platform/irix.py
+./SCons/Platform/os2.py
+./SCons/Platform/posix.py
+./SCons/Platform/sunos.py
+./SCons/Platform/win32.py
+./SCons/Scanner/C.py
+./SCons/Scanner/D.py
+./SCons/Scanner/Fortran.py
+./SCons/Scanner/IDL.py
+./SCons/Scanner/Prog.py
+./SCons/Scanner/__init__.py
+./SCons/Script/SConscript.py
+./SCons/Script/__init__.py
+./SCons/Sig/MD5.py
+./SCons/Sig/TimeStamp.py
+./SCons/Sig/__init__.py
+./SCons/Taskmaster.py
+./SCons/Tool/__init__.py
+./SCons/Tool/aixc++.py
+./SCons/Tool/aixcc.py
+./SCons/Tool/aixf77.py
+./SCons/Tool/aixlink.py
+./SCons/Tool/ar.py
+./SCons/Tool/as.py
+./SCons/Tool/bcc32.py
+./SCons/Tool/c++.py
+./SCons/Tool/cc.py
+./SCons/Tool/CVS.py
+./SCons/Tool/dmd.py
+./SCons/Tool/default.py
+./SCons/Tool/dvipdf.py
+./SCons/Tool/dvips.py
+./SCons/Tool/f77.py
+./SCons/Tool/g++.py
+./SCons/Tool/g77.py
+./SCons/Tool/gas.py
+./SCons/Tool/gcc.py
+./SCons/Tool/gnulink.py
+./SCons/Tool/hpc++.py
+./SCons/Tool/hpcc.py
+./SCons/Tool/hplink.py
+./SCons/Tool/icc.py
+./SCons/Tool/icl.py
+./SCons/Tool/ifl.py
+./SCons/Tool/ilink.py
+./SCons/Tool/ilink32.py
+./SCons/Tool/jar.py
+./SCons/Tool/javac.py
+./SCons/Tool/JavaCommon.py
+./SCons/Tool/javah.py
+./SCons/Tool/latex.py
+./SCons/Tool/lex.py
+./SCons/Tool/link.py
+./SCons/Tool/m4.py
+./SCons/Tool/masm.py
+./SCons/Tool/midl.py
+./SCons/Tool/mingw.py
+./SCons/Tool/mslib.py
+./SCons/Tool/mslink.py
+./SCons/Tool/msvc.py
+./SCons/Tool/msvs.py
+./SCons/Tool/nasm.py
+./SCons/Tool/pdflatex.py
+./SCons/Tool/pdftex.py
+./SCons/Tool/qt.py
+./SCons/Tool/rmic.py
+./SCons/Tool/sgiar.py
+./SCons/Tool/sgic++.py
+./SCons/Tool/sgicc.py
+./SCons/Tool/sgilink.py
+./SCons/Tool/sunar.py
+./SCons/Tool/sunc++.py
+./SCons/Tool/suncc.py
+./SCons/Tool/sunlink.py
+./SCons/Tool/swig.py
+./SCons/Tool/tar.py
+./SCons/Tool/tex.py
+./SCons/Tool/tlib.py
+./SCons/Tool/yacc.py
+./SCons/Util.py
+./SCons/Warnings.py
+./SCons/__init__.py
+./SCons/exitfuncs.py
diff --git a/bin/import-test.py b/bin/import-test.py
new file mode 100644
index 0000000..473a7ed
--- /dev/null
+++ b/bin/import-test.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# tree2test.py - turn a directory tree into TestSCons code
+#
+# A quick script for importing directory hierarchies containing test
+# cases that people supply (typically in a .zip or .tar.gz file) into a
+# TestSCons.py script. No error checking or options yet, it just walks
+# the first command-line argument (assumed to be the directory containing
+# the test case) and spits out code looking like the following:
+#
+# test.subdir(['sub1'],
+# ['sub1', 'sub2'])
+#
+# test.write(['sub1', 'file1'], """\
+# contents of file1
+# """)
+#
+# test.write(['sub1', 'sub2', 'file2'], """\
+# contents of file2
+# """)
+#
+# There's no massaging of contents, so any files that themselves contain
+# """ triple-quotes will need to have their contents edited by hand.
+#
+
+__revision__ = "bin/import-test.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import sys
+
+directory = sys.argv[1]
+
+Top = None
+TopPath = None
+
+class Dir:
+ def __init__(self, path):
+ self.path = path
+ self.entries = {}
+ def call_for_each_entry(self, func):
+ entries = self.entries
+ names = entries.keys()
+ names.sort()
+ for name in names:
+ func(name, entries[name])
+
+def lookup(dirname):
+ global Top, TopPath
+ if not Top:
+ Top = Dir([])
+ TopPath = dirname + os.sep
+ return Top
+ dirname = dirname.replace(TopPath, '')
+ dirs = dirname.split(os.sep)
+ t = Top
+ for d in dirs[:-1]:
+ t = t.entries[d]
+ node = t.entries[dirs[-1]] = Dir(dirs)
+ return node
+
+def make_nodes(arg, dirname, fnames):
+ dir = lookup(dirname)
+ for f in fnames:
+ dir.entries[f] = None
+
+def collect_dirs(l, dir):
+ if dir.path:
+ l.append(dir.path)
+ def recurse(n, d):
+ if d:
+ collect_dirs(l, d)
+ dir.call_for_each_entry(recurse)
+
+def print_files(dir):
+ def print_a_file(n, d):
+ if not d:
+ l = dir.path + [n]
+ sys.stdout.write('\ntest.write(%s, """\\\n' % l)
+ p = os.path.join(*([directory] + l))
+ sys.stdout.write(open(p, 'r').read())
+ sys.stdout.write('""")\n')
+ dir.call_for_each_entry(print_a_file)
+
+ def recurse(n, d):
+ if d:
+ print_files(d)
+ dir.call_for_each_entry(recurse)
+
+os.path.walk(directory, make_nodes, None)
+
+subdir_list = []
+collect_dirs(subdir_list, Top)
+subdir_list = [ str(l) for l in subdir_list ]
+sys.stdout.write('test.subdir(' + ',\n '.join(subdir_list) + ')\n')
+
+print_files(Top)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/install_python.py b/bin/install_python.py
new file mode 100644
index 0000000..ca1c8b7
--- /dev/null
+++ b/bin/install_python.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+#
+# A script for unpacking and installing different historic versions of
+# Python in a consistent manner for side-by-side development testing.
+#
+# This was written for a Linux system (specifically Ubuntu) but should
+# be reasonably generic to any POSIX-style system with a /usr/local
+# hierarchy.
+
+import getopt
+import os
+import shutil
+import sys
+
+from Command import CommandRunner, Usage
+
+all_versions = [
+ #'1.5.2', # no longer available at python.org
+ '2.0.1',
+ '2.1.3',
+ '2.2',
+ '2.3.7',
+ '2.4.5',
+ #'2.5.2',
+ '2.6',
+]
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ all = False
+ downloads_dir = 'Downloads'
+ downloads_url = 'http://www.python.org/ftp/python'
+ sudo = 'sudo'
+ prefix = '/usr/local'
+
+ short_options = 'ad:hnp:q'
+ long_options = ['all', 'help', 'no-exec', 'prefix=', 'quiet']
+
+ helpstr = """\
+Usage: install_python.py [-ahnq] [-d DIR] [-p PREFIX] [VERSION ...]
+
+ -a, --all Install all SCons versions.
+ -d DIR, --downloads=DIR Downloads directory.
+ -h, --help Print this help and exit
+ -n, --no-exec No execute, just print command lines
+ -p PREFIX, --prefix=PREFIX Installation prefix.
+ -q, --quiet Quiet, don't print command lines
+"""
+
+ try:
+ try:
+ opts, args = getopt.getopt(argv[1:], short_options, long_options)
+ except getopt.error, msg:
+ raise Usage(msg)
+
+ for o, a in opts:
+ if o in ('-a', '--all'):
+ all = True
+ elif o in ('-d', '--downloads'):
+ downloads_dir = a
+ elif o in ('-h', '--help'):
+ print helpstr
+ sys.exit(0)
+ elif o in ('-n', '--no-exec'):
+ CommandRunner.execute = CommandRunner.do_not_execute
+ elif o in ('-p', '--prefix'):
+ prefix = a
+ elif o in ('-q', '--quiet'):
+ CommandRunner.display = CommandRunner.do_not_display
+ except Usage, err:
+ sys.stderr.write(str(err.msg) + '\n')
+ sys.stderr.write('use -h to get help\n')
+ return 2
+
+ if all:
+ if args:
+ msg = 'install-scons.py: -a and version arguments both specified'
+ sys.stderr.write(msg)
+ sys.exit(1)
+
+ args = all_versions
+
+ cmd = CommandRunner()
+
+ for version in args:
+ python = 'Python-' + version
+ tar_gz = os.path.join(downloads_dir, python + '.tgz')
+ tar_gz_url = os.path.join(downloads_url, version, python + '.tgz')
+
+ if (version.startswith('1.5') or
+ version.startswith('1.6') or
+ version.startswith('2.0')):
+
+ configureflags = '--with-threads'
+
+ else:
+
+ configureflags = ''
+
+ cmd.subst_dictionary(locals())
+
+ if not os.path.exists(tar_gz):
+ if not os.path.exists(downloads_dir):
+ cmd.run('mkdir %(downloads_dir)s')
+ cmd.run('wget -O %(tar_gz)s %(tar_gz_url)s')
+
+ cmd.run('tar zxf %(tar_gz)s')
+
+ cmd.run('cd %(python)s')
+
+ if (version.startswith('1.6') or
+ version.startswith('2.0')):
+
+ def edit_modules_setup_in():
+ content = open('Modules/Setup.in', 'r').read()
+ content = content.replace('\n#zlib', '\nzlib')
+ open('Modules/Setup.in', 'w').write(content)
+
+ display = 'ed Modules/Setup.in <<EOF\ns/^#zlib/zlib/\nw\nq\nEOF\n'
+ cmd.run((edit_modules_setup_in,), display)
+
+ cmd.run('./configure --prefix=%(prefix)s %(configureflags)s 2>&1 | tee configure.out')
+ cmd.run('make 2>&1 | tee make.out')
+ cmd.run('%(sudo)s make install')
+
+ cmd.run('%(sudo)s rm -f %(prefix)s/bin/{idle,pydoc,python,python-config,smtpd.py}')
+
+ cmd.run('cd ..')
+
+ cmd.run((shutil.rmtree, python), 'rm -rf %(python)s')
+
+if __name__ == "__main__":
+ sys.exit(main())
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/install_scons.py b/bin/install_scons.py
new file mode 100644
index 0000000..e4e6aff
--- /dev/null
+++ b/bin/install_scons.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python
+#
+# A script for unpacking and installing different historic versions of
+# SCons in a consistent manner for side-by-side development testing.
+#
+# This abstracts the changes we've made to the SCons setup.py scripts in
+# different versions so that, no matter what version is specified, it ends
+# up installing the necessary script(s) and library into version-specific
+# names that won't interfere with other things.
+#
+# By default, we expect to extract the .tar.gz files from a Downloads
+# subdirectory in the current directory.
+#
+# Note that this script cleans up after itself, removing the extracted
+# directory in which we do the build.
+#
+# This was written for a Linux system (specifically Ubuntu) but should
+# be reasonably generic to any POSIX-style system with a /usr/local
+# hierarchy.
+
+import getopt
+import os
+import shutil
+import sys
+
+from Command import CommandRunner, Usage
+
+all_versions = [
+ '0.01',
+ '0.02',
+ '0.03',
+ '0.04',
+ '0.05',
+ '0.06',
+ '0.07',
+ '0.08',
+ '0.09',
+ '0.10',
+ '0.11',
+ '0.12',
+ '0.13',
+ '0.14',
+ '0.90',
+ '0.91',
+ '0.92',
+ '0.93',
+ '0.94',
+ #'0.94.1',
+ '0.95',
+ #'0.95.1',
+ '0.96',
+ '0.96.1',
+ '0.96.90',
+ '0.96.91',
+ '0.96.92',
+ '0.96.93',
+ '0.96.94',
+ '0.96.95',
+ '0.96.96',
+ '0.97',
+ '0.97.0d20070809',
+ '0.97.0d20070918',
+ '0.97.0d20071212',
+ '0.98.0',
+ '0.98.1',
+ '0.98.2',
+ '0.98.3',
+ '0.98.4',
+ '0.98.5',
+ '1.0.0',
+ '1.0.0.d20080826',
+ '1.0.1',
+ '1.0.1.d20080915',
+ '1.0.1.d20081001',
+ '1.1.0',
+ '1.1.0.d20081104',
+ '1.1.0.d20081125',
+ '1.1.0.d20081207',
+ '1.2.0',
+ '1.2.0.d20090113',
+ '1.2.0.d20090223',
+]
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ all = False
+ downloads_dir = 'Downloads'
+ downloads_url = 'http://downloads.sourceforge.net/scons'
+ sudo = 'sudo'
+ prefix = '/usr/local'
+ python = sys.executable
+
+ short_options = 'ad:hnp:q'
+ long_options = ['all', 'help', 'no-exec', 'prefix=', 'quiet']
+
+ helpstr = """\
+Usage: install_scons.py [-ahnq] [-d DIR] [-p PREFIX] [VERSION ...]
+
+ -a, --all Install all SCons versions.
+ -d DIR, --downloads=DIR Downloads directory.
+ -h, --help Print this help and exit
+ -n, --no-exec No execute, just print command lines
+ -p PREFIX, --prefix=PREFIX Installation prefix.
+ -q, --quiet Quiet, don't print command lines
+"""
+
+ try:
+ try:
+ opts, args = getopt.getopt(argv[1:], short_options, long_options)
+ except getopt.error, msg:
+ raise Usage(msg)
+
+ for o, a in opts:
+ if o in ('-a', '--all'):
+ all = True
+ elif o in ('-d', '--downloads'):
+ downloads_dir = a
+ elif o in ('-h', '--help'):
+ print helpstr
+ sys.exit(0)
+ elif o in ('-n', '--no-exec'):
+ CommandRunner.execute = CommandRunner.do_not_execute
+ elif o in ('-p', '--prefix'):
+ prefix = a
+ elif o in ('-q', '--quiet'):
+ CommandRunner.display = CommandRunner.do_not_display
+ except Usage, err:
+ sys.stderr.write(str(err.msg) + '\n')
+ sys.stderr.write('use -h to get help\n')
+ return 2
+
+ if all:
+ if args:
+ msg = 'install-scons.py: -a and version arguments both specified'
+ sys.stderr.write(msg)
+ sys.exit(1)
+
+ args = all_versions
+
+ cmd = CommandRunner()
+
+ for version in args:
+ scons = 'scons-' + version
+ tar_gz = os.path.join(downloads_dir, scons + '.tar.gz')
+ tar_gz_url = os.path.join(downloads_url, scons + '.tar.gz')
+
+ cmd.subst_dictionary(locals())
+
+ if not os.path.exists(tar_gz):
+ if not os.path.exists(downloads_dir):
+ cmd.run('mkdir %(downloads_dir)s')
+ cmd.run('wget -O %(tar_gz)s %(tar_gz_url)s')
+
+ cmd.run('tar zxf %(tar_gz)s')
+
+ cmd.run('cd %(scons)s')
+
+ if version in ('0.01', '0.02', '0.03', '0.04', '0.05',
+ '0.06', '0.07', '0.08', '0.09', '0.10'):
+
+ # 0.01 through 0.10 install /usr/local/bin/scons and
+ # /usr/local/lib/scons. The "scons" script knows how to
+ # look up the library in a version-specific directory, but
+ # we have to move both it and the library directory into
+ # the right version-specific name by hand.
+ cmd.run('%(python)s setup.py build')
+ cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s')
+ cmd.run('%(sudo)s mv %(prefix)s/bin/scons %(prefix)s/bin/scons-%(version)s')
+ cmd.run('%(sudo)s mv %(prefix)s/lib/scons %(prefix)s/lib/scons-%(version)s')
+
+ elif version in ('0.11', '0.12', '0.13', '0.14', '0.90'):
+
+ # 0.11 through 0.90 install /usr/local/bin/scons and
+ # /usr/local/lib/scons-%(version)s. We just need to move
+ # the script to a version-specific name.
+ cmd.run('%(python)s setup.py build')
+ cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s')
+ cmd.run('%(sudo)s mv %(prefix)s/bin/scons %(prefix)s/bin/scons-%(version)s')
+
+ elif version in ('0.91', '0.92', '0.93',
+ '0.94', '0.94.1',
+ '0.95', '0.95.1',
+ '0.96', '0.96.1', '0.96.90'):
+
+ # 0.91 through 0.96.90 install /usr/local/bin/scons,
+ # /usr/local/bin/sconsign and /usr/local/lib/scons-%(version)s.
+ # We need to move both scripts to version-specific names.
+ cmd.run('%(python)s setup.py build')
+ cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s')
+ cmd.run('%(sudo)s mv %(prefix)s/bin/scons %(prefix)s/bin/scons-%(version)s')
+ cmd.run('%(sudo)s mv %(prefix)s/bin/sconsign %(prefix)s/bin/sconsign-%(version)s')
+ lib_scons = os.path.join(prefix, 'lib', 'scons')
+ if os.path.isdir(lib_scons):
+ cmd.run('%(sudo)s mv %(prefix)s/lib/scons %(prefix)s/lib/scons-%(version)s')
+
+ else:
+
+ # Versions from 0.96.91 and later support what we want
+ # with a --no-scons-script option.
+ cmd.run('%(python)s setup.py build')
+ cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s --no-scons-script')
+
+ cmd.run('cd ..')
+
+ cmd.run((shutil.rmtree, scons), 'rm -rf %(scons)s')
+
+if __name__ == "__main__":
+ sys.exit(main())
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/linecount.py b/bin/linecount.py
new file mode 100644
index 0000000..359f642
--- /dev/null
+++ b/bin/linecount.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Count statistics about SCons test and source files. This must be run
+# against a fully-populated tree (for example, one that's been freshly
+# checked out).
+#
+# A test file is anything under the src/ directory that begins with
+# 'test_' or ends in 'Tests.py', or anything under the test/ directory
+# that ends in '.py'. Note that runtest.py script does *not*, by default,
+# consider the files that begin with 'test_' to be tests, because they're
+# tests of SCons packaging and installation, not functional tests of
+# SCons code.
+#
+# A source file is anything under the src/engine/ or src/script/
+# directories that ends in '.py' but does NOT begin with 'test_'
+# or end in 'Tests.py'.
+#
+# We report the number of tests and sources, the total number of lines
+# in each category, the number of non-blank lines, and the number of
+# non-comment lines. The last figure (non-comment) lines is the most
+# interesting one for most purposes.
+#
+
+__revision__ = "bin/linecount.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+
+fmt = "%-16s %5s %7s %9s %11s %11s"
+
+class Collection(object):
+ def __init__(self, name, files=None, pred=None):
+ self._name = name
+ if files is None:
+ files = []
+ self.files = files
+ if pred is None:
+ pred = lambda x: True
+ self.pred = pred
+ def __call__(self, fname):
+ return self.pred(fname)
+ def __len__(self):
+ return len(self.files)
+ def extend(self, files):
+ self.files.extend(files)
+ def lines(self):
+ try:
+ return self._lines
+ except AttributeError:
+ self._lines = lines = []
+ for file in self.files:
+ file_lines = open(file).readlines()
+ lines.extend([s.lstrip() for s in file_lines])
+ return lines
+ def non_blank(self):
+ return [s for s in self.lines() if s != '']
+ def non_comment(self):
+ return [s for s in self.lines() if s == '' or s[0] != '#']
+ def non_blank_non_comment(self):
+ return [s for s in self.lines() if s != '' and s[0] != '#']
+ def printables(self):
+ return (self._name + ':',
+ len(self.files),
+ len(self.lines()),
+ len(self.non_blank()),
+ len(self.non_comment()),
+ len(self.non_blank_non_comment()))
+
+def is_Tests_py(x):
+ return x[-8:] == 'Tests.py'
+def is_test_(x):
+ return x[:5] == 'test_'
+def is_python(x):
+ return x[-3:] == '.py'
+def is_source(x):
+ return is_python(x) and not is_Tests_py(x) and not is_test_(x)
+
+src_Tests_py_tests = Collection('src/ *Tests.py', pred=is_Tests_py)
+src_test_tests = Collection('src/ test_*.py', pred=is_test_)
+test_tests = Collection('test/ tests', pred=is_python)
+sources = Collection('sources', pred=is_source)
+
+def t(arg, dirname, names):
+ try: names.remove('.svn')
+ except ValueError: pass
+ names = filter(arg, names)
+ arg.extend(map(lambda n, d=dirname: os.path.join(d, n), names))
+
+os.path.walk('src', t, src_Tests_py_tests)
+os.path.walk('src', t, src_test_tests)
+os.path.walk('test', t, test_tests)
+os.path.walk('src/engine', t, sources)
+os.path.walk('src/script', t, sources)
+
+src_tests = Collection('src/ tests', src_Tests_py_tests.files
+ + src_test_tests.files)
+all_tests = Collection('all tests', src_tests.files + test_tests.files)
+
+def ratio(over, under):
+ return "%.2f" % (float(len(over)) / float(len(under)))
+
+print fmt % ('', '', '', '', '', 'non-blank')
+print fmt % ('', 'files', 'lines', 'non-blank', 'non-comment', 'non-comment')
+print
+print fmt % src_Tests_py_tests.printables()
+print fmt % src_test_tests.printables()
+print
+print fmt % src_tests.printables()
+print fmt % test_tests.printables()
+print
+print fmt % all_tests.printables()
+print fmt % sources.printables()
+print
+print fmt % ('ratio:',
+ ratio(all_tests, sources),
+ ratio(all_tests.lines(), sources.lines()),
+ ratio(all_tests.non_blank(), sources.non_blank()),
+ ratio(all_tests.non_comment(), sources.non_comment()),
+ ratio(all_tests.non_blank_non_comment(),
+ sources.non_blank_non_comment())
+ )
diff --git a/bin/makedocs b/bin/makedocs
new file mode 100644
index 0000000..2278a97
--- /dev/null
+++ b/bin/makedocs
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+# This script uses HappyDoc to create the HTML class documentation for
+# SCons. It must be run from the src/engine directory.
+
+base=`basename $PWD`
+if [ "$base" != "engine" ]; then
+ echo "You must run this script from the engine directory."
+ exit
+fi
+
+DEVDIR=../../doc/developer
+if [ ! -d $DEVDIR ]; then
+ mkdir $DEVDIR
+fi
+
+SRCFILE=../../bin/files
+happydoc -d $DEVDIR `cat $SRCFILE`
diff --git a/bin/memlogs.py b/bin/memlogs.py
new file mode 100644
index 0000000..9d957c9
--- /dev/null
+++ b/bin/memlogs.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2005 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import getopt
+import sys
+
+filenames = sys.argv[1:]
+
+if not filenames:
+ print """Usage: memlogs.py file [...]
+
+Summarizes the --debug=memory numbers from one or more build logs.
+"""
+ sys.exit(0)
+
+fmt = "%12s %12s %12s %12s %s"
+
+print fmt % ("pre-read", "post-read", "pre-build", "post-build", "")
+
+for fname in sys.argv[1:]:
+ lines = [l for l in open(fname).readlines() if l[:7] == 'Memory ']
+ t = tuple([l.split()[-1] for l in lines]) + (fname,)
+ print fmt % t
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/memoicmp.py b/bin/memoicmp.py
new file mode 100644
index 0000000..f45ecb0
--- /dev/null
+++ b/bin/memoicmp.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# A script to compare the --debug=memoizer output found int
+# two different files.
+
+import sys,string
+
+def memoize_output(fname):
+ mout = {}
+ lines=filter(lambda words:
+ len(words) == 5 and
+ words[1] == 'hits' and words[3] == 'misses',
+ map(string.split, open(fname,'r').readlines()))
+ for line in lines:
+ mout[line[-1]] = ( int(line[0]), int(line[2]) )
+ return mout
+
+
+def memoize_cmp(filea, fileb):
+ ma = memoize_output(filea)
+ mb = memoize_output(fileb)
+
+ print 'All output: %s / %s [delta]'%(filea, fileb)
+ print '----------HITS---------- ---------MISSES---------'
+ cfmt='%7d/%-7d [%d]'
+ ma_o = []
+ mb_o = []
+ mab = []
+ for k in ma.keys():
+ if k in mb.keys():
+ if k not in mab:
+ mab.append(k)
+ else:
+ ma_o.append(k)
+ for k in mb.keys():
+ if k in ma.keys():
+ if k not in mab:
+ mab.append(k)
+ else:
+ mb_o.append(k)
+
+ mab.sort()
+ ma_o.sort()
+ mb_o.sort()
+
+ for k in mab:
+ hits = cfmt%(ma[k][0], mb[k][0], mb[k][0]-ma[k][0])
+ miss = cfmt%(ma[k][1], mb[k][1], mb[k][1]-ma[k][1])
+ print '%-24s %-24s %s'%(hits, miss, k)
+
+ for k in ma_o:
+ hits = '%7d/ --'%(ma[k][0])
+ miss = '%7d/ --'%(ma[k][1])
+ print '%-24s %-24s %s'%(hits, miss, k)
+
+ for k in mb_o:
+ hits = ' -- /%-7d'%(mb[k][0])
+ miss = ' -- /%-7d'%(mb[k][1])
+ print '%-24s %-24s %s'%(hits, miss, k)
+
+ print '-'*(24+24+1+20)
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 3:
+ print """Usage: %s file1 file2
+
+Compares --debug=memomize output from file1 against file2."""%sys.argv[0]
+ sys.exit(1)
+
+ memoize_cmp(sys.argv[1], sys.argv[2])
+ sys.exit(0)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/objcounts.py b/bin/objcounts.py
new file mode 100644
index 0000000..ca814b4
--- /dev/null
+++ b/bin/objcounts.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2005 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import re
+import sys
+
+filenames = sys.argv[1:]
+
+if len(sys.argv) != 3:
+ print """Usage: objcounts.py file1 file2
+
+Compare the --debug=object counts from two build logs.
+"""
+ sys.exit(0)
+
+def fetch_counts(fname):
+ contents = open(fname).read()
+ m = re.search('\nObject counts:(\n\s[^\n]*)*', contents, re.S)
+ lines = m.group().split('\n')
+ list = [l.split() for l in lines if re.match('\s+\d', l)]
+ d = {}
+ for l in list:
+ d[l[-1]] = map(int, l[:-1])
+ return d
+
+c1 = fetch_counts(sys.argv[1])
+c2 = fetch_counts(sys.argv[2])
+
+common = {}
+for k in c1.keys():
+ try:
+ common[k] = (c1[k], c2[k])
+ except KeyError:
+ # Transition: we added the module to the names of a bunch of
+ # the logged objects. Assume that c1 might be from an older log
+ # without the modules in the names, and look for an equivalent
+ # in c2.
+ if not '.' in k:
+ s = '.'+k
+ l = len(s)
+ for k2 in c2.keys():
+ if k2[-l:] == s:
+ common[k2] = (c1[k], c2[k2])
+ del c1[k]
+ del c2[k2]
+ break
+ else:
+ del c1[k]
+ del c2[k]
+
+def diffstr(c1, c2):
+ try:
+ d = c2 - c1
+ except TypeError:
+ d = ''
+ else:
+ if d:
+ d = '[%+d]' % d
+ else:
+ d = ''
+ return " %5s/%-5s %-8s" % (c1, c2, d)
+
+def printline(c1, c2, classname):
+ print \
+ diffstr(c1[2], c2[2]) + \
+ diffstr(c1[3], c2[3]) + \
+ ' ' + classname
+
+keys = common.keys()
+keys.sort()
+for k in keys:
+ c = common[k]
+ printline(c[0], c[1], k)
+
+keys = c1.keys()
+keys.sort()
+for k in keys:
+ printline(c1[k], ['--']*4, k)
+
+keys = c2.keys()
+keys.sort()
+for k in keys:
+ printline(['--']*4, c2[k], k)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/restore.sh b/bin/restore.sh
new file mode 100644
index 0000000..a5da9e8
--- /dev/null
+++ b/bin/restore.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env sh
+#
+# Simple hack script to restore __revision__, __COPYRIGHT_, 1.2.0.d20091224
+# and other similar variables to what gets checked in to source. This
+# comes in handy when people send in diffs based on the released source.
+#
+
+if test "X$*" = "X"; then
+ DIRS="src test"
+else
+ DIRS="$*"
+fi
+
+SEPARATOR="================================================================================"
+
+header() {
+ arg_space="$1 "
+ dots=`echo "$arg_space" | sed 's/./\./g'`
+ echo "$SEPARATOR" | sed "s;$dots;$arg_space;"
+}
+
+for i in `find $DIRS -name '*.py'`; do
+ header $i
+ ed $i <<EOF
+g/Copyright (c) 2001.*SCons Foundation/s//Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation/p
+w
+/^__revision__ = /s/= .*/= "bin/restore.sh 4577 2009/12/27 19:44:43 scons"/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name 'scons.bat'`; do
+ header $i
+ ed $i <<EOF
+g/Copyright (c) 2001.*SCons Foundation/s//Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation/p
+w
+/^@REM src\/script\/scons.bat/s/@REM .* knight/@REM bin/restore.sh 4577 2009/12/27 19:44:43 scons/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name '__init__.py' -o -name 'scons.py' -o -name 'sconsign.py'`; do
+ header $i
+ ed $i <<EOF
+/^__version__ = /s/= .*/= "1.2.0.d20091224"/p
+w
+/^__build__ = /s/= .*/= "r4577[MODIFIED]"/p
+w
+/^__buildsys__ = /s/= .*/= "scons-dev"/p
+w
+/^__date__ = /s/= .*/= "2009/12/27 19:44:43"/p
+w
+/^__developer__ = /s/= .*/= "scons"/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name 'setup.py'`; do
+ header $i
+ ed $i <<EOF
+/^ *version = /s/= .*/= "1.2.0.d20091224",/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name '*.txt'`; do
+ header $i
+ ed $i <<EOF
+g/Copyright (c) 2001.*SCons Foundation/s//Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation/p
+w
+/# [^ ]* 0.96.[CD][0-9]* [0-9\/]* [0-9:]* knight$/s/.*/# bin/restore.sh 4577 2009/12/27 19:44:43 scons/p
+w
+/Version [0-9][0-9]*\.[0-9][0-9]*/s//Version 1.2.0.d20091224/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name '*.xml'`; do
+ header $i
+ ed $i <<EOF
+g/Copyright (c) 2001.*SCons Foundation/s//Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation/p
+w
+q
+EOF
+done
diff --git a/bin/rsync-sourceforge b/bin/rsync-sourceforge
new file mode 100644
index 0000000..de44e3b
--- /dev/null
+++ b/bin/rsync-sourceforge
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Sync this directory tree with sourceforge.
+#
+# Cribbed and modified from Peter Miller's same-named script in
+# /home/groups/a/ae/aegis/aegis at SourceForge.
+#
+# Guide to what this does with rsync:
+#
+# --rsh=ssh use ssh for the transfer
+# -l copy symlinks as symlinks
+# -p preserve permissions
+# -r recursive
+# -t preserve times
+# -z compress data
+# --stats file transfer statistics
+# --exclude exclude files matching the pattern
+# --delete delete files that don't exist locally
+# --delete-excluded delete files that match the --exclude patterns
+# --progress show progress during the transfer
+# -v verbose
+#
+LOCAL=/home/scons/scons
+REMOTE=/home/groups/s/sc/scons/scons
+/usr/bin/rsync --rsh=ssh -l -p -r -t -z --stats \
+ --exclude build \
+ --exclude "*,D" \
+ --exclude "*.pyc" \
+ --exclude aegis.log \
+ --delete --delete-excluded \
+ --progress -v \
+ ${LOCAL}/. scons.sourceforge.net:${REMOTE}/.
diff --git a/bin/scons-cdist b/bin/scons-cdist
new file mode 100644
index 0000000..58b1bae
--- /dev/null
+++ b/bin/scons-cdist
@@ -0,0 +1,272 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+PROG=`basename $0`
+NOARGFLAGS="afhlnqrstz"
+ARGFLAGS="p:"
+ALLFLAGS="${NOARGFLAGS}${ARGFLAGS}"
+USAGE="Usage: ${PROG} [-${NOARGFLAGS}] [-p project] change"
+
+HELP="$USAGE
+
+ -a Update the latest Aegis baseline (aedist) file.
+ -f Force update, skipping up-front sanity check.
+ -h Print this help message and exit.
+ -l Update the local CVS repository.
+ -n Don't execute, just echo commands.
+ -p project Set the Aegis project.
+ -q Quiet, don't print commands before executing them.
+ -r Rsync the Aegis repository to SourceForge.
+ -s Update the sourceforge.net CVS repository.
+ -t Update the tigris.org CVS repository.
+ -z Update the latest .tar.gz and .zip files.
+"
+
+DO=""
+PRINT="echo"
+EXECUTE="eval"
+SANITY_CHECK="yes"
+
+while getopts $ALLFLAGS FLAG; do
+ case $FLAG in
+ a | l | r | s | t | z )
+ DO="${DO}${FLAG}"
+ ;;
+ f )
+ SANITY_CHECK="no"
+ ;;
+ h )
+ echo "${HELP}"
+ exit 0
+ ;;
+ n )
+ EXECUTE=":"
+ ;;
+ p )
+ AEGIS_PROJECT="${OPTARG}"
+ ;;
+ q )
+ PRINT=":"
+ ;;
+ * )
+ echo "FLAG = ${FLAG}" >&2
+ echo "${USAGE}" >&2
+ exit 1
+ ;;
+ esac
+done
+
+shift `expr ${OPTIND} - 1`
+
+if test "X$1" = "X"; then
+ echo "${USAGE}" >&2
+ exit 1
+fi
+
+if test "X${AEGIS_PROJECT}" = "X"; then
+ echo "$PROG: No AEGIS_PROJECT set." >&2
+ echo "${USAGE}" >&2
+ exit 1
+fi
+
+if test "X$DO" = "X"; then
+ DO="alrstz"
+fi
+
+cmd()
+{
+ $PRINT "$*"
+ $EXECUTE "$*"
+}
+
+CHANGE=$1
+
+if test "X${SANITY_CHECK}" = "Xyes"; then
+ SCM="cvs"
+ SCMROOT="/home/scons/CVSROOT/scons"
+ DELTA=`aegis -l -ter cd ${CHANGE} | sed -n 's/.*, Delta \([0-9]*\)\./\1/p'`
+ if test "x${DELTA}" = "x"; then
+ echo "${PROG}: Could not find delta for change ${CHANGE}." >&2
+ echo "Has this finished integrating? Change ${CHANGE} not distributed." >&2
+ exit 1
+ fi
+ PREV_DELTA=`expr ${DELTA} - 1`
+ COMMAND="scons-scmcheck -D ${PREV_DELTA} -d q -p ${AEGIS_PROJECT} -s ${SCM} ${SCMROOT}"
+ $PRINT "${COMMAND}"
+ OUTPUT=`${COMMAND}`
+ if test "X${OUTPUT}" != "X"; then
+ echo "${PROG}: ${SCMROOT} is not up to date:" >&2
+ echo "${OUTPUT}" >& 2
+ echo "Did you skip any changes? Change ${CHANGE} not distributed." >&2
+ exit 1
+ fi
+fi
+
+if test X$EXECUTE != "X:" -a "X$SSH_AGENT_PID" = "X"; then
+ eval `ssh-agent`
+ ssh-add
+ trap 'eval `ssh-agent -k`; exit' 0 1 2 3 15
+fi
+
+cd
+
+BASELINE=`aesub -p ${AEGIS_PROJECT} -c ${CHANGE} '${Project trunk_name}'`
+
+TMPBLAE="/tmp/${BASELINE}.ae"
+TMPCAE="/tmp/${AEGIS_PROJECT}.C${CHANGE}.ae"
+
+# Original values for SourceForge.
+#SFLOGIN="stevenknight"
+#SFHOST="scons.sourceforge.net"
+#SFDEST="/home/groups/s/sc/scons/htdocs"
+
+SCONSLOGIN="scons"
+SCONSHOST="manam.pair.com"
+#SCONSDEST="public_html/production"
+SCONSDEST="public_ftp"
+
+#
+# Copy the baseline .ae to the constant location on SourceForge.
+#
+case "${DO}" in
+*a* )
+ cmd "aedist -s -bl -p ${AEGIS_PROJECT} > ${TMPBLAE}"
+ cmd "scp ${TMPBLAE} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/${BASELINE}.ae"
+ cmd "rm ${TMPBLAE}"
+ ;;
+esac
+
+#
+# Copy the latest .tar.gz and .zip files to the constant location on
+# SourceForge.
+#
+case "${DO}" in
+*z* )
+ BUILD_DIST=`aegis -p ${AEGIS_PROJECT} -cd -bl`/build/dist
+ SCONS_SRC_TAR_GZ=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.tar.gz
+ SCONS_SRC_ZIP=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.zip
+ cmd "scp ${BUILD_DIST}/${SCONS_SRC_TAR_GZ} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.tar.gz"
+ cmd "scp ${BUILD_DIST}/${SCONS_SRC_ZIP} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.zip"
+esac
+
+#
+# Sync Aegis tree with SourceForge.
+#
+# Cribbed and modified from Peter Miller's same-named script in
+# /home/groups/a/ae/aegis/aegis at SourceForge.
+#
+# Guide to what this does with rsync:
+#
+# --rsh=ssh use ssh for the transfer
+# -l copy symlinks as symlinks
+# -p preserve permissions
+# -r recursive
+# -t preserve times
+# -z compress data
+# --stats file transfer statistics
+# --exclude exclude files matching the pattern
+# --delete delete files that don't exist locally
+# --delete-excluded delete files that match the --exclude patterns
+# --progress show progress during the transfer
+# -v verbose
+#
+# We no longer use the --stats option.
+#
+case "${DO}" in
+*r* )
+ LOCAL=/home/scons/scons
+ REMOTE=/home/groups/s/sc/scons/scons
+ cmd "/usr/bin/rsync --rsh='ssh -l stevenknight' \
+ -l -p -r -t -z \
+ --exclude build \
+ --exclude '*,D' \
+ --exclude '*.pyc' \
+ --exclude aegis.log \
+ --exclude '.sconsign*' \
+ --delete --delete-excluded \
+ --progress -v \
+ ${LOCAL}/. scons.sourceforge.net:${REMOTE}/."
+ ;;
+esac
+
+#
+# Sync the CVS tree with the local repository.
+#
+case "${DO}" in
+*l* )
+ (
+ export CVSROOT=/home/scons/CVSROOT/scons
+ #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/baldmt.com/scons"
+ cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}"
+ )
+ ;;
+esac
+
+#
+# Sync the Subversion tree with Tigris.org.
+#
+case "${DO}" in
+*t* )
+ (
+ SVN=http://scons.tigris.org/svn/scons
+ case ${AEGIS_PROJECT} in
+ scons.0.96 )
+ SVN_URL=${SVN}/branches/core
+ ;;
+ scons.0.96.513 )
+ SVN_URL=${SVN}/branches/sigrefactor
+ ;;
+ * )
+ echo "$PROG: Don't know SVN branch for '${AEGIS_PROJECT}'" >&2
+ exit 1
+ ;;
+ esac
+ SVN_CO_FLAGS="--username stevenknight"
+ #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/tigris.org/scons"
+ cmd "ae-svn-ci ${AEGIS_PROJECT} ${CHANGE} ${SVN_URL} ${SVN_CO_FLAGS}"
+ )
+ ;;
+esac
+
+#
+# Sync the CVS tree with SourceForge.
+#
+case "${DO}" in
+*s* )
+ (
+ export CVS_RSH=ssh
+ export CVSROOT=:ext:stevenknight@scons.cvs.sourceforge.net:/cvsroot/scons
+ #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/sourceforge.net/scons"
+ cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}"
+ )
+ ;;
+esac
+
+#
+# Send the change .ae to the scons-aedist mailing list
+#
+# The subject requires editing by hand...
+#
+#aedist -s -p ${AEGIS_PROJECT} ${CHANGE} > ${TMPCAE}
+#aegis -l -p ${AEGIS_PROJECT} -c ${CHANGE} cd |
+# pine -attach_and_delete ${TMPCAE} scons-aedist@lists.sourceforge.net
diff --git a/bin/scons-diff.py b/bin/scons-diff.py
new file mode 100644
index 0000000..11d1bda
--- /dev/null
+++ b/bin/scons-diff.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+#
+# scons-diff.py - diff-like utility for comparing SCons trees
+#
+# This supports most common diff options (with some quirks, like you can't
+# just say -c and have it use a default value), but canonicalizes the
+# various version strings within the file like __revision__, __build__,
+# etc. so that you can diff trees without having to ignore changes in
+# version lines.
+#
+
+import difflib
+import getopt
+import os.path
+import re
+import sys
+
+Usage = """\
+Usage: scons-diff.py [OPTIONS] dir1 dir2
+Options:
+ -c NUM, --context=NUM Print NUM lines of copied context.
+ -h, --help Print this message and exit.
+ -n Don't canonicalize SCons lines.
+ -q, --quiet Print only whether files differ.
+ -r, --recursive Recursively compare found subdirectories.
+ -s Report when two files are the same.
+ -u NUM, --unified=NUM Print NUM lines of unified context.
+"""
+
+opts, args = getopt.getopt(sys.argv[1:],
+ 'c:dhnqrsu:',
+ ['context=', 'help', 'recursive', 'unified='])
+
+diff_type = None
+edit_type = None
+context = 2
+recursive = False
+report_same = False
+diff_options = []
+
+def diff_line(left, right):
+ if diff_options:
+ opts = ' ' + ' '.join(diff_options)
+ else:
+ opts = ''
+ print 'diff%s %s %s' % (opts, left, right)
+
+for o, a in opts:
+ if o in ('-c', '-u'):
+ diff_type = o
+ context = int(a)
+ diff_options.append(o)
+ elif o in ('-h', '--help'):
+ print Usage
+ sys.exit(0)
+ elif o in ('-n'):
+ diff_options.append(o)
+ edit_type = o
+ elif o in ('-q'):
+ diff_type = o
+ diff_line = lambda l, r: None
+ elif o in ('-r', '--recursive'):
+ recursive = True
+ diff_options.append(o)
+ elif o in ('-s'):
+ report_same = True
+
+try:
+ left, right = args
+except ValueError:
+ sys.stderr.write(Usage)
+ sys.exit(1)
+
+def quiet_diff(a, b, fromfile='', tofile='',
+ fromfiledate='', tofiledate='', n=3, lineterm='\n'):
+ """
+ A function with the same calling signature as difflib.context_diff
+ (diff -c) and difflib.unified_diff (diff -u) but which prints
+ output like the simple, unadorned 'diff" command.
+ """
+ if a == b:
+ return []
+ else:
+ return ['Files %s and %s differ\n' % (fromfile, tofile)]
+
+def simple_diff(a, b, fromfile='', tofile='',
+ fromfiledate='', tofiledate='', n=3, lineterm='\n'):
+ """
+ A function with the same calling signature as difflib.context_diff
+ (diff -c) and difflib.unified_diff (diff -u) but which prints
+ output like the simple, unadorned 'diff" command.
+ """
+ sm = difflib.SequenceMatcher(None, a, b)
+ def comma(x1, x2):
+ return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2)
+ result = []
+ for op, a1, a2, b1, b2 in sm.get_opcodes():
+ if op == 'delete':
+ result.append("%sd%d\n" % (comma(a1, a2), b1))
+ result.extend(map(lambda l: '< ' + l, a[a1:a2]))
+ elif op == 'insert':
+ result.append("%da%s\n" % (a1, comma(b1, b2)))
+ result.extend(map(lambda l: '> ' + l, b[b1:b2]))
+ elif op == 'replace':
+ result.append("%sc%s\n" % (comma(a1, a2), comma(b1, b2)))
+ result.extend(map(lambda l: '< ' + l, a[a1:a2]))
+ result.append('---\n')
+ result.extend(map(lambda l: '> ' + l, b[b1:b2]))
+ return result
+
+diff_map = {
+ '-c' : difflib.context_diff,
+ '-q' : quiet_diff,
+ '-u' : difflib.unified_diff,
+}
+
+diff_function = diff_map.get(diff_type, simple_diff)
+
+baseline_re = re.compile('(# |@REM )/home/\S+/baseline/')
+comment_rev_re = re.compile('(# |@REM )(\S+) 0.96.[CD]\d+ \S+ \S+( knight)')
+revision_re = re.compile('__revision__ = "[^"]*"')
+build_re = re.compile('__build__ = "[^"]*"')
+date_re = re.compile('__date__ = "[^"]*"')
+
+def lines_read(file):
+ return open(file).readlines()
+
+def lines_massage(file):
+ text = open(file).read()
+ text = baseline_re.sub('\\1', text)
+ text = comment_rev_re.sub('\\1\\2\\3', text)
+ text = revision_re.sub('__revision__ = "bin/scons-diff.py"', text)
+ text = build_re.sub('__build__ = "0.96.92.DXXX"', text)
+ text = date_re.sub('__date__ = "2006/08/25 02:59:00"', text)
+ return text.splitlines(1)
+
+lines_map = {
+ '-n' : lines_read,
+}
+
+lines_function = lines_map.get(edit_type, lines_massage)
+
+def do_diff(left, right, diff_subdirs):
+ if os.path.isfile(left) and os.path.isfile(right):
+ diff_file(left, right)
+ elif not os.path.isdir(left):
+ diff_file(left, os.path.join(right, os.path.split(left)[1]))
+ elif not os.path.isdir(right):
+ diff_file(os.path.join(left, os.path.split(right)[1]), right)
+ elif diff_subdirs:
+ diff_dir(left, right)
+
+def diff_file(left, right):
+ l = lines_function(left)
+ r = lines_function(right)
+ d = diff_function(l, r, left, right, context)
+ try:
+ text = ''.join(d)
+ except IndexError:
+ sys.stderr.write('IndexError diffing %s and %s\n' % (left, right))
+ else:
+ if text:
+ diff_line(left, right)
+ print text,
+ elif report_same:
+ print 'Files %s and %s are identical' % (left, right)
+
+def diff_dir(left, right):
+ llist = os.listdir(left)
+ rlist = os.listdir(right)
+ u = {}
+ for l in llist:
+ u[l] = 1
+ for r in rlist:
+ u[r] = 1
+ clist = [ x for x in u.keys() if x[-4:] != '.pyc' ]
+ clist.sort()
+ for x in clist:
+ if x in llist:
+ if x in rlist:
+ do_diff(os.path.join(left, x),
+ os.path.join(right, x),
+ recursive)
+ else:
+ print 'Only in %s: %s' % (left, x)
+ else:
+ print 'Only in %s: %s' % (right, x)
+
+do_diff(left, right, True)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/scons-proc.py b/bin/scons-proc.py
new file mode 100644
index 0000000..cc3b085
--- /dev/null
+++ b/bin/scons-proc.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python
+#
+# Process a list of Python and/or XML files containing SCons documentation.
+#
+# This script creates formatted lists of the Builders, Tools or
+# construction variables documented in the specified XML files.
+#
+# Dependening on the options, the lists are output in either
+# DocBook-formatted generated XML files containing the summary text
+# and/or .mod files contining the ENTITY definitions for each item,
+# or in man-page-formatted output.
+#
+import getopt
+import os.path
+import re
+import string
+import StringIO
+import sys
+import xml.sax
+
+import SConsDoc
+
+base_sys_path = [os.getcwd() + '/build/test-tar-gz/lib/scons'] + sys.path
+
+helpstr = """\
+Usage: scons-proc.py [--man|--xml]
+ [-b file(s)] [-t file(s)] [-v file(s)] [infile ...]
+Options:
+ -b file(s) dump builder information to the specified file(s)
+ -t file(s) dump tool information to the specified file(s)
+ -v file(s) dump variable information to the specified file(s)
+ --man print info in man page format, each -[btv] argument
+ is a single file name
+ --xml (default) print info in SML format, each -[btv] argument
+ is a pair of comma-separated .gen,.mod file names
+"""
+
+opts, args = getopt.getopt(sys.argv[1:],
+ "b:ht:v:",
+ ['builders=', 'help',
+ 'man', 'xml', 'tools=', 'variables='])
+
+buildersfiles = None
+output_type = '--xml'
+toolsfiles = None
+variablesfiles = None
+
+for o, a in opts:
+ if o in ['-b', '--builders']:
+ buildersfiles = a
+ elif o in ['-h', '--help']:
+ sys.stdout.write(helpstr)
+ sys.exit(0)
+ elif o in ['--man', '--xml']:
+ output_type = o
+ elif o in ['-t', '--tools']:
+ toolsfiles = a
+ elif o in ['-v', '--variables']:
+ variablesfiles = a
+
+h = SConsDoc.SConsDocHandler()
+saxparser = xml.sax.make_parser()
+saxparser.setContentHandler(h)
+saxparser.setErrorHandler(h)
+
+xml_preamble = """\
+<?xml version="1.0"?>
+<scons_doc>
+"""
+
+xml_postamble = """\
+</scons_doc>
+"""
+
+for f in args:
+ _, ext = os.path.splitext(f)
+ if ext == '.py':
+ dir, _ = os.path.split(f)
+ if dir:
+ sys.path = [dir] + base_sys_path
+ module = SConsDoc.importfile(f)
+ h.set_file_info(f, len(xml_preamble.split('\n')))
+ try:
+ content = module.__scons_doc__
+ except AttributeError:
+ content = None
+ else:
+ del module.__scons_doc__
+ else:
+ h.set_file_info(f, len(xml_preamble.split('\n')))
+ content = open(f).read()
+ if content:
+ content = content.replace('&', '&amp;')
+ input = xml_preamble + content + xml_postamble
+ try:
+ saxparser.parse(StringIO.StringIO(input))
+ except:
+ sys.stderr.write("error in %s\n" % f)
+ raise
+
+Warning = """\
+<!--
+THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
+-->
+"""
+
+Regular_Entities_Header = """\
+<!--
+
+ Regular %s entities.
+
+-->
+"""
+
+Link_Entities_Header = """\
+<!--
+
+ Entities that are links to the %s entries in the appendix.
+
+-->
+"""
+
+class SCons_XML:
+ def __init__(self, entries, **kw):
+ values = entries.values()
+ values.sort()
+ self.values = values
+ for k, v in kw.items():
+ setattr(self, k, v)
+ def fopen(self, name):
+ if name == '-':
+ return sys.stdout
+ return open(name, 'w')
+
+class SCons_XML_to_XML(SCons_XML):
+ def write(self, files):
+ gen, mod = string.split(files, ',')
+ g.write_gen(gen)
+ g.write_mod(mod)
+ def write_gen(self, filename):
+ if not filename:
+ return
+ f = self.fopen(filename)
+ for v in self.values:
+ f.write('\n<varlistentry id="%s%s">\n' %
+ (self.prefix, self.idfunc(v.name)))
+ for term in self.termfunc(v.name):
+ f.write('<term><%s>%s</%s></term>\n' %
+ (self.tag, term, self.tag))
+ f.write('<listitem>\n')
+ for chunk in v.summary.body:
+ f.write(str(chunk))
+ if v.sets:
+ s = map(lambda x: '&cv-link-%s;' % x, v.sets)
+ f.write('<para>\n')
+ f.write('Sets: ' + ', '.join(s) + '.\n')
+ f.write('</para>\n')
+ if v.uses:
+ u = map(lambda x: '&cv-link-%s;' % x, v.uses)
+ f.write('<para>\n')
+ f.write('Uses: ' + ', '.join(u) + '.\n')
+ f.write('</para>\n')
+ f.write('</listitem>\n')
+ f.write('</varlistentry>\n')
+ def write_mod(self, filename):
+ if not filename:
+ return
+ f = self.fopen(filename)
+ f.write(Warning)
+ f.write('\n')
+ f.write(Regular_Entities_Header % self.description)
+ f.write('\n')
+ for v in self.values:
+ f.write('<!ENTITY %s%s "<%s>%s</%s>">\n' %
+ (self.prefix, self.idfunc(v.name),
+ self.tag, self.entityfunc(v.name), self.tag))
+ f.write('\n')
+ f.write(Warning)
+ f.write('\n')
+ f.write(Link_Entities_Header % self.description)
+ f.write('\n')
+ for v in self.values:
+ f.write('<!ENTITY %slink-%s \'<link linkend="%s%s"><%s>%s</%s></link>\'>\n' %
+ (self.prefix, self.idfunc(v.name),
+ self.prefix, self.idfunc(v.name),
+ self.tag, self.entityfunc(v.name), self.tag))
+ f.write('\n')
+ f.write(Warning)
+
+class SCons_XML_to_man(SCons_XML):
+ def mansep(self):
+ return ['\n']
+ def initial_chunks(self, name):
+ return [name]
+ def write(self, filename):
+ if not filename:
+ return
+ f = self.fopen(filename)
+ chunks = []
+ for v in self.values:
+ chunks.extend(self.mansep())
+ for n in self.initial_chunks(v.name):
+ chunks.append('.IP %s\n' % n)
+ chunks.extend(map(str, v.summary.body))
+
+ body = ''.join(chunks)
+ body = string.replace(body, '<programlisting>', '.ES')
+ body = string.replace(body, '</programlisting>', '.EE')
+ body = string.replace(body, '\n</para>\n<para>\n', '\n\n')
+ body = string.replace(body, '<para>\n', '')
+ body = string.replace(body, '<para>', '\n')
+ body = string.replace(body, '</para>\n', '')
+ body = re.sub('\.EE\n\n+(?!\.IP)', '.EE\n.IP\n', body)
+ body = re.sub('&(scons|SConstruct|SConscript|jar);', r'\\fB\1\\fP', body)
+ body = string.replace(body, '&Dir;', r'\fBDir\fP')
+ body = string.replace(body, '&target;', r'\fItarget\fP')
+ body = string.replace(body, '&source;', r'\fIsource\fP')
+ body = re.sub('&b(-link)?-([^;]*);', r'\\fB\2\\fP()', body)
+ body = re.sub('&cv(-link)?-([^;]*);', r'$\2', body)
+ body = re.sub(r'<(command|envar|filename|literal|option)>([^<]*)</\1>',
+ r'\\fB\2\\fP', body)
+ body = re.sub(r'<(classname|emphasis|varname)>([^<]*)</\1>',
+ r'\\fI\2\\fP', body)
+ body = re.compile(r'^\\f([BI])(.*)\\fP\s*$', re.M).sub(r'.\1 \2', body)
+ body = re.compile(r'^\\f([BI])(.*)\\fP(\S+)', re.M).sub(r'.\1R \2 \3', body)
+ body = string.replace(body, '&lt;', '<')
+ body = string.replace(body, '&gt;', '>')
+ body = re.sub(r'\\([^f])', r'\\\\\1', body)
+ body = re.compile("^'\\\\\\\\", re.M).sub("'\\\\", body)
+ body = re.compile(r'^\.([BI]R?) -', re.M).sub(r'.\1 \-', body)
+ body = re.compile(r'^\.([BI]R?) (\S+)\\\\(\S+)', re.M).sub(r'.\1 "\2\\\\\\\\\2"', body)
+ body = re.compile(r'\\f([BI])-', re.M).sub(r'\\f\1\-', body)
+ f.write(body)
+
+if output_type == '--man':
+ processor_class = SCons_XML_to_man
+elif output_type == '--xml':
+ processor_class = SCons_XML_to_XML
+else:
+ sys.stderr.write("Unknown output type '%s'\n" % output_type)
+ sys.exit(1)
+
+if buildersfiles:
+ g = processor_class(h.builders,
+ description = 'builder',
+ prefix = 'b-',
+ tag = 'function',
+ idfunc = lambda x: x,
+ termfunc = lambda x: [x+'()', 'env.'+x+'()'],
+ entityfunc = lambda x: x)
+
+ g.mansep = lambda: ['\n', "'\\" + '"'*69 + '\n']
+ g.initial_chunks = lambda n: [n+'()', 'env.'+n+'()']
+
+ g.write(buildersfiles)
+
+if toolsfiles:
+ g = processor_class(h.tools,
+ description = 'tool',
+ prefix = 't-',
+ tag = 'literal',
+ idfunc = lambda x: string.replace(x, '+', 'X'),
+ termfunc = lambda x: [x],
+ entityfunc = lambda x: x)
+
+ g.write(toolsfiles)
+
+if variablesfiles:
+ g = processor_class(h.cvars,
+ description = 'construction variable',
+ prefix = 'cv-',
+ tag = 'envar',
+ idfunc = lambda x: x,
+ termfunc = lambda x: [x],
+ entityfunc = lambda x: '$'+x)
+
+ g.write(variablesfiles)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/scons-review.sh b/bin/scons-review.sh
new file mode 100755
index 0000000..f126333
--- /dev/null
+++ b/bin/scons-review.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+case "$1" in
+'') exec svn diff --diff-cmd diff -x -c $* ;;
+-m) svn diff --diff-cmd diff -x -c $* | alpine scons-dev ;;
+*) echo "Error: unknown option '$1"; exit 1 ;;
+esac
+
+# OLD CODE FOR USE WITH AEGIS
+#
+#if test $# -ne 1; then
+# echo "Usage: scons-review change#" >&2
+# exit 1
+#fi
+#if test "X$AEGIS_PROJECT" = "X"; then
+# echo "scons-review: AEGIS_PROJECT is not set" >&2
+# exit 1
+#fi
+#DIR=`aegis -cd -dd $*`
+#if test "X${DIR}" = "X"; then
+# echo "scons-review: No Aegis directory for '$*'" >&2
+# exit 1
+#fi
+#(cd ${DIR} && find * -name '*,D' | sort | xargs cat) | pine scons-dev
diff --git a/bin/scons-test.py b/bin/scons-test.py
new file mode 100644
index 0000000..aa03d72
--- /dev/null
+++ b/bin/scons-test.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python
+#
+# A script that takes an scons-src-{version}.zip file, unwraps it in
+# a temporary location, and calls runtest.py to execute one or more of
+# its tests.
+#
+# The default is to download the latest scons-src archive from the SCons
+# web site, and to execute all of the tests.
+#
+# With a little more work, this will become the basis of an automated
+# testing and reporting system that anyone will be able to use to
+# participate in testing SCons on their system and regularly reporting
+# back the results. A --xml option is a stab at gathering a lot of
+# relevant information about the system, the Python version, etc.,
+# so that problems on different platforms can be identified sooner.
+#
+
+import getopt
+import imp
+import os
+import os.path
+import string
+import sys
+import tempfile
+import time
+import urllib
+import zipfile
+
+helpstr = """\
+Usage: scons-test.py [-f zipfile] [-o outdir] [-v] [--xml] [runtest arguments]
+Options:
+ -f FILE Specify input .zip FILE name
+ -o DIR, --out DIR Change output directory name to DIR
+ -v, --verbose Print file names when extracting
+ --xml XML output
+"""
+
+opts, args = getopt.getopt(sys.argv[1:],
+ "f:o:v",
+ ['file=', 'out=', 'verbose', 'xml'])
+
+format = None
+outdir = None
+printname = lambda x: x
+inputfile = 'http://scons.sourceforge.net/scons-src-latest.zip'
+
+for o, a in opts:
+ if o == '-f' or o == '--file':
+ inputfile = a
+ elif o == '-o' or o == '--out':
+ outdir = a
+ elif o == '-v' or o == '--verbose':
+ def printname(x):
+ print x
+ elif o == '--xml':
+ format = o
+
+startdir = os.getcwd()
+
+tempfile.template = 'scons-test.'
+tempdir = tempfile.mktemp()
+
+if not os.path.exists(tempdir):
+ os.mkdir(tempdir)
+ def cleanup(tempdir=tempdir):
+ import shutil
+ os.chdir(startdir)
+ shutil.rmtree(tempdir)
+ sys.exitfunc = cleanup
+
+# Fetch the input file if it happens to be across a network somewhere.
+# Ohmigod, does Python make this simple...
+inputfile, headers = urllib.urlretrieve(inputfile)
+
+# Unzip the header file in the output directory. We use our own code
+# (lifted from scons-unzip.py) to make the output subdirectory name
+# match the basename of the .zip file.
+zf = zipfile.ZipFile(inputfile, 'r')
+
+if outdir is None:
+ name, _ = os.path.splitext(os.path.basename(inputfile))
+ outdir = os.path.join(tempdir, name)
+
+def outname(n, outdir=outdir):
+ l = []
+ while 1:
+ n, tail = os.path.split(n)
+ if not n:
+ break
+ l.append(tail)
+ l.append(outdir)
+ l.reverse()
+ return apply(os.path.join, l)
+
+for name in zf.namelist():
+ dest = outname(name)
+ dir = os.path.dirname(dest)
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ printname(dest)
+ # if the file exists, then delete it before writing
+ # to it so that we don't end up trying to write to a symlink:
+ if os.path.isfile(dest) or os.path.islink(dest):
+ os.unlink(dest)
+ if not os.path.isdir(dest):
+ open(dest, 'w').write(zf.read(name))
+
+os.chdir(outdir)
+
+# Load (by hand) the SCons modules we just unwrapped so we can
+# extract their version information. Note that we have to override
+# SCons.Script.main() with a do_nothing() function, because loading up
+# the 'scons' script will actually try to execute SCons...
+src_script = os.path.join(outdir, 'src', 'script')
+src_engine = os.path.join(outdir, 'src', 'engine')
+src_engine_SCons = os.path.join(src_engine, 'SCons')
+
+fp, pname, desc = imp.find_module('SCons', [src_engine])
+SCons = imp.load_module('SCons', fp, pname, desc)
+
+fp, pname, desc = imp.find_module('Script', [src_engine_SCons])
+SCons.Script = imp.load_module('Script', fp, pname, desc)
+
+def do_nothing():
+ pass
+SCons.Script.main = do_nothing
+
+fp, pname, desc = imp.find_module('scons', [src_script])
+scons = imp.load_module('scons', fp, pname, desc)
+fp.close()
+
+# Default is to run all the tests by passing the -a flags to runtest.py.
+if not args:
+ runtest_args = '-a'
+else:
+ runtest_args = string.join(args)
+
+if format == '--xml':
+
+ print "<scons_test_run>"
+ print " <sys>"
+ sys_keys = ['byteorder', 'exec_prefix', 'executable', 'maxint', 'maxunicode', 'platform', 'prefix', 'version', 'version_info']
+ for k in sys_keys:
+ print " <%s>%s</%s>" % (k, sys.__dict__[k], k)
+ print " </sys>"
+
+ fmt = '%a %b %d %H:%M:%S %Y'
+ print " <time>"
+ print " <gmtime>%s</gmtime>" % time.strftime(fmt, time.gmtime())
+ print " <localtime>%s</localtime>" % time.strftime(fmt, time.localtime())
+ print " </time>"
+
+ print " <tempdir>%s</tempdir>" % tempdir
+
+ def print_version_info(tag, module):
+ print " <%s>" % tag
+ print " <version>%s</version>" % module.__version__
+ print " <build>%s</build>" % module.__build__
+ print " <buildsys>%s</buildsys>" % module.__buildsys__
+ print " <date>%s</date>" % module.__date__
+ print " <developer>%s</developer>" % module.__developer__
+ print " </%s>" % tag
+
+ print " <scons>"
+ print_version_info("script", scons)
+ print_version_info("engine", SCons)
+ print " </scons>"
+
+ environ_keys = [
+ 'PATH',
+ 'SCONSFLAGS',
+ 'SCONS_LIB_DIR',
+ 'PYTHON_ROOT',
+ 'QTDIR',
+
+ 'COMSPEC',
+ 'INTEL_LICENSE_FILE',
+ 'INCLUDE',
+ 'LIB',
+ 'MSDEVDIR',
+ 'OS',
+ 'PATHEXT',
+ 'SystemRoot',
+ 'TEMP',
+ 'TMP',
+ 'USERNAME',
+ 'VXDOMNTOOLS',
+ 'WINDIR',
+ 'XYZZY'
+
+ 'ENV',
+ 'HOME',
+ 'LANG',
+ 'LANGUAGE',
+ 'LOGNAME',
+ 'MACHINE',
+ 'OLDPWD',
+ 'PWD',
+ 'OPSYS',
+ 'SHELL',
+ 'TMPDIR',
+ 'USER',
+ ]
+
+ print " <environment>"
+ #keys = os.environ.keys()
+ keys = environ_keys
+ keys.sort()
+ for key in keys:
+ value = os.environ.get(key)
+ if value:
+ print " <variable>"
+ print " <name>%s</name>" % key
+ print " <value>%s</value>" % value
+ print " </variable>"
+ print " </environment>"
+
+ command = '"%s" runtest.py -q -o - --xml %s' % (sys.executable, runtest_args)
+ #print command
+ os.system(command)
+ print "</scons_test_run>"
+
+else:
+
+ def print_version_info(tag, module):
+ print "\t%s: v%s.%s, %s, by %s on %s" % (tag,
+ module.__version__,
+ module.__build__,
+ module.__date__,
+ module.__developer__,
+ module.__buildsys__)
+
+ print "SCons by Steven Knight et al.:"
+ print_version_info("script", scons)
+ print_version_info("engine", SCons)
+
+ command = '"%s" runtest.py %s' % (sys.executable, runtest_args)
+ #print command
+ os.system(command)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/scons-unzip.py b/bin/scons-unzip.py
new file mode 100644
index 0000000..28c73f8
--- /dev/null
+++ b/bin/scons-unzip.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# A quick script to unzip a .zip archive and put the files in a
+# subdirectory that matches the basename of the .zip file.
+#
+# This is actually generic functionality, it's not SCons-specific, but
+# I'm using this to make it more convenient to manage working on multiple
+# changes on Windows, where I don't have access to my Aegis tools.
+#
+
+import getopt
+import os.path
+import sys
+import zipfile
+
+helpstr = """\
+Usage: scons-unzip.py [-o outdir] zipfile
+Options:
+ -o DIR, --out DIR Change output directory name to DIR
+ -v, --verbose Print file names when extracting
+"""
+
+opts, args = getopt.getopt(sys.argv[1:],
+ "o:v",
+ ['out=', 'verbose'])
+
+outdir = None
+printname = lambda x: x
+
+for o, a in opts:
+ if o == '-o' or o == '--out':
+ outdir = a
+ elif o == '-v' or o == '--verbose':
+ def printname(x):
+ print x
+
+if len(args) != 1:
+ sys.stderr.write("scons-unzip.py: \n")
+ sys.exit(1)
+
+zf = zipfile.ZipFile(str(args[0]), 'r')
+
+if outdir is None:
+ outdir, _ = os.path.splitext(os.path.basename(args[0]))
+
+def outname(n, outdir=outdir):
+ l = []
+ while 1:
+ n, tail = os.path.split(n)
+ if not n:
+ break
+ l.append(tail)
+ l.append(outdir)
+ l.reverse()
+ return apply(os.path.join, l)
+
+for name in zf.namelist():
+ dest = outname(name)
+ dir = os.path.dirname(dest)
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ printname(dest)
+ # if the file exists, then delete it before writing
+ # to it so that we don't end up trying to write to a symlink:
+ if os.path.isfile(dest) or os.path.islink(dest):
+ os.unlink(dest)
+ if not os.path.isdir(dest):
+ open(dest, 'w').write(zf.read(name))
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/scons_dev_master.py b/bin/scons_dev_master.py
new file mode 100644
index 0000000..5e8df41
--- /dev/null
+++ b/bin/scons_dev_master.py
@@ -0,0 +1,215 @@
+#!/bin/sh
+#
+
+# A script for turning a generic Ubuntu system into a master for
+# SCons development.
+
+import getopt
+import sys
+
+from Command import CommandRunner, Usage
+
+INITIAL_PACKAGES = [
+ 'subversion',
+]
+
+INSTALL_PACKAGES = [
+ 'wget',
+]
+
+PYTHON_PACKAGES = [
+ 'g++',
+ 'gcc',
+ 'make',
+ 'zlib1g-dev',
+]
+
+BUILDING_PACKAGES = [
+ 'docbook',
+ 'docbook-dsssl',
+ 'docbook-utils',
+ 'docbook-xml',
+ 'groff-base',
+ 'jade',
+ 'jadetex',
+ 'man2html',
+ 'python-epydoc',
+ 'rpm',
+ 'sp',
+ 'tar',
+
+ # additional packages that Bill Deegan's web page suggests
+ #'docbook-to-man',
+ #'docbook-xsl',
+ #'docbook2x',
+ #'tetex-bin',
+ #'tetex-latex',
+]
+
+DOCUMENTATION_PACKAGES = [
+ 'docbook-doc',
+ 'epydoc-doc',
+ 'gcc-doc',
+ 'pkg-config',
+ 'python-doc',
+ 'sun-java5-doc',
+ 'sun-java6-doc',
+ 'swig-doc',
+ 'texlive-doc',
+]
+
+TESTING_PACKAGES = [
+ 'bison',
+ 'cssc',
+ 'cvs',
+ 'flex',
+ 'g++',
+ 'gcc',
+ 'gcj',
+ 'ghostscript',
+# 'libgcj7-dev',
+ 'm4',
+ 'openssh-client',
+ 'openssh-server',
+ 'python-profiler',
+ 'python-all-dev',
+ 'rcs',
+ 'rpm',
+ 'sun-java5-jdk',
+ 'sun-java6-jdk',
+ 'swig',
+ 'texlive-base-bin',
+ 'texlive-latex-base',
+ 'texlive-latex-extra',
+ 'zip',
+]
+
+BUILDBOT_PACKAGES = [
+ 'buildbot',
+ 'cron',
+]
+
+default_args = [
+ 'upgrade',
+ 'checkout',
+ 'building',
+ 'testing',
+ 'python-versions',
+ 'scons-versions',
+]
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ short_options = 'hnqy'
+ long_options = ['help', 'no-exec', 'password=', 'quiet', 'username=',
+ 'yes', 'assume-yes']
+
+ helpstr = """\
+Usage: scons_dev_master.py [-hnqy] [--password PASSWORD] [--username USER]
+ [ACTIONS ...]
+
+ ACTIONS (in default order):
+ upgrade Upgrade the system
+ checkout Check out SCons
+ building Install packages for building SCons
+ testing Install packages for testing SCons
+ scons-versions Install versions of SCons
+ python-versions Install versions of Python
+
+ ACTIONS (optional):
+ buildbot Install packages for running BuildBot
+"""
+
+ scons_url = 'http://scons.tigris.org/svn/scons/trunk'
+ sudo = 'sudo'
+ password = '""'
+ username = 'guest'
+ yesflag = ''
+
+ try:
+ try:
+ opts, args = getopt.getopt(argv[1:], short_options, long_options)
+ except getopt.error, msg:
+ raise Usage(msg)
+
+ for o, a in opts:
+ if o in ('-h', '--help'):
+ print helpstr
+ sys.exit(0)
+ elif o in ('-n', '--no-exec'):
+ CommandRunner.execute = CommandRunner.do_not_execute
+ elif o in ('--password'):
+ password = a
+ elif o in ('-q', '--quiet'):
+ CommandRunner.display = CommandRunner.do_not_display
+ elif o in ('--username'):
+ username = a
+ elif o in ('-y', '--yes', '--assume-yes'):
+ yesflag = o
+ except Usage, err:
+ sys.stderr.write(str(err.msg) + '\n')
+ sys.stderr.write('use -h to get help\n')
+ return 2
+
+ if not args:
+ args = default_args
+
+ initial_packages = ' '.join(INITIAL_PACKAGES)
+ install_packages = ' '.join(INSTALL_PACKAGES)
+ building_packages = ' '.join(BUILDING_PACKAGES)
+ testing_packages = ' '.join(TESTING_PACKAGES)
+ buildbot_packages = ' '.join(BUILDBOT_PACKAGES)
+ python_packages = ' '.join(PYTHON_PACKAGES)
+
+ cmd = CommandRunner(locals())
+
+ for arg in args:
+ if arg == 'upgrade':
+ cmd.run('%(sudo)s apt-get %(yesflag)s upgrade')
+ elif arg == 'checkout':
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(initial_packages)s')
+ cmd.run('svn co --username guest --password "" %(scons_url)s')
+ elif arg == 'building':
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(building_packages)s')
+ elif arg == 'testing':
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(testing_packages)s')
+ elif arg == 'buildbot':
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(buildbot_packages)s')
+ elif arg == 'python-versions':
+ if install_packages:
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(install_packages)s')
+ install_packages = None
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(python_packages)s')
+ try:
+ import install_python
+ except ImportError:
+ msg = 'Could not import install_python; skipping python-versions.\n'
+ sys.stderr.write(msg)
+ else:
+ install_python.main(['install_python.py', '-a'])
+ elif arg == 'scons-versions':
+ if install_packages:
+ cmd.run('%(sudo)s apt-get %(yesflag)s install %(install_packages)s')
+ install_packages = None
+ try:
+ import install_scons
+ except ImportError:
+ msg = 'Could not import install_scons; skipping scons-versions.\n'
+ sys.stderr.write(msg)
+ else:
+ install_scons.main(['install_scons.py', '-a'])
+ else:
+ msg = '%s: unknown argument %s\n'
+ sys.stderr.write(msg % (argv[0], repr(arg)))
+ sys.exit(1)
+
+if __name__ == "__main__":
+ sys.exit(main())
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/sconsexamples.py b/bin/sconsexamples.py
new file mode 100644
index 0000000..0a409bc
--- /dev/null
+++ b/bin/sconsexamples.py
@@ -0,0 +1,512 @@
+#!/usr/bin/env python2
+#
+# scons_examples.py - an SGML preprocessor for capturing SCons output
+# and inserting into examples in our DocBook
+# documentation
+#
+
+# This script looks for some SGML tags that describe SCons example
+# configurations and commands to execute in those configurations, and
+# uses TestCmd.py to execute the commands and insert the output into
+# the output SGML. This way, we can run a script and update all of
+# our example output without having to do a lot of laborious by-hand
+# checking.
+#
+# An "SCons example" looks like this, and essentially describes a set of
+# input files (program source files as well as SConscript files):
+#
+# <scons_example name="ex1">
+# <file name="SConstruct" printme="1">
+# env = Environment()
+# env.Program('foo')
+# </file>
+# <file name="foo.c">
+# int main() { printf("foo.c\n"); }
+# </file>
+# </scons_example>
+#
+# The <file> contents within the <scons_example> tag will get written
+# into a temporary directory whenever example output needs to be
+# generated. By default, the <file> contents are not inserted into text
+# directly, unless you set the "printme" attribute on one or more files,
+# in which case they will get inserted within a <programlisting> tag.
+# This makes it easy to define the example at the appropriate
+# point in the text where you intend to show the SConstruct file.
+#
+# Note that you should usually give the <scons_example> a "name"
+# attribute so that you can refer to the example configuration later to
+# run SCons and generate output.
+#
+# If you just want to show a file's contents without worry about running
+# SCons, there's a shorter <sconstruct> tag:
+#
+# <sconstruct>
+# env = Environment()
+# env.Program('foo')
+# </sconstruct>
+#
+# This is essentially equivalent to <scons_example><file printme="1">,
+# but it's more straightforward.
+#
+# SCons output is generated from the following sort of tag:
+#
+# <scons_output example="ex1" os="posix">
+# <command>scons -Q foo</command>
+# <command>scons -Q foo</command>
+# </scons_output>
+#
+# You tell it which example to use with the "example" attribute, and
+# then give it a list of <command> tags to execute. You can also supply
+# an "os" tag, which specifies the type of operating system this example
+# is intended to show; if you omit this, default value is "posix".
+#
+# The generated SGML will show the command line (with the appropriate
+# command-line prompt for the operating system), execute the command in
+# a temporary directory with the example files, capture the standard
+# output from SCons, and insert it into the text as appropriate.
+# Error output gets passed through to your error output so you
+# can see if there are any problems executing the command.
+#
+
+import os
+import os.path
+import re
+import sgmllib
+import string
+import sys
+
+sys.path.append(os.path.join(os.getcwd(), 'etc'))
+sys.path.append(os.path.join(os.getcwd(), 'build', 'etc'))
+
+scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py')
+if not os.path.exists(scons_py):
+ scons_py = os.path.join('src', 'script', 'scons.py')
+
+scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine')
+if not os.path.exists(scons_lib_dir):
+ scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine')
+
+import TestCmd
+
+# The regular expression that identifies entity references in the
+# standard sgmllib omits the underscore from the legal characters.
+# Override it with our own regular expression that adds underscore.
+sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]')
+
+class DataCollector:
+ """Generic class for collecting data between a start tag and end
+ tag. We subclass for various types of tags we care about."""
+ def __init__(self):
+ self.data = ""
+ def afunc(self, data):
+ self.data = self.data + data
+
+class Example(DataCollector):
+ """An SCons example. This is essentially a list of files that
+ will get written to a temporary directory to collect output
+ from one or more SCons runs."""
+ def __init__(self):
+ DataCollector.__init__(self)
+ self.files = []
+ self.dirs = []
+
+class File(DataCollector):
+ """A file, that will get written out to a temporary directory
+ for one or more SCons runs."""
+ def __init__(self, name):
+ DataCollector.__init__(self)
+ self.name = name
+
+class Directory(DataCollector):
+ """A directory, that will get created in a temporary directory
+ for one or more SCons runs."""
+ def __init__(self, name):
+ DataCollector.__init__(self)
+ self.name = name
+
+class Output(DataCollector):
+ """Where the command output goes. This is essentially
+ a list of commands that will get executed."""
+ def __init__(self):
+ DataCollector.__init__(self)
+ self.commandlist = []
+
+class Command(DataCollector):
+ """A tag for where the command output goes. This is essentially
+ a list of commands that will get executed."""
+ pass
+
+Prompt = {
+ 'posix' : '% ',
+ 'win32' : 'C:\\>'
+}
+
+# Magick SCons hackery.
+#
+# So that our examples can still use the default SConstruct file, we
+# actually feed the following into SCons via stdin and then have it
+# SConscript() the SConstruct file. This stdin wrapper creates a set
+# of ToolSurrogates for the tools for the appropriate platform. These
+# Surrogates print output like the real tools and behave like them
+# without actually having to be on the right platform or have the right
+# tool installed.
+#
+# The upshot: We transparently change the world out from under the
+# top-level SConstruct file in an example just so we can get the
+# command output.
+
+Stdin = """\
+import string
+import SCons.Defaults
+
+platform = '%s'
+
+class Curry:
+ def __init__(self, fun, *args, **kwargs):
+ self.fun = fun
+ self.pending = args[:]
+ self.kwargs = kwargs.copy()
+
+ def __call__(self, *args, **kwargs):
+ if kwargs and self.kwargs:
+ kw = self.kwargs.copy()
+ kw.update(kwargs)
+ else:
+ kw = kwargs or self.kwargs
+
+ return apply(self.fun, self.pending + args, kw)
+
+def Str(target, source, env, cmd=""):
+ result = []
+ for cmd in env.subst_list(cmd, target=target, source=source):
+ result.append(string.join(map(str, cmd)))
+ return string.join(result, '\\n')
+
+class ToolSurrogate:
+ def __init__(self, tool, variable, func):
+ self.tool = tool
+ self.variable = variable
+ self.func = func
+ def __call__(self, env):
+ t = Tool(self.tool)
+ t.generate(env)
+ orig = env[self.variable]
+ env[self.variable] = Action(self.func, strfunction=Curry(Str, cmd=orig))
+
+def Null(target, source, env):
+ pass
+
+def Cat(target, source, env):
+ target = str(target[0])
+ f = open(target, "wb")
+ for src in map(str, source):
+ f.write(open(src, "rb").read())
+ f.close()
+
+ToolList = {
+ 'posix' : [('cc', 'CCCOM', Cat),
+ ('link', 'LINKCOM', Cat),
+ ('tar', 'TARCOM', Null),
+ ('zip', 'ZIPCOM', Null)],
+ 'win32' : [('msvc', 'CCCOM', Cat),
+ ('mslink', 'LINKCOM', Cat)]
+}
+
+tools = map(lambda t: apply(ToolSurrogate, t), ToolList[platform])
+
+SCons.Defaults.ConstructionEnvironment.update({
+ 'PLATFORM' : platform,
+ 'TOOLS' : tools,
+})
+
+SConscript('SConstruct')
+"""
+
+class MySGML(sgmllib.SGMLParser):
+ """A subclass of the standard Python 2.2 sgmllib SGML parser.
+
+ Note that this doesn't work with the 1.5.2 sgmllib module, because
+ that didn't have the ability to work with ENTITY declarations.
+ """
+ def __init__(self):
+ sgmllib.SGMLParser.__init__(self)
+ self.examples = {}
+ self.afunclist = []
+
+ def handle_data(self, data):
+ try:
+ f = self.afunclist[-1]
+ except IndexError:
+ sys.stdout.write(data)
+ else:
+ f(data)
+
+ def handle_comment(self, data):
+ sys.stdout.write('<!--' + data + '-->')
+
+ def handle_decl(self, data):
+ sys.stdout.write('<!' + data + '>')
+
+ def unknown_starttag(self, tag, attrs):
+ try:
+ f = self.example.afunc
+ except AttributeError:
+ f = sys.stdout.write
+ if not attrs:
+ f('<' + tag + '>')
+ else:
+ f('<' + tag)
+ for name, value in attrs:
+ f(' ' + name + '=' + '"' + value + '"')
+ f('>')
+
+ def unknown_endtag(self, tag):
+ sys.stdout.write('</' + tag + '>')
+
+ def unknown_entityref(self, ref):
+ sys.stdout.write('&' + ref + ';')
+
+ def unknown_charref(self, ref):
+ sys.stdout.write('&#' + ref + ';')
+
+ def start_scons_example(self, attrs):
+ t = filter(lambda t: t[0] == 'name', attrs)
+ if t:
+ name = t[0][1]
+ try:
+ e = self.examples[name]
+ except KeyError:
+ e = self.examples[name] = Example()
+ else:
+ e = Example()
+ for name, value in attrs:
+ setattr(e, name, value)
+ self.e = e
+ self.afunclist.append(e.afunc)
+
+ def end_scons_example(self):
+ e = self.e
+ files = filter(lambda f: f.printme, e.files)
+ if files:
+ sys.stdout.write('<programlisting>')
+ for f in files:
+ if f.printme:
+ i = len(f.data) - 1
+ while f.data[i] == ' ':
+ i = i - 1
+ output = string.replace(f.data[:i+1], '__ROOT__', '')
+ sys.stdout.write(output)
+ if e.data and e.data[0] == '\n':
+ e.data = e.data[1:]
+ sys.stdout.write(e.data + '</programlisting>')
+ delattr(self, 'e')
+ self.afunclist = self.afunclist[:-1]
+
+ def start_file(self, attrs):
+ try:
+ e = self.e
+ except AttributeError:
+ self.error("<file> tag outside of <scons_example>")
+ t = filter(lambda t: t[0] == 'name', attrs)
+ if not t:
+ self.error("no <file> name attribute found")
+ try:
+ e.prefix
+ except AttributeError:
+ e.prefix = e.data
+ e.data = ""
+ f = File(t[0][1])
+ f.printme = None
+ for name, value in attrs:
+ setattr(f, name, value)
+ e.files.append(f)
+ self.afunclist.append(f.afunc)
+
+ def end_file(self):
+ self.e.data = ""
+ self.afunclist = self.afunclist[:-1]
+
+ def start_directory(self, attrs):
+ try:
+ e = self.e
+ except AttributeError:
+ self.error("<directory> tag outside of <scons_example>")
+ t = filter(lambda t: t[0] == 'name', attrs)
+ if not t:
+ self.error("no <directory> name attribute found")
+ try:
+ e.prefix
+ except AttributeError:
+ e.prefix = e.data
+ e.data = ""
+ d = Directory(t[0][1])
+ for name, value in attrs:
+ setattr(d, name, value)
+ e.dirs.append(d)
+ self.afunclist.append(d.afunc)
+
+ def end_directory(self):
+ self.e.data = ""
+ self.afunclist = self.afunclist[:-1]
+
+ def start_scons_example_file(self, attrs):
+ t = filter(lambda t: t[0] == 'example', attrs)
+ if not t:
+ self.error("no <scons_example_file> example attribute found")
+ exname = t[0][1]
+ try:
+ e = self.examples[exname]
+ except KeyError:
+ self.error("unknown example name '%s'" % exname)
+ fattrs = filter(lambda t: t[0] == 'name', attrs)
+ if not fattrs:
+ self.error("no <scons_example_file> name attribute found")
+ fname = fattrs[0][1]
+ f = filter(lambda f, fname=fname: f.name == fname, e.files)
+ if not f:
+ self.error("example '%s' does not have a file named '%s'" % (exname, fname))
+ self.f = f[0]
+
+ def end_scons_example_file(self):
+ f = self.f
+ sys.stdout.write('<programlisting>')
+ i = len(f.data) - 1
+ while f.data[i] == ' ':
+ i = i - 1
+ sys.stdout.write(f.data[:i+1] + '</programlisting>')
+ delattr(self, 'f')
+
+ def start_scons_output(self, attrs):
+ t = filter(lambda t: t[0] == 'example', attrs)
+ if not t:
+ self.error("no <scons_output> example attribute found")
+ exname = t[0][1]
+ try:
+ e = self.examples[exname]
+ except KeyError:
+ self.error("unknown example name '%s'" % exname)
+ # Default values for an example.
+ o = Output()
+ o.os = 'posix'
+ o.e = e
+ # Locally-set.
+ for name, value in attrs:
+ setattr(o, name, value)
+ self.o = o
+ self.afunclist.append(o.afunc)
+
+ def end_scons_output(self):
+ o = self.o
+ e = o.e
+ t = TestCmd.TestCmd(workdir='', combine=1)
+ t.subdir('ROOT', 'WORK')
+ for d in e.dirs:
+ dir = t.workpath('WORK', d.name)
+ if not os.path.exists(dir):
+ os.makedirs(dir)
+ for f in e.files:
+ i = 0
+ while f.data[i] == '\n':
+ i = i + 1
+ lines = string.split(f.data[i:], '\n')
+ i = 0
+ while lines[0][i] == ' ':
+ i = i + 1
+ lines = map(lambda l, i=i: l[i:], lines)
+ path = string.replace(f.name, '__ROOT__', t.workpath('ROOT'))
+ dir, name = os.path.split(f.name)
+ if dir:
+ dir = t.workpath('WORK', dir)
+ if not os.path.exists(dir):
+ os.makedirs(dir)
+ content = string.join(lines, '\n')
+ content = string.replace(content,
+ '__ROOT__',
+ t.workpath('ROOT'))
+ t.write(t.workpath('WORK', f.name), content)
+ i = len(o.prefix)
+ while o.prefix[i-1] != '\n':
+ i = i - 1
+ sys.stdout.write('<literallayout>' + o.prefix[:i])
+ p = o.prefix[i:]
+ for c in o.commandlist:
+ sys.stdout.write(p + Prompt[o.os])
+ d = string.replace(c.data, '__ROOT__', '')
+ sys.stdout.write('<userinput>' + d + '</userinput>\n')
+ e = string.replace(c.data, '__ROOT__', t.workpath('ROOT'))
+ args = string.split(e)[1:]
+ os.environ['SCONS_LIB_DIR'] = scons_lib_dir
+ t.run(interpreter = sys.executable,
+ program = scons_py,
+ arguments = '-f - ' + string.join(args),
+ chdir = t.workpath('WORK'),
+ stdin = Stdin % o.os)
+ out = string.replace(t.stdout(), t.workpath('ROOT'), '')
+ if out:
+ lines = string.split(out, '\n')
+ if lines:
+ while lines[-1] == '':
+ lines = lines[:-1]
+ for l in lines:
+ sys.stdout.write(p + l + '\n')
+ #err = t.stderr()
+ #if err:
+ # sys.stderr.write(err)
+ if o.data[0] == '\n':
+ o.data = o.data[1:]
+ sys.stdout.write(o.data + '</literallayout>')
+ delattr(self, 'o')
+ self.afunclist = self.afunclist[:-1]
+
+ def start_command(self, attrs):
+ try:
+ o = self.o
+ except AttributeError:
+ self.error("<command> tag outside of <scons_output>")
+ try:
+ o.prefix
+ except AttributeError:
+ o.prefix = o.data
+ o.data = ""
+ c = Command()
+ o.commandlist.append(c)
+ self.afunclist.append(c.afunc)
+
+ def end_command(self):
+ self.o.data = ""
+ self.afunclist = self.afunclist[:-1]
+
+ def start_sconstruct(self, attrs):
+ sys.stdout.write('<programlisting>')
+
+ def end_sconstruct(self):
+ sys.stdout.write('</programlisting>')
+
+try:
+ file = sys.argv[1]
+except IndexError:
+ file = '-'
+
+if file == '-':
+ f = sys.stdin
+else:
+ try:
+ f = open(file, 'r')
+ except IOError, msg:
+ print file, ":", msg
+ sys.exit(1)
+
+data = f.read()
+if f is not sys.stdin:
+ f.close()
+
+x = MySGML()
+for c in data:
+ x.feed(c)
+x.close()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/sconsoutput.py b/bin/sconsoutput.py
new file mode 100644
index 0000000..f99ec5a
--- /dev/null
+++ b/bin/sconsoutput.py
@@ -0,0 +1,827 @@
+#!/usr/bin/env python2
+
+#
+# Copyright (c) 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "/home/scons/sconsoutput/branch.0/baseline/src/sconsoutput.py 0.4.D001 2004/11/27 18:44:37 knight"
+
+#
+# sconsoutput.py - an SGML preprocessor for capturing SCons output
+# and inserting it into examples in our DocBook
+# documentation
+#
+# This script looks for some SGML tags that describe SCons example
+# configurations and commands to execute in those configurations, and
+# uses TestCmd.py to execute the commands and insert the output from
+# those commands into the SGML that we output. This way, we can run a
+# script and update all of our example documentation output without
+# a lot of laborious by-hand checking.
+#
+# An "SCons example" looks like this, and essentially describes a set of
+# input files (program source files as well as SConscript files):
+#
+# <scons_example name="ex1">
+# <file name="SConstruct" printme="1">
+# env = Environment()
+# env.Program('foo')
+# </file>
+# <file name="foo.c">
+# int main() { printf("foo.c\n"); }
+# </file>
+# </scons_example>
+#
+# The <file> contents within the <scons_example> tag will get written
+# into a temporary directory whenever example output needs to be
+# generated. By default, the <file> contents are not inserted into text
+# directly, unless you set the "printme" attribute on one or more files,
+# in which case they will get inserted within a <programlisting> tag.
+# This makes it easy to define the example at the appropriate
+# point in the text where you intend to show the SConstruct file.
+#
+# Note that you should usually give the <scons_example> a "name"
+# attribute so that you can refer to the example configuration later to
+# run SCons and generate output.
+#
+# If you just want to show a file's contents without worry about running
+# SCons, there's a shorter <sconstruct> tag:
+#
+# <sconstruct>
+# env = Environment()
+# env.Program('foo')
+# </sconstruct>
+#
+# This is essentially equivalent to <scons_example><file printme="1">,
+# but it's more straightforward.
+#
+# SCons output is generated from the following sort of tag:
+#
+# <scons_output example="ex1" os="posix">
+# <scons_output_command>scons -Q foo</scons_output_command>
+# <scons_output_command>scons -Q foo</scons_output_command>
+# </scons_output>
+#
+# You tell it which example to use with the "example" attribute, and then
+# give it a list of <scons_output_command> tags to execute. You can also
+# supply an "os" tag, which specifies the type of operating system this
+# example is intended to show; if you omit this, default value is "posix".
+#
+# The generated SGML will show the command line (with the appropriate
+# command-line prompt for the operating system), execute the command in
+# a temporary directory with the example files, capture the standard
+# output from SCons, and insert it into the text as appropriate.
+# Error output gets passed through to your error output so you
+# can see if there are any problems executing the command.
+#
+
+import os
+import os.path
+import re
+import sgmllib
+import string
+import sys
+import time
+
+sys.path.append(os.path.join(os.getcwd(), 'QMTest'))
+sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest'))
+
+scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py')
+if not os.path.exists(scons_py):
+ scons_py = os.path.join('src', 'script', 'scons.py')
+
+scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine')
+if not os.path.exists(scons_lib_dir):
+ scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine')
+
+os.environ['SCONS_LIB_DIR'] = scons_lib_dir
+
+import TestCmd
+
+# The regular expression that identifies entity references in the
+# standard sgmllib omits the underscore from the legal characters.
+# Override it with our own regular expression that adds underscore.
+sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]')
+
+# Classes for collecting different types of data we're interested in.
+class DataCollector:
+ """Generic class for collecting data between a start tag and end
+ tag. We subclass for various types of tags we care about."""
+ def __init__(self):
+ self.data = ""
+ def afunc(self, data):
+ self.data = self.data + data
+
+class Example(DataCollector):
+ """An SCons example. This is essentially a list of files that
+ will get written to a temporary directory to collect output
+ from one or more SCons runs."""
+ def __init__(self):
+ DataCollector.__init__(self)
+ self.files = []
+ self.dirs = []
+
+class File(DataCollector):
+ """A file, that will get written out to a temporary directory
+ for one or more SCons runs."""
+ def __init__(self, name):
+ DataCollector.__init__(self)
+ self.name = name
+
+class Directory(DataCollector):
+ """A directory, that will get created in a temporary directory
+ for one or more SCons runs."""
+ def __init__(self, name):
+ DataCollector.__init__(self)
+ self.name = name
+
+class Output(DataCollector):
+ """Where the command output goes. This is essentially
+ a list of commands that will get executed."""
+ def __init__(self):
+ DataCollector.__init__(self)
+ self.commandlist = []
+
+class Command(DataCollector):
+ """A tag for where the command output goes. This is essentially
+ a list of commands that will get executed."""
+ def __init__(self):
+ DataCollector.__init__(self)
+ self.output = None
+
+Prompt = {
+ 'posix' : '% ',
+ 'win32' : 'C:\\>'
+}
+
+# The magick SCons hackery that makes this work.
+#
+# So that our examples can still use the default SConstruct file, we
+# actually feed the following into SCons via stdin and then have it
+# SConscript() the SConstruct file. This stdin wrapper creates a set
+# of ToolSurrogates for the tools for the appropriate platform. These
+# Surrogates print output like the real tools and behave like them
+# without actually having to be on the right platform or have the right
+# tool installed.
+#
+# The upshot: The wrapper transparently changes the world out from
+# under the top-level SConstruct file in an example just so we can get
+# the command output.
+
+Stdin = """\
+import os
+import re
+import string
+import SCons.Action
+import SCons.Defaults
+import SCons.Node.FS
+
+platform = '%(osname)s'
+
+Sep = {
+ 'posix' : '/',
+ 'win32' : '\\\\',
+}[platform]
+
+
+# Slip our own __str__() method into the EntryProxy class used to expand
+# $TARGET{S} and $SOURCE{S} to translate the path-name separators from
+# what's appropriate for the system we're running on to what's appropriate
+# for the example system.
+orig = SCons.Node.FS.EntryProxy
+class MyEntryProxy(orig):
+ def __str__(self):
+ return string.replace(str(self._Proxy__subject), os.sep, Sep)
+SCons.Node.FS.EntryProxy = MyEntryProxy
+
+# Slip our own RDirs() method into the Node.FS.File class so that the
+# expansions of $_{CPPINC,F77INC,LIBDIR}FLAGS will have the path-name
+# separators translated from what's appropriate for the system we're
+# running on to what's appropriate for the example system.
+orig_RDirs = SCons.Node.FS.File.RDirs
+def my_RDirs(self, pathlist, orig_RDirs=orig_RDirs):
+ return map(lambda x: string.replace(str(x), os.sep, Sep),
+ orig_RDirs(self, pathlist))
+SCons.Node.FS.File.RDirs = my_RDirs
+
+class Curry:
+ def __init__(self, fun, *args, **kwargs):
+ self.fun = fun
+ self.pending = args[:]
+ self.kwargs = kwargs.copy()
+
+ def __call__(self, *args, **kwargs):
+ if kwargs and self.kwargs:
+ kw = self.kwargs.copy()
+ kw.update(kwargs)
+ else:
+ kw = kwargs or self.kwargs
+
+ return apply(self.fun, self.pending + args, kw)
+
+def Str(target, source, env, cmd=""):
+ result = []
+ for cmd in env.subst_list(cmd, target=target, source=source):
+ result.append(string.join(map(str, cmd)))
+ return string.join(result, '\\n')
+
+class ToolSurrogate:
+ def __init__(self, tool, variable, func, varlist):
+ self.tool = tool
+ if not type(variable) is type([]):
+ variable = [variable]
+ self.variable = variable
+ self.func = func
+ self.varlist = varlist
+ def __call__(self, env):
+ t = Tool(self.tool)
+ t.generate(env)
+ for v in self.variable:
+ orig = env[v]
+ try:
+ strfunction = orig.strfunction
+ except AttributeError:
+ strfunction = Curry(Str, cmd=orig)
+ # Don't call Action() through its global function name, because
+ # that leads to infinite recursion in trying to initialize the
+ # Default Environment.
+ env[v] = SCons.Action.Action(self.func,
+ strfunction=strfunction,
+ varlist=self.varlist)
+ def __repr__(self):
+ # This is for the benefit of printing the 'TOOLS'
+ # variable through env.Dump().
+ return repr(self.tool)
+
+def Null(target, source, env):
+ pass
+
+def Cat(target, source, env):
+ target = str(target[0])
+ f = open(target, "wb")
+ for src in map(str, source):
+ f.write(open(src, "rb").read())
+ f.close()
+
+def CCCom(target, source, env):
+ target = str(target[0])
+ fp = open(target, "wb")
+ def process(source_file, fp=fp):
+ for line in open(source_file, "rb").readlines():
+ m = re.match(r'#include\s[<"]([^<"]+)[>"]', line)
+ if m:
+ include = m.group(1)
+ for d in [str(env.Dir('$CPPPATH')), '.']:
+ f = os.path.join(d, include)
+ if os.path.exists(f):
+ process(f)
+ break
+ elif line[:11] != "STRIP CCCOM":
+ fp.write(line)
+ for src in map(str, source):
+ process(src)
+ fp.write('debug = ' + ARGUMENTS.get('debug', '0') + '\\n')
+ fp.close()
+
+public_class_re = re.compile('^public class (\S+)', re.MULTILINE)
+
+def JavaCCom(target, source, env):
+ # This is a fake Java compiler that just looks for
+ # public class FooBar
+ # lines in the source file(s) and spits those out
+ # to .class files named after the class.
+ tlist = map(str, target)
+ not_copied = {}
+ for t in tlist:
+ not_copied[t] = 1
+ for src in map(str, source):
+ contents = open(src, "rb").read()
+ classes = public_class_re.findall(contents)
+ for c in classes:
+ for t in filter(lambda x: string.find(x, c) != -1, tlist):
+ open(t, "wb").write(contents)
+ del not_copied[t]
+ for t in not_copied.keys():
+ open(t, "wb").write("\\n")
+
+def JavaHCom(target, source, env):
+ tlist = map(str, target)
+ slist = map(str, source)
+ for t, s in zip(tlist, slist):
+ open(t, "wb").write(open(s, "rb").read())
+
+def find_class_files(arg, dirname, names):
+ class_files = filter(lambda n: n[-6:] == '.class', names)
+ paths = map(lambda n, d=dirname: os.path.join(d, n), class_files)
+ arg.extend(paths)
+
+def JarCom(target, source, env):
+ target = str(target[0])
+ class_files = []
+ for src in map(str, source):
+ os.path.walk(src, find_class_files, class_files)
+ f = open(target, "wb")
+ for cf in class_files:
+ f.write(open(cf, "rb").read())
+ f.close()
+
+# XXX Adding COLOR, COLORS and PACKAGE to the 'cc' varlist(s) by hand
+# here is bogus. It's for the benefit of doc/user/command-line.in, which
+# uses examples that want to rebuild based on changes to these variables.
+# It would be better to figure out a way to do it based on the content of
+# the generated command-line, or else find a way to let the example markup
+# language in doc/user/command-line.in tell this script what variables to
+# add, but that's more difficult than I want to figure out how to do right
+# now, so let's just use the simple brute force approach for the moment.
+
+ToolList = {
+ 'posix' : [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
+ ('link', ['LINKCOM', 'SHLINKCOM'], Cat, []),
+ ('ar', ['ARCOM', 'RANLIBCOM'], Cat, []),
+ ('tar', 'TARCOM', Null, []),
+ ('zip', 'ZIPCOM', Null, []),
+ ('BitKeeper', 'BITKEEPERCOM', Cat, []),
+ ('CVS', 'CVSCOM', Cat, []),
+ ('RCS', 'RCS_COCOM', Cat, []),
+ ('SCCS', 'SCCSCOM', Cat, []),
+ ('javac', 'JAVACCOM', JavaCCom, []),
+ ('javah', 'JAVAHCOM', JavaHCom, []),
+ ('jar', 'JARCOM', JarCom, []),
+ ('rmic', 'RMICCOM', Cat, []),
+ ],
+ 'win32' : [('msvc', ['CCCOM', 'SHCCCOM', 'RCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
+ ('mslink', ['LINKCOM', 'SHLINKCOM'], Cat, []),
+ ('mslib', 'ARCOM', Cat, []),
+ ('tar', 'TARCOM', Null, []),
+ ('zip', 'ZIPCOM', Null, []),
+ ('BitKeeper', 'BITKEEPERCOM', Cat, []),
+ ('CVS', 'CVSCOM', Cat, []),
+ ('RCS', 'RCS_COCOM', Cat, []),
+ ('SCCS', 'SCCSCOM', Cat, []),
+ ('javac', 'JAVACCOM', JavaCCom, []),
+ ('javah', 'JAVAHCOM', JavaHCom, []),
+ ('jar', 'JARCOM', JarCom, []),
+ ('rmic', 'RMICCOM', Cat, []),
+ ],
+}
+
+toollist = ToolList[platform]
+filter_tools = string.split('%(tools)s')
+if filter_tools:
+ toollist = filter(lambda x, ft=filter_tools: x[0] in ft, toollist)
+
+toollist = map(lambda t: apply(ToolSurrogate, t), toollist)
+
+toollist.append('install')
+
+def surrogate_spawn(sh, escape, cmd, args, env):
+ pass
+
+def surrogate_pspawn(sh, escape, cmd, args, env, stdout, stderr):
+ pass
+
+SCons.Defaults.ConstructionEnvironment.update({
+ 'PLATFORM' : platform,
+ 'TOOLS' : toollist,
+ 'SPAWN' : surrogate_spawn,
+ 'PSPAWN' : surrogate_pspawn,
+})
+
+SConscript('SConstruct')
+"""
+
+# "Commands" that we will execute in our examples.
+def command_scons(args, c, test, dict):
+ save_vals = {}
+ delete_keys = []
+ try:
+ ce = c.environment
+ except AttributeError:
+ pass
+ else:
+ for arg in string.split(c.environment):
+ key, val = string.split(arg, '=')
+ try:
+ save_vals[key] = os.environ[key]
+ except KeyError:
+ delete_keys.append(key)
+ os.environ[key] = val
+ test.run(interpreter = sys.executable,
+ program = scons_py,
+ arguments = '-f - ' + string.join(args),
+ chdir = test.workpath('WORK'),
+ stdin = Stdin % dict)
+ os.environ.update(save_vals)
+ for key in delete_keys:
+ del(os.environ[key])
+ out = test.stdout()
+ out = string.replace(out, test.workpath('ROOT'), '')
+ out = string.replace(out, test.workpath('WORK/SConstruct'),
+ '/home/my/project/SConstruct')
+ lines = string.split(out, '\n')
+ if lines:
+ while lines[-1] == '':
+ lines = lines[:-1]
+ #err = test.stderr()
+ #if err:
+ # sys.stderr.write(err)
+ return lines
+
+def command_touch(args, c, test, dict):
+ if args[0] == '-t':
+ t = int(time.mktime(time.strptime(args[1], '%Y%m%d%H%M')))
+ times = (t, t)
+ args = args[2:]
+ else:
+ time.sleep(1)
+ times = None
+ for file in args:
+ if not os.path.isabs(file):
+ file = os.path.join(test.workpath('WORK'), file)
+ if not os.path.exists(file):
+ open(file, 'wb')
+ os.utime(file, times)
+ return []
+
+def command_edit(args, c, test, dict):
+ try:
+ add_string = c.edit[:]
+ except AttributeError:
+ add_string = 'void edit(void) { ; }\n'
+ if add_string[-1] != '\n':
+ add_string = add_string + '\n'
+ for file in args:
+ if not os.path.isabs(file):
+ file = os.path.join(test.workpath('WORK'), file)
+ contents = open(file, 'rb').read()
+ open(file, 'wb').write(contents + add_string)
+ return []
+
+def command_ls(args, c, test, dict):
+ def ls(a):
+ files = os.listdir(a)
+ files = filter(lambda x: x[0] != '.', files)
+ files.sort()
+ return [string.join(files, ' ')]
+ if args:
+ l = []
+ for a in args:
+ l.extend(ls(test.workpath('WORK', a)))
+ return l
+ else:
+ return ls(test.workpath('WORK'))
+
+CommandDict = {
+ 'scons' : command_scons,
+ 'touch' : command_touch,
+ 'edit' : command_edit,
+ 'ls' : command_ls,
+}
+
+def ExecuteCommand(args, c, t, dict):
+ try:
+ func = CommandDict[args[0]]
+ except KeyError:
+ func = lambda args, c, t, dict: []
+ return func(args[1:], c, t, dict)
+
+class MySGML(sgmllib.SGMLParser):
+ """A subclass of the standard Python 2.2 sgmllib SGML parser.
+
+ This extends the standard sgmllib parser to recognize, and do cool
+ stuff with, the added tags that describe our SCons examples,
+ commands, and other stuff.
+
+ Note that this doesn't work with the 1.5.2 sgmllib module, because
+ that didn't have the ability to work with ENTITY declarations.
+ """
+ def __init__(self):
+ sgmllib.SGMLParser.__init__(self)
+ self.examples = {}
+ self.afunclist = []
+
+ # The first set of methods here essentially implement pass-through
+ # handling of most of the stuff in an SGML file. We're really
+ # only concerned with the tags specific to SCons example processing,
+ # the methods for which get defined below.
+
+ def handle_data(self, data):
+ try:
+ f = self.afunclist[-1]
+ except IndexError:
+ sys.stdout.write(data)
+ else:
+ f(data)
+
+ def handle_comment(self, data):
+ sys.stdout.write('<!--' + data + '-->')
+
+ def handle_decl(self, data):
+ sys.stdout.write('<!' + data + '>')
+
+ def unknown_starttag(self, tag, attrs):
+ try:
+ f = self.example.afunc
+ except AttributeError:
+ f = sys.stdout.write
+ if not attrs:
+ f('<' + tag + '>')
+ else:
+ f('<' + tag)
+ for name, value in attrs:
+ f(' ' + name + '=' + '"' + value + '"')
+ f('>')
+
+ def unknown_endtag(self, tag):
+ sys.stdout.write('</' + tag + '>')
+
+ def unknown_entityref(self, ref):
+ sys.stdout.write('&' + ref + ';')
+
+ def unknown_charref(self, ref):
+ sys.stdout.write('&#' + ref + ';')
+
+ # Here is where the heavy lifting begins. The following methods
+ # handle the begin-end tags of our SCons examples.
+
+ def start_scons_example(self, attrs):
+ t = filter(lambda t: t[0] == 'name', attrs)
+ if t:
+ name = t[0][1]
+ try:
+ e = self.examples[name]
+ except KeyError:
+ e = self.examples[name] = Example()
+ else:
+ e = Example()
+ for name, value in attrs:
+ setattr(e, name, value)
+ self.e = e
+ self.afunclist.append(e.afunc)
+
+ def end_scons_example(self):
+ e = self.e
+ files = filter(lambda f: f.printme, e.files)
+ if files:
+ sys.stdout.write('<programlisting>')
+ for f in files:
+ if f.printme:
+ i = len(f.data) - 1
+ while f.data[i] == ' ':
+ i = i - 1
+ output = string.replace(f.data[:i+1], '__ROOT__', '')
+ output = string.replace(output, '<', '&lt;')
+ output = string.replace(output, '>', '&gt;')
+ sys.stdout.write(output)
+ if e.data and e.data[0] == '\n':
+ e.data = e.data[1:]
+ sys.stdout.write(e.data + '</programlisting>')
+ delattr(self, 'e')
+ self.afunclist = self.afunclist[:-1]
+
+ def start_file(self, attrs):
+ try:
+ e = self.e
+ except AttributeError:
+ self.error("<file> tag outside of <scons_example>")
+ t = filter(lambda t: t[0] == 'name', attrs)
+ if not t:
+ self.error("no <file> name attribute found")
+ try:
+ e.prefix
+ except AttributeError:
+ e.prefix = e.data
+ e.data = ""
+ f = File(t[0][1])
+ f.printme = None
+ for name, value in attrs:
+ setattr(f, name, value)
+ e.files.append(f)
+ self.afunclist.append(f.afunc)
+
+ def end_file(self):
+ self.e.data = ""
+ self.afunclist = self.afunclist[:-1]
+
+ def start_directory(self, attrs):
+ try:
+ e = self.e
+ except AttributeError:
+ self.error("<directory> tag outside of <scons_example>")
+ t = filter(lambda t: t[0] == 'name', attrs)
+ if not t:
+ self.error("no <directory> name attribute found")
+ try:
+ e.prefix
+ except AttributeError:
+ e.prefix = e.data
+ e.data = ""
+ d = Directory(t[0][1])
+ for name, value in attrs:
+ setattr(d, name, value)
+ e.dirs.append(d)
+ self.afunclist.append(d.afunc)
+
+ def end_directory(self):
+ self.e.data = ""
+ self.afunclist = self.afunclist[:-1]
+
+ def start_scons_example_file(self, attrs):
+ t = filter(lambda t: t[0] == 'example', attrs)
+ if not t:
+ self.error("no <scons_example_file> example attribute found")
+ exname = t[0][1]
+ try:
+ e = self.examples[exname]
+ except KeyError:
+ self.error("unknown example name '%s'" % exname)
+ fattrs = filter(lambda t: t[0] == 'name', attrs)
+ if not fattrs:
+ self.error("no <scons_example_file> name attribute found")
+ fname = fattrs[0][1]
+ f = filter(lambda f, fname=fname: f.name == fname, e.files)
+ if not f:
+ self.error("example '%s' does not have a file named '%s'" % (exname, fname))
+ self.f = f[0]
+
+ def end_scons_example_file(self):
+ f = self.f
+ sys.stdout.write('<programlisting>')
+ sys.stdout.write(f.data + '</programlisting>')
+ delattr(self, 'f')
+
+ def start_scons_output(self, attrs):
+ t = filter(lambda t: t[0] == 'example', attrs)
+ if not t:
+ self.error("no <scons_output> example attribute found")
+ exname = t[0][1]
+ try:
+ e = self.examples[exname]
+ except KeyError:
+ self.error("unknown example name '%s'" % exname)
+ # Default values for an example.
+ o = Output()
+ o.preserve = None
+ o.os = 'posix'
+ o.tools = ''
+ o.e = e
+ # Locally-set.
+ for name, value in attrs:
+ setattr(o, name, value)
+ self.o = o
+ self.afunclist.append(o.afunc)
+
+ def end_scons_output(self):
+ # The real raison d'etre for this script, this is where we
+ # actually execute SCons to fetch the output.
+ o = self.o
+ e = o.e
+ t = TestCmd.TestCmd(workdir='', combine=1)
+ if o.preserve:
+ t.preserve()
+ t.subdir('ROOT', 'WORK')
+ t.rootpath = string.replace(t.workpath('ROOT'), '\\', '\\\\')
+
+ for d in e.dirs:
+ dir = t.workpath('WORK', d.name)
+ if not os.path.exists(dir):
+ os.makedirs(dir)
+
+ for f in e.files:
+ i = 0
+ while f.data[i] == '\n':
+ i = i + 1
+ lines = string.split(f.data[i:], '\n')
+ i = 0
+ while lines[0][i] == ' ':
+ i = i + 1
+ lines = map(lambda l, i=i: l[i:], lines)
+ path = string.replace(f.name, '__ROOT__', t.rootpath)
+ if not os.path.isabs(path):
+ path = t.workpath('WORK', path)
+ dir, name = os.path.split(path)
+ if dir and not os.path.exists(dir):
+ os.makedirs(dir)
+ content = string.join(lines, '\n')
+ content = string.replace(content, '__ROOT__', t.rootpath)
+ path = t.workpath('WORK', path)
+ t.write(path, content)
+ if hasattr(f, 'chmod'):
+ os.chmod(path, int(f.chmod, 0))
+
+ i = len(o.prefix)
+ while o.prefix[i-1] != '\n':
+ i = i - 1
+
+ sys.stdout.write('<screen>' + o.prefix[:i])
+ p = o.prefix[i:]
+
+ for c in o.commandlist:
+ sys.stdout.write(p + Prompt[o.os])
+ d = string.replace(c.data, '__ROOT__', '')
+ sys.stdout.write('<userinput>' + d + '</userinput>\n')
+
+ e = string.replace(c.data, '__ROOT__', t.workpath('ROOT'))
+ args = string.split(e)
+ lines = ExecuteCommand(args, c, t, {'osname':o.os, 'tools':o.tools})
+ content = None
+ if c.output:
+ content = c.output
+ elif lines:
+ content = string.join(lines, '\n' + p)
+ if content:
+ content = re.sub(' at 0x[0-9a-fA-F]*\>', ' at 0x700000&gt;', content)
+ content = string.replace(content, '<', '&lt;')
+ content = string.replace(content, '>', '&gt;')
+ sys.stdout.write(p + content + '\n')
+
+ if o.data[0] == '\n':
+ o.data = o.data[1:]
+ sys.stdout.write(o.data + '</screen>')
+ delattr(self, 'o')
+ self.afunclist = self.afunclist[:-1]
+
+ def start_scons_output_command(self, attrs):
+ try:
+ o = self.o
+ except AttributeError:
+ self.error("<scons_output_command> tag outside of <scons_output>")
+ try:
+ o.prefix
+ except AttributeError:
+ o.prefix = o.data
+ o.data = ""
+ c = Command()
+ for name, value in attrs:
+ setattr(c, name, value)
+ o.commandlist.append(c)
+ self.afunclist.append(c.afunc)
+
+ def end_scons_output_command(self):
+ self.o.data = ""
+ self.afunclist = self.afunclist[:-1]
+
+ def start_sconstruct(self, attrs):
+ f = File('')
+ self.f = f
+ self.afunclist.append(f.afunc)
+
+ def end_sconstruct(self):
+ f = self.f
+ sys.stdout.write('<programlisting>')
+ output = string.replace(f.data, '__ROOT__', '')
+ sys.stdout.write(output + '</programlisting>')
+ delattr(self, 'f')
+ self.afunclist = self.afunclist[:-1]
+
+# The main portion of the program itself, short and simple.
+try:
+ file = sys.argv[1]
+except IndexError:
+ file = '-'
+
+if file == '-':
+ f = sys.stdin
+else:
+ try:
+ f = open(file, 'r')
+ except IOError, msg:
+ print file, ":", msg
+ sys.exit(1)
+
+data = f.read()
+if f is not sys.stdin:
+ f.close()
+
+if data.startswith('<?xml '):
+ first_line, data = data.split('\n', 1)
+ sys.stdout.write(first_line + '\n')
+
+x = MySGML()
+for c in data:
+ x.feed(c)
+x.close()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/sfsum b/bin/sfsum
new file mode 100644
index 0000000..a560b7d
--- /dev/null
+++ b/bin/sfsum
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+#
+# sfsum.py: A script for parsing XML data exported from
+# SourceForge projects.
+#
+# Right now, this is hard-coded to generate a summary of open bugs.
+#
+# XML data for SourceForge project is available for download by project
+# administrators. Because it's intended for backup purposes, you have
+# to slurp the whole set of data, including info about all of the closed
+# items, the feature requests, etc., so it can get big.
+#
+# You can do this by hand (if you're an administrator) with a URL like
+# this (where 30337 is the group_id for SCons):
+#
+# http://sourceforge.net/export/xml_export.php?group_id=30337
+#
+# They also have a Perl script, called xml_export, available as part
+# of a set of utilities called "adocman" which automate dealing with
+# SourceForge document management from the command line. "adocman"
+# is available at:
+#
+# https://sourceforge.net/projects/sitedocs/
+#
+
+import xml.sax
+import xml.sax.saxutils
+import string
+import sys
+
+SFName = {
+ 'Unassigned' : 'nobody',
+ 'Chad Austin' : 'aegis',
+ 'Charle Crain' : 'diewarzau',
+ 'Steven Knight' : 'stevenknight',
+ 'Steve Leblanc' : 'stevenleblanc',
+ 'Jeff Petkau' : 'jpet',
+ 'Anthony Roach' : 'anthonyroach',
+ 'Steven Shaw' : 'steven_shaw',
+ 'Terrel Shumway' : 'terrelshumway',
+ 'Greg Spencer' : 'greg_spencer',
+ 'Christoph Wiedemann' : 'wiedeman',
+}
+
+class Artifact:
+ """Just a place to hold attributes that we find in the XML."""
+ pass
+
+Artifacts = {}
+
+def nws(text):
+ """Normalize white space. This will become important if/when
+ we enhance this to search for arbitrary fields."""
+ return string.join(string.split(text), ' ')
+
+class ClassifyArtifacts(xml.sax.saxutils.DefaultHandler):
+ """
+ Simple SAX subclass to classify the artifacts in SourceForge
+ XML output.
+
+ This reads up the fields in an XML description and turns the field
+ descriptions into attributes of an Artificat object, on the fly.
+ Artifacts are of the following types:
+
+ Bugs
+ Feature Requests
+ Patches
+ Support Requests
+
+ We could, if we choose to, add additional types in the future
+ by creating additional trackers.
+
+ This class loses some info right now because we don't pay attention
+ to the <messages> tag in the output, which contains a list of items
+ that have <field> tags in them. Right now, these just overwrite
+ each other in the Arifact object we create.
+
+ We also don't pay attention to any attributes of a <field> tag other
+ than the "name" attribute. We'll need to extend this class if we
+ ever want to pay attention to those attributes.
+ """
+ def __init__(self):
+ self.artifact = None
+
+ def startElement(self, name, attrs):
+ self.text = ""
+ if name == 'artifact':
+ self.artifact = Artifact()
+ elif not self.artifact is None and name == 'field':
+ self.fname = attrs.get('name', None)
+
+ def characters(self, ch):
+ if not self.artifact is None:
+ self.text = self.text + ch
+
+ def endElement(self, name):
+ global Artifacts
+ if name == 'artifact':
+ type = self.artifact.artifact_type
+ try:
+ list = Artifacts[type]
+ except KeyError:
+ Artifacts[type] = list = []
+ list.append(self.artifact)
+ self.artifact = None
+ elif not self.artifact is None and name == 'field':
+ setattr(self.artifact, self.fname, self.text)
+
+if __name__ == '__main__':
+ # Create a parser.
+ parser = xml.sax.make_parser()
+ # Tell the parser we are not interested in XML namespaces.
+ parser.setFeature(xml.sax.handler.feature_namespaces, 0)
+
+ # Instantiate our handler and tell the parser to use it.
+ parser.setContentHandler(ClassifyArtifacts())
+
+ # Parse the input.
+ parser.parse(sys.argv[1])
+
+ # Hard-coded search for 'Open' bugs. This should be easily
+ # generalized once we figure out other things for this script to do.
+ bugs = filter(lambda x: x.status == 'Open', Artifacts['Bugs'])
+
+ print Artifacts.keys()
+
+ print "%d open bugs" % len(bugs)
+
+ # Sort them into a separate list for each assignee.
+ Assigned = {}
+ for bug in bugs:
+ a = bug.assigned_to
+ try:
+ list = Assigned[a]
+ except KeyError:
+ Assigned[a] = list = []
+ list.append(bug)
+
+ for a in SFName.keys():
+ try:
+ b = Assigned[SFName[a]]
+ except KeyError:
+ pass
+ else:
+ print " %s" % a
+ b.sort(lambda x, y: cmp(x.artifact_id, y.artifact_id))
+ for bug in b:
+ print " %-6s %s" % (bug.artifact_id, bug.summary)
diff --git a/bin/svn-bisect.py b/bin/svn-bisect.py
new file mode 100755
index 0000000..e9ebcf8
--- /dev/null
+++ b/bin/svn-bisect.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# -*- Python -*-
+
+import sys
+from math import log, ceil
+from optparse import OptionParser
+import subprocess
+
+# crack the command line
+parser = OptionParser(
+ usage="%prog lower-revision upper-revision test_script [arg1 ...]",
+ description="Binary search for a bug in a SVN checkout")
+(options,script_args) = parser.parse_args()
+
+# make sure we have sufficient parameters
+if len(script_args) < 1:
+ parser.error("Need a lower revision")
+elif len(script_args) < 2:
+ parser.error("Need an upper revision")
+elif len(script_args) < 3:
+ parser.error("Need a script to run")
+
+# extract our starting values
+lower = int(script_args[0])
+upper = int(script_args[1])
+script = script_args[2:]
+
+# print an error message and quit
+def error(s):
+ print >>sys.stderr, "******", s, "******"
+ sys.exit(1)
+
+# update to the specified version and run test
+def testfail(revision):
+ "Return true if test fails"
+ print "Updating to revision", revision
+ if subprocess.call(["svn","up","-qr",str(revision)]) != 0:
+ m = "SVN did not update properly to revision %d"
+ raise RuntimeError(m % revision)
+ return subprocess.call(script,shell=False) != 0
+
+# confirm that the endpoints are different
+print "****** Checking upper bracket", upper
+upperfails = testfail(upper)
+print "****** Checking lower bracket", lower
+lowerfails = testfail(lower)
+if upperfails == lowerfails:
+ error("Upper and lower revisions must bracket the failure")
+
+# binary search for transition
+msg = "****** max %d revisions to test (bug bracketed by [%d,%d])"
+while upper-lower > 1:
+ print msg % (ceil(log(upper-lower,2)), lower, upper)
+
+ mid = int((lower + upper)/2)
+ midfails = testfail(mid)
+ if midfails == lowerfails:
+ lower = mid
+ lowerfails = midfails
+ else:
+ upper = mid
+ upperfails = midfails
+
+# show which revision was first to fail
+if upperfails != lowerfails: lower = upper
+print "The error was caused by revision", lower
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/bin/time-scons.py b/bin/time-scons.py
new file mode 100644
index 0000000..78d26e5
--- /dev/null
+++ b/bin/time-scons.py
@@ -0,0 +1,355 @@
+#!/usr/bin/env python
+#
+# time-scons.py: a wrapper script for running SCons timings
+#
+# This script exists to:
+#
+# 1) Wrap the invocation of runtest.py to run the actual TimeSCons
+# timings consistently. It does this specifically by building
+# SCons first, so .pyc compilation is not part of the timing.
+#
+# 2) Provide an interface for running TimeSCons timings against
+# earlier revisions, before the whole TimeSCons infrastructure
+# was "frozen" to provide consistent timings. This is done
+# by updating the specific pieces containing the TimeSCons
+# infrastructure to the earliest revision at which those pieces
+# were "stable enough."
+#
+# By encapsulating all the logic in this script, our Buildbot
+# infrastructure only needs to call this script, and we should be able
+# to change what we need to in this script and have it affect the build
+# automatically when the source code is updated, without having to
+# restart either master or slave.
+
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import xml.sax.handler
+
+
+SubversionURL = 'http://scons.tigris.org/svn/scons'
+
+
+# This is the baseline revision when the TimeSCons scripts first
+# stabilized and collected "real," consistent timings. If we're timing
+# a revision prior to this, we'll forcibly update the TimeSCons pieces
+# of the tree to this revision to collect consistent timings for earlier
+# revisions.
+TimeSCons_revision = 4569
+
+# The pieces of the TimeSCons infrastructure that are necessary to
+# produce consistent timings, even when the rest of the tree is from
+# an earlier revision that doesn't have these pieces.
+TimeSCons_pieces = ['QMTest', 'timings', 'runtest.py']
+
+
+class CommandRunner:
+ """
+ Executor class for commands, including "commands" implemented by
+ Python functions.
+ """
+ verbose = True
+ active = True
+
+ def __init__(self, dictionary={}):
+ self.subst_dictionary(dictionary)
+
+ def subst_dictionary(self, dictionary):
+ self._subst_dictionary = dictionary
+
+ def subst(self, string, dictionary=None):
+ """
+ Substitutes (via the format operator) the values in the specified
+ dictionary into the specified command.
+
+ The command can be an (action, string) tuple. In all cases, we
+ perform substitution on strings and don't worry if something isn't
+ a string. (It's probably a Python function to be executed.)
+ """
+ if dictionary is None:
+ dictionary = self._subst_dictionary
+ if dictionary:
+ try:
+ string = string % dictionary
+ except TypeError:
+ pass
+ return string
+
+ def display(self, command, stdout=None, stderr=None):
+ if not self.verbose:
+ return
+ if type(command) == type(()):
+ func = command[0]
+ args = command[1:]
+ s = '%s(%s)' % (func.__name__, ', '.join(map(repr, args)))
+ if type(command) == type([]):
+ # TODO: quote arguments containing spaces
+ # TODO: handle meta characters?
+ s = ' '.join(command)
+ else:
+ s = self.subst(command)
+ if not s.endswith('\n'):
+ s += '\n'
+ sys.stdout.write(s)
+ sys.stdout.flush()
+
+ def execute(self, command, stdout=None, stderr=None):
+ """
+ Executes a single command.
+ """
+ if not self.active:
+ return 0
+ if type(command) == type(''):
+ command = self.subst(command)
+ cmdargs = shlex.split(command)
+ if cmdargs[0] == 'cd':
+ command = (os.chdir,) + tuple(cmdargs[1:])
+ if type(command) == type(()):
+ func = command[0]
+ args = command[1:]
+ return func(*args)
+ else:
+ if stdout is sys.stdout:
+ # Same as passing sys.stdout, except works with python2.4.
+ subout = None
+ elif stdout is None:
+ # Open pipe for anything else so Popen works on python2.4.
+ subout = subprocess.PIPE
+ else:
+ subout = stdout
+ if stderr is sys.stderr:
+ # Same as passing sys.stdout, except works with python2.4.
+ suberr = None
+ elif stderr is None:
+ # Merge with stdout if stderr isn't specified.
+ suberr = subprocess.STDOUT
+ else:
+ suberr = stderr
+ p = subprocess.Popen(command,
+ shell=(sys.platform == 'win32'),
+ stdout=subout,
+ stderr=suberr)
+ p.wait()
+ return p.returncode
+
+ def run(self, command, display=None, stdout=None, stderr=None):
+ """
+ Runs a single command, displaying it first.
+ """
+ if display is None:
+ display = command
+ self.display(display)
+ return self.execute(command, stdout, stderr)
+
+ def run_list(self, command_list, **kw):
+ """
+ Runs a list of commands, stopping with the first error.
+
+ Returns the exit status of the first failed command, or 0 on success.
+ """
+ status = 0
+ for command in command_list:
+ s = self.run(command, **kw)
+ if s and status == 0:
+ status = s
+ return 0
+
+
+def get_svn_revisions(branch, revisions=None):
+ """
+ Fetch the actual SVN revisions for the given branch querying
+ "svn log." A string specifying a range of revisions can be
+ supplied to restrict the output to a subset of the entire log.
+ """
+ command = ['svn', 'log', '--xml']
+ if revisions:
+ command.extend(['-r', revisions])
+ command.append(branch)
+ p = subprocess.Popen(command, stdout=subprocess.PIPE)
+
+ class SVNLogHandler(xml.sax.handler.ContentHandler):
+ def __init__(self):
+ self.revisions = []
+ def startElement(self, name, attributes):
+ if name == 'logentry':
+ self.revisions.append(int(attributes['revision']))
+
+ parser = xml.sax.make_parser()
+ handler = SVNLogHandler()
+ parser.setContentHandler(handler)
+ parser.parse(p.stdout)
+ return sorted(handler.revisions)
+
+
+def prepare_commands():
+ """
+ Returns a list of the commands to be executed to prepare the tree
+ for testing. This involves building SCons, specifically the
+ build/scons subdirectory where our packaging build is staged,
+ and then running setup.py to create a local installed copy
+ with compiled *.pyc files. The build directory gets removed
+ first.
+ """
+ commands = []
+ if os.path.exists('build'):
+ commands.extend([
+ ['mv', 'build', 'build.OLD'],
+ ['rm', '-rf', 'build.OLD'],
+ ])
+ commands.append([sys.executable, 'bootstrap.py', 'build/scons'])
+ commands.append([sys.executable,
+ 'build/scons/setup.py',
+ 'install',
+ '--prefix=' + os.path.abspath('build/usr')])
+ return commands
+
+def script_command(script):
+ """Returns the command to actually invoke the specified timing
+ script using our "built" scons."""
+ return [sys.executable, 'runtest.py', '-x', 'build/usr/bin/scons', script]
+
+def do_revisions(cr, opts, branch, revisions, scripts):
+ """
+ Time the SCons branch specified scripts through a list of revisions.
+
+ We assume we're in a (temporary) directory in which we can check
+ out the source for the specified revisions.
+ """
+ stdout = sys.stdout
+ stderr = sys.stderr
+
+ status = 0
+
+ if opts.logsdir and not opts.no_exec and len(scripts) > 1:
+ for script in scripts:
+ subdir = os.path.basename(os.path.dirname(script))
+ logsubdir = os.path.join(opts.origin, opts.logsdir, subdir)
+ if not os.path.exists(logsubdir):
+ os.makedirs(logsubdir)
+
+ for this_revision in revisions:
+
+ if opts.logsdir and not opts.no_exec:
+ log_name = '%s.log' % this_revision
+ log_file = os.path.join(opts.origin, opts.logsdir, log_name)
+ stdout = open(log_file, 'w')
+ stderr = None
+
+ commands = [
+ ['svn', 'co', '-q', '-r', str(this_revision), branch, '.'],
+ ]
+
+ if int(this_revision) < int(TimeSCons_revision):
+ commands.append(['svn', 'up', '-q', '-r', str(TimeSCons_revision)]
+ + TimeSCons_pieces)
+
+ commands.extend(prepare_commands())
+
+ s = cr.run_list(commands, stdout=stdout, stderr=stderr)
+ if s:
+ if status == 0:
+ status = s
+ continue
+
+ for script in scripts:
+ if opts.logsdir and not opts.no_exec and len(scripts) > 1:
+ subdir = os.path.basename(os.path.dirname(script))
+ lf = os.path.join(opts.origin, opts.logsdir, subdir, log_name)
+ out = open(lf, 'w')
+ err = None
+ else:
+ out = stdout
+ err = stderr
+ s = cr.run(script_command(script), stdout=out, stderr=err)
+ if s and status == 0:
+ status = s
+ if out not in (sys.stdout, None):
+ out.close()
+ out = None
+
+ if int(this_revision) < int(TimeSCons_revision):
+ # "Revert" the pieces that we previously updated to the
+ # TimeSCons_revision, so the update to the next revision
+ # works cleanly.
+ command = (['svn', 'up', '-q', '-r', str(this_revision)]
+ + TimeSCons_pieces)
+ s = cr.run(command, stdout=stdout, stderr=stderr)
+ if s:
+ if status == 0:
+ status = s
+ continue
+
+ if stdout not in (sys.stdout, None):
+ stdout.close()
+ stdout = None
+
+ return status
+
+Usage = """\
+time-scons.py [-hnq] [-r REVISION ...] [--branch BRANCH]
+ [--logsdir DIR] [--svn] SCRIPT ..."""
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ parser = optparse.OptionParser(usage=Usage)
+ parser.add_option("--branch", metavar="BRANCH", default="trunk",
+ help="time revision on BRANCH")
+ parser.add_option("--logsdir", metavar="DIR", default='.',
+ help="generate separate log files for each revision")
+ parser.add_option("-n", "--no-exec", action="store_true",
+ help="no execute, just print the command line")
+ parser.add_option("-q", "--quiet", action="store_true",
+ help="quiet, don't print the command line")
+ parser.add_option("-r", "--revision", metavar="REVISION",
+ help="time specified revisions")
+ parser.add_option("--svn", action="store_true",
+ help="fetch actual revisions for BRANCH")
+ opts, scripts = parser.parse_args(argv[1:])
+
+ if not scripts:
+ sys.stderr.write('No scripts specified.\n')
+ sys.exit(1)
+
+ CommandRunner.verbose = not opts.quiet
+ CommandRunner.active = not opts.no_exec
+ cr = CommandRunner()
+
+ os.environ['TESTSCONS_SCONSFLAGS'] = ''
+
+ branch = SubversionURL + '/' + opts.branch
+
+ if opts.svn:
+ revisions = get_svn_revisions(branch, opts.revision)
+ elif opts.revision:
+ # TODO(sgk): parse this for SVN-style revision strings
+ revisions = [opts.revision]
+ else:
+ revisions = None
+
+ if opts.logsdir and not os.path.exists(opts.logsdir):
+ os.makedirs(opts.logsdir)
+
+ if revisions:
+ opts.origin = os.getcwd()
+ tempdir = tempfile.mkdtemp(prefix='time-scons-')
+ try:
+ os.chdir(tempdir)
+ status = do_revisions(cr, opts, branch, revisions, scripts)
+ finally:
+ os.chdir(opts.origin)
+ shutil.rmtree(tempdir)
+ else:
+ commands = prepare_commands()
+ commands.extend([ script_command(script) for script in scripts ])
+ status = cr.run_list(commands, stdout=sys.stdout, stderr=sys.stderr)
+
+ return status
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/bin/timebuild b/bin/timebuild
new file mode 100644
index 0000000..d5af983
--- /dev/null
+++ b/bin/timebuild
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# Profile running SCons to build itself from the current package.
+#
+# This runs "aegis -build" to build a current scons-src-*.tar.gz
+# package, unpacks it in the supplied directory name, and then
+# starts a profiled run of an SCons build, followed by another.
+# This results in two profiles:
+#
+# NAME/NAME-0.prof
+# profile of a build-everything run
+#
+# NAME/NAME-1.prof
+# profile of an all-up-to-date run
+#
+# This also copies the build scons-src-*.tar.gz file to the NAME
+# subdirectory, and tars up everything under src/ as NAME/src.tar.gz,
+# so that repeated runs with different in-progress changes can serve
+# as their own crude version control, so you don't lose that exact
+# combination of features which performed best.
+
+if test X$1 = X; then
+ echo "Must supply name!" >&2
+ exit 1
+fi
+
+VERSION=0.90
+
+DIR=$1
+
+SRC="scons-src-$VERSION"
+SRC_TAR_GZ="${SRC}.tar.gz"
+B_D_SRC_TAR_GZ="build/dist/${SRC_TAR_GZ}"
+
+echo "Building ${B_D_SRC_TAR_GZ}: " `date`
+aegis -build ${B_D_SRC_TAR_GZ}
+
+echo "mkdir ${DIR}: " `date`
+mkdir ${DIR}
+
+echo "cp ${B_D_SRC_TAR_GZ} ${DIR}: " `date`
+cp ${B_D_SRC_TAR_GZ} ${DIR}
+
+echo "tar cf ${DIR}/src.tar.gz: " `date`
+tar cf ${DIR}/src.tar.gz src
+
+cd ${DIR}
+
+echo "tar zxf ${SRC_TAR_GZ}: " `date`
+tar zxf ${SRC_TAR_GZ}
+
+cd ${SRC}
+
+SCRIPT="src/script/scons.py"
+ARGS="version=$VERSION"
+
+export SCONS_LIB_DIR=`pwd`/src/engine
+
+echo "Build run starting: " `date`
+python $SCRIPT --profile=../$DIR-0.prof $ARGS > ../$DIR-0.log 2>&1
+
+echo "Up-to-date run starting: " `date`
+python $SCRIPT --profile=../$DIR-1.prof $ARGS > ../$DIR-1.log 2>&1
+
+echo "Finished $DIR at: " `date`
diff --git a/bin/xml_export b/bin/xml_export
new file mode 100644
index 0000000..bc9ccbd
--- /dev/null
+++ b/bin/xml_export
@@ -0,0 +1,225 @@
+#!/usr/bin/perl -w
+#
+# xml_export - Retrieve data from the SF.net XML export for project data
+#
+# Copyright (C) 2002 Open Source Development Network, Inc. ("OSDN")
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the license details found
+# below in the section marked "$LICENSE_TEXT".
+#
+# SCons: modified the following RCS Id line so it won't expand during
+# our checkins.
+#
+# $_Id: adocman,v 1.51 2002/06/07 18:56:35 moorman Exp _$
+#
+# Written by Nate Oostendorp <oostendo@sourceforge.net>
+# and Jacob Moorman <moorman@sourceforge.net>
+###########################################################################
+
+use strict;
+use Alexandria::Client;
+use HTTP::Request::Common;
+my $client = new Alexandria::Client;
+
+util_verifyvariables("groupid");
+
+my $res = $ua->simple_request(GET "$config{hosturl}/export/xml_export.php?group_id=$config{groupid}");
+
+if (not $res->is_success()) {
+ die "Failed to connect: ".$res->as_string();
+}
+print $res->content;
+
+###########################################################################
+
+__END__
+=head1 NAME
+
+xml_export - Retrieve data for a project via the SF.net XML export facility
+
+=head1 DESCRIPTION
+
+B<This program> provides a simple mechanism to download data from the
+XML data export facility on SourceForge.net. This utility is needed
+(in place of a downloader like wget or curl) since authentication by
+a project administrator is required to access the XML export facility.
+
+=head1 SYNOPSIS
+
+xml_export [options] > output_file
+
+ OPTIONS
+ --login Login to the SourceForge.net site
+ --logout Logout of the SourceForge.net site
+ --groupid=GROUPID Group ID of the project whose data you wish to export
+
+=head1 ERROR LEVELS
+
+The following error levels are returned upon exit of this program:
+
+ 0 success
+
+ 1 failure: general (requested DocManager operation failed)
+
+ 2 failure: authentication failure
+
+ 3 failure: must --login before performing this operation
+
+ 4 failure: bad command-line option specified or variable setting problem
+
+ 5 failure: error in accessing/creating a file or directory
+
+ 6 failure: failed to enter requested input before timeout expired
+
+=head1 AUTHORITATIVE SOURCE
+
+The original version of B<this program> may be found in the materials
+provided from the SourceForge.net Site Documentation project (sitedocs)
+on the SourceForge.net site. The latest version of this program
+may be found in the CVS repository for the sitedocs project on
+SourceForge.net. The sitedocs project pages may be accessed at:
+http://sourceforge.net/projects/sitedocs
+
+=head1 SECURITY
+
+For security-related information for this application, please review
+the documentation provided for the adocman utility.
+
+=head1 EXAMPLES
+
+The following are examples for using this program to export project
+data via the XML data export facility on SourceForge.net. It is presumed
+that you have a valid SourceForge.net user account, which is listed as
+a project administrator on the project in question. This tool will
+only work for project administrators. The group ID for the project
+may be derived from the URL for the Admin page for the project, or by
+viewing the Project Admin page for the project (look for the text
+"Your Group ID is: xxxxxx").
+
+To login to the SourceForge.net site via the command-line:
+
+ adocman --username=myusername --password=mypassword --login \
+ --groupid=8675309
+
+To login to the SourceForge.net site, and be prompted to enter your
+password interactively:
+
+ adocman --username=myusername --interactive --login --groupid=8675309
+
+To perform an export (after logging-in):
+
+ xml_export --groupid=8675309 > output.xml
+
+To logout of SourceForge.net:
+
+ adocman --logout
+
+Additional capabilities (including the use of configuration files to
+specify information that would otherwise be provided interactively
+or on the command-line) are detailed in the documentation provided for
+the adocman utility.
+
+To obtain output for debugging a problem, perform the same command
+as originally tested, but first add the --verbose flag, and determine
+whether you are able to solve the issue on your own. If the problem
+persists, see the "SUPPORT AND BUGS" section, below.
+
+=head1 SUPPORT AND BUGS
+
+This program was written by a member of the SourceForge.net staff
+team. This software has been released under an Open Source license,
+for the greater benefit of the SourceForge.net developer community.
+
+The SourceForge.net Site Documentation project is the caretaker of
+this software. Issues related to the use of this program, or bugs
+found in using this program, may be reported to the SourceForge.net
+Site Documentation project using their Support Request Tracker at:
+https://sourceforge.net/tracker/?func=add&group_id=52614&atid=467457
+
+Any support that is provided for this program is provided as to
+further enhance the stability and functionality of this program
+for SourceForge.net users. The SourceForge.net Site Documentation
+project makes use of this software for its own internal purposes,
+in managing the Site Documentation collection for the SourceForge.net
+site.
+
+=head1 AUTHOR
+
+Nathan Oostendorp <oostendo@sourceforge.net> and
+Jacob Moorman <moorman@sourceforge.net>
+
+=head1 PREREQUISITES
+
+C<LWP::UserAgent>, C<HTML::TokeParser>, C<Crypt::SSLeay>, C<Digest::MD5>,
+C<Term::ReadKey>
+
+These prerequisites may be installed in an interactive, but automated
+fashion through the use of perl's CPAN module, invoked as:
+
+ perl -MCPAN -e shell;
+
+=head1 LICENSE
+
+Copyright (c) 2002 Open Source Development Network, Inc. ("OSDN")
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+2. Neither the names of VA Software Corporation, OSDN, SourceForge.net,
+the SourceForge.net Site Documentation project, nor the names of its
+contributors may be used to endorse or promote products derived from
+the Software without specific prior written permission of OSDN.
+
+3. The name and trademarks of copyright holders may NOT be used in
+advertising or publicity pertaining to the Software without specific,
+written prior permission. Title to copyright in the Software and
+any associated documentation will at all times remain with copyright
+holders.
+
+4. If any files are modified, you must cause the modified files to carry
+prominent notices stating that you changed the files and the date of
+any change. We recommend that you provide URLs to the location from which
+the code is derived.
+
+5. Altered versions of the Software must be plainly marked as such, and
+must not be misrepresented as being the original Software.
+
+6. The origin of the Software must not be misrepresented; you must not
+claim that you wrote the original Software. If you use the Software in a
+product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+7. The data files supplied as input to, or produced as output from,
+the programs of the Software do not automatically fall under the
+copyright of the Software, but belong to whomever generated them, and may
+be sold commercially, and may be aggregated with the Software.
+
+8. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE OR DOCUMENTATION.
+
+This Software consists of contributions made by OSDN and many individuals
+on behalf of OSDN. Specific attributions are listed in the accompanying
+credits file.
+
+=head1 HISTORY
+
+B<2002-12-03> Completed version 0.10 - move to classes, added POD
+
+=cut
diff --git a/bin/xml_export-LICENSE b/bin/xml_export-LICENSE
new file mode 100644
index 0000000..3f06fb7
--- /dev/null
+++ b/bin/xml_export-LICENSE
@@ -0,0 +1,52 @@
+Copyright (c) 2002 Open Source Development Network, Inc. ("OSDN")
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+2. Neither the names of VA Software Corporation, OSDN, SourceForge.net,
+the SourceForge.net Site Documentation project, nor the names of its
+contributors may be used to endorse or promote products derived from
+the Software without specific prior written permission of OSDN.
+
+3. The name and trademarks of copyright holders may NOT be used in
+advertising or publicity pertaining to the Software without specific,
+written prior permission. Title to copyright in the Software and
+any associated documentation will at all times remain with copyright
+holders.
+
+4. If any files are modified, you must cause the modified files to carry
+prominent notices stating that you changed the files and the date of
+any change. We recommend that you provide URLs to the location from which
+the code is derived.
+
+5. Altered versions of the Software must be plainly marked as such, and
+must not be misrepresented as being the original Software.
+
+6. The origin of the Software must not be misrepresented; you must not
+claim that you wrote the original Software. If you use the Software in a
+product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+7. The data files supplied as input to, or produced as output from,
+the programs of the Software do not automatically fall under the
+copyright of the Software, but belong to whomever generated them, and may
+be sold commercially, and may be aggregated with the Software.
+
+8. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE OR DOCUMENTATION.
+
+This Software consists of contributions made by OSDN and many individuals
+on behalf of OSDN. Specific attributions are listed in the accompanying
+credits file.
diff --git a/bin/xml_export-README b/bin/xml_export-README
new file mode 100644
index 0000000..82e3524
--- /dev/null
+++ b/bin/xml_export-README
@@ -0,0 +1,56 @@
+This copy of xml_export was snarfed from adocman-0.10 from SourceForge.
+We're checking in a copy as a convenience for any future SCons project
+administrator who may need to download exported XML data. The original,
+unmodified contents of the README file for that release of adocman are
+as follows:
+
+
+adocman - Automation tool for SourceForge.net DocManager handling
+Copyright (C) 2002 Open Source Development Network, Inc. ("OSDN")
+
+
+File manifest:
+
+Alexandria perl-based API for performing operations against the
+ SourceForge.net site, currently including basic Client
+ operations (i.e. login/logout) and DocManager operations
+
+adocman The adocman program, providing the means to perform
+ DocManager operations from the command-line or scripts
+ (by project developers or admins listed as DocManager Editors)
+
+xml_export The xml_export program, providing the means to automate
+ downloads of data from the XML data export facility
+ on SourceForge.net (by project administrators)
+
+adocman.html Manual for adocman, including background information,
+ command-line options detail, etc.
+
+xml_export.html Manual for xml_export, including basic info about
+ command-line options. See adocman.html for additional
+ information.
+
+LICENSE License terms for adocman
+
+README This file
+
+TODO List of ongoing work in improving adocman. NOTE:
+ Please contact the maintainer before starting any effort
+ to improve this code. We have significantly modified
+ the structure and design of this program for the next
+ release; structure and command-line interface are subject
+ to change without notice.
+
+A list of the prerequisites required to execute 'adocman' may be found
+at in the PREREQUISITES section of the adocman manual (adocman.html).
+Though not listed, a recent installation of 'perl' is also a prerequisite.
+
+Support for this program may be obtained as per the SUPPORT AND BUGS
+section of the adocman.html manual. Any questions or concerns regarding
+this software should be escalated as per the SUPPORT AND BUGS section
+of the provided manual.
+
+The authoritative source of this software is:
+ https://sourceforge.net/projects/sitedocs
+
+
diff --git a/bin/xmlagenda.py b/bin/xmlagenda.py
new file mode 100755
index 0000000..3009e4c
--- /dev/null
+++ b/bin/xmlagenda.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+# Download the issues from Issuzilla as XML; this creates a file named
+# 'issues.xml'. Run this script to translate 'issues.xml' into a CSV
+# file named 'editlist.csv'. Upload the CSV into a Google spreadsheet.
+
+# In the spreadsheet, select the last column and pick "delete-->column" (it
+# was added by the upload to allow for expansion and we don't need it).
+# Select all the columns and pick "align-->top"
+# Select the ID and votes columns and pick "align-->right"
+# Select the priority column and pick "align-->center"
+# Select the first row and click on the "bold" button
+# Grab the lines between the column headers to adjust the column widths
+# Grab the sort bar on the far left (just above the "1" for row one)
+# and move it down one row. (Row one becomes a floating header)
+# Voila!
+
+# The team members
+# FIXME: These names really should be external to this script
+team = 'Bill Greg Steven Gary Ken Brandon Sohail Jim David'.split()
+team.sort()
+
+# The elements to be picked out of the issue
+PickList = [
+ # sort key -- these are used to sort the entry
+ 'target_milestone', 'priority', 'votes_desc', 'creation_ts',
+ # payload -- these are displayed
+ 'issue_id', 'votes', 'issue_type', 'target_milestone',
+ 'priority', 'assigned_to', 'short_desc',
+ ]
+
+# Conbert a leaf element into its value as a text string
+# We assume it's "short enough" that there's only one substring
+def Value(element):
+ v = element.firstChild
+ if v is None: return ''
+ return v.nodeValue
+
+# Parse the XML issues file and produce a DOM for it
+import sys
+if len(sys.argv) > 1: xml = sys.argv[1]
+else: xml = 'issues.xml'
+from xml.dom.minidom import parse
+xml = parse(xml)
+
+# Go through the issues in the DOM, pick out the elements we want,
+# and put them in our list of issues.
+issues = []
+for issuezilla in xml.childNodes:
+ # The Issuezilla element contains the issues
+ if issuezilla.nodeType != issuezilla.ELEMENT_NODE: continue
+ for issue in issuezilla.childNodes:
+ # The issue elements contain the info for an issue
+ if issue.nodeType != issue.ELEMENT_NODE: continue
+ # Accumulate the pieces we want to include
+ d = {}
+ for element in issue.childNodes:
+ if element.nodeName in PickList:
+ d[element.nodeName] = Value(element)
+ # convert 'votes' to numeric, ascending and descending
+ try:
+ v = int('0' + d['votes'])
+ except KeyError:
+ pass
+ else:
+ d['votes_desc'] = -v
+ d['votes'] = v
+ # Marshal the elements and add them to the list
+ issues.append([ d[ix] for ix in PickList ])
+issues.sort()
+
+# Transcribe the issues into comma-separated values.
+# FIXME: parameterize the output file name
+import csv
+writer = csv.writer(open('editlist.csv', 'w'))
+# header
+writer.writerow(['ID', 'Votes', 'Type/Member', 'Milestone',
+ 'Pri', 'Owner', 'Summary/Comments'])
+for issue in issues:
+ row = issue[4:] # strip off sort key
+ #row[0] = """=hyperlink("http://scons.tigris.org/issues/show_bug.cgi?id=%s","%s")""" % (row[0],row[0])
+ if row[3] == '-unspecified-': row[3] = 'triage'
+ writer.writerow(['','','','','','',''])
+ writer.writerow(row)
+ writer.writerow(['','','consensus','','','',''])
+ writer.writerow(['','','','','','',''])
+ for member in team: writer.writerow(['','',member,'','','',''])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/config b/config
new file mode 100644
index 0000000..b055bdb
--- /dev/null
+++ b/config
@@ -0,0 +1,299 @@
+/*
+ * MANIFEST: use of SCons in project config file to build itself
+ *
+ * SCons has a Repository feature, introduced in SCons 0.09, that was
+ * designed to work well with Aegis.
+ */
+
+/*
+ * The build_command field of the project config file is used to invoke
+ * the relevant build command. This command tells SCons where to find
+ * the rules.
+ *
+ * Our chicken-and-egg dilemma is this: we want to use the version of
+ * SCons under development in an Aegis change to build itself. But the
+ * pieces of SCons are likely only partly in this change, and partly in
+ * baselines.
+ *
+ * Python only imports things on a module-by-module basis--which is to
+ * say, once it finds __init__.py in a given directory, it assumes that
+ * all other files in that module are in the same directory. But that's
+ * not the way Aegis works, because if a file hasn't changed on the
+ * branch, it will only be in its parent's baseline directory.
+ *
+ * Aegis' mechanism for working around this sort of problem is to make
+ * symlinks to the proper baseline versions of each file, which makes
+ * it look like everything is in the local tree. That's unattractive,
+ * though, because we really want to eat our own dog food and use the
+ * SCons -Y options to pull things from the baseline repositories.
+ *
+ * So our solution (suggested by Anthony Roach) is a bootstrap.py script
+ * that does some Aegis-like searching through the baseline directories
+ * and makes a bootstrap copy of the version of SCons under development
+ * that we can use for building. After it makes this copy of SCons, it
+ * executes it with the same command-line arguments we supplied (and
+ * setting $SCONS_LIB_DIR to the right directory) so we can use it
+ * here with command-line options as if it were SCons itself. (Note,
+ * however, that bootstrap.py only understands the specific command-line
+ * options already in use here, so if you change the call below to add
+ * some other SCons options, you may have to modify bootstrap.py to
+ * recognize them.
+ *
+ * The ${Source bootstrap.py} substitution finds bootstrap.py wherever
+ * it may be in the Aegis baselines.
+ *
+ * The long -Y${SUBSTitute...} substitution takes the Aegis baseline
+ * search path and turns it into the right -Y command-line options for
+ * SCons.
+ *
+ * The rest of the substitutions (${DEVeloper}, etc.) should be obvious.
+ *
+ * Look in aesub(5) for more information about command substitutions.
+ */
+build_command = "python2.1 ${Source bootstrap.py} -Y${SUBSTitute : \\ -Y $Search_Path} date='${DAte %Y/%m/%d %H:%M:%S}' developer=${DEVeloper} version=${VERsion} change=${Change}";
+
+/*
+ * SCons removes its targets before constructing them, which qualifies it
+ * for the following entry in the config file. The files must be removed
+ * first, otherwise the baseline would cease to be self-consistent.
+ */
+
+link_integration_directory = true;
+
+/*
+ * This is set temporarily to allow us to build using the SCons
+ * currently checked in to the src directory.
+create_symlinks_before_build = true;
+ */
+
+/*
+ * aegis - project change supervisor
+ * This file is in the Public Domain, 1995, 1998 Peter Miller.
+ *
+ * MANIFEST: example of using rcs in the project config file
+ *
+ * The entries for the commands are listed below. RCS uses a slightly
+ * different model than aegis wants, so some maneuvering is required.
+ * The command strings in this section assume that the RCS commands ci and co
+ * and rcs and rlog are in the command search PATH, but you may like to
+ * hard-wire the paths, or set PATH at the start of each. You should also note
+ * that the strings are always handed to the Bourne shell to be executed, and
+ * are set to exit with an error immediately a sub-command fails.
+ *
+ * In these commands, the RCS file is kept unlocked, since only the owner will
+ * be checking changes in. The RCS functionality for coordinating shared
+ * access is not required.
+ *
+ * One advantage of using RCS version 5.6 or later is that binary files are
+ * supported, should you want to have binary files in the baseline.
+ *
+ * The ${quote ...} construct is used to quote filenames which contain
+ * shell special characters. A minimum of quoting is performed, so if
+ * the filenames do not contail shell special characters, no quotes will
+ * be used.
+ */
+
+/*
+ * This command is used to create a new file history.
+ * This command is always executed as the project owner.
+ * The following substitutions are available:
+ *
+ * ${Input}
+ * absolute path of the source file
+ * ${History}
+ * absolute path of the history file
+ *
+ * The "ci -f" option is used to specify that a copy is to be checked-in even
+ * if there are no changes.
+ * The "ci -u" option is used to specify that an unlocked copy will remain in
+ * the baseline.
+ * The "ci -d" option is used to specify that the file time rather than the
+ * current time is to be used for the new revision.
+ * The "ci -M" option is used to specify that the mode date on the original
+ * file is not to be altered.
+ * The "ci -t" option is used to specify that there is to be no description
+ * text for the new RCS file.
+ * The "ci -m" option is used to specify that the change number is to be stored
+ * in the file log if this is actually an update (typically from aenf
+ * after aerm on the same file name).
+ * The "rcs -U" option is used to specify that the new RCS file is to have
+ * unstrict locking.
+ * The "rcs -kk" option is used to specify that keyword substitution is
+ * disabled (only keyword names, not values, are substituted).
+ */
+history_create_command =
+ "ci -f -u -d -M -m$c -t/dev/null ${quote $input} ${quote $history,v}; \
+rcs -kk -U ${quote $history,v}";
+
+
+/*
+ * This command is used to get a specific edit back from history.
+ * This command is always executed as the project owner.
+ * The following substitutions are available:
+ *
+ * ${History}
+ * absolute path of the history file
+ * ${Edit}
+ * edit number, as given by history_\%query_\%command
+ * ${Output}
+ * absolute path of the destination file
+ *
+ * The "co -r" option is used to specify the edit to be retrieved.
+ * The "co -p" option is used to specify that the results be printed on the
+ * standard output; this is because the destination filename will never
+ * look anything like the history source filename.
+ * The "rcs -kk" option is used to specify that keyword substitution is
+ * disabled (only keyword names, not values, are substituted).
+ */
+history_get_command =
+ "co -kk -r${quote $edit} -p ${quote $history,v} > ${quote $output}";
+
+/*
+ * This command is used to add a new "top-most" entry to the history file.
+ * This command is always executed as the project owner.
+ * The following substitutions are available:
+ *
+ * ${Input}
+ * absolute path of source file
+ * ${History}
+ * absolute path of history file
+ *
+ * The "ci -f" option is used to specify that a copy is to be checked-in even
+ * if there are no changes.
+ * The "ci -u" option is used to specify that an unlocked copy will remain in
+ * the baseline.
+ * The "ci -d" option is used to specify that the file time rather than the
+ * current time is to be used for the new revision.
+ * The "ci -M" option is used to specify that the mode date on the original
+ * file is not to be altered.
+ * The "ci -m" option is used to specify that the change number is to be stored
+ * in the file log, which allows rlog to be used to find the change
+ * numbers to which each revision of the file corresponds.
+ *
+ * It is possible for a a very cautious approach has been taken, in which case
+ * the history_put_command may be set to the same string specified above for
+ * the history_create_command.
+ */
+history_put_command =
+ "ci -f -u -d -M -m$c ${quote $input} ${quote $history,v}";
+
+/*
+ * This command is used to query what the history mechanism calls the top-most
+ * edit of a history file. The result may be any arbitrary string, it need not
+ * be anything like a number, just so long as it uniquely identifies the edit
+ * for use by the history_get_command at a later date. The edit number is to
+ * be printed on the standard output. This command is always executed as the
+ * project owner.
+ *
+ * The following substitutions are available:
+ *
+ * ${History}
+ * absolute path of the history file
+ */
+history_query_command =
+ "rlog -r ${quote $history,v} | awk '/^head:/ {print $$2}'";
+
+/*
+ * RCS also provides a merge program, which can be used to provide a three-way
+ * merge. It has an ouput format some sites prefer to the fmerge output.
+ *
+ * This command is used by aed(1) to produce a difference listing when a file
+ * in the development directory is out of date compared to the current version
+ * in the baseline.
+ *
+ * All of the command substitutions described in aesub(5) are available.
+ * In addition, the following substitutions are also available:
+ *
+ * ${ORiginal}
+ * The absolute path name of a file containing the common ancestor
+ * version of ${MostRecent} and {$Input}. Usually the version originally
+ * copied into the change. Usually in a temporary file.
+ * ${Most_Recent}
+ * The absolute path name of a file containing the most recent version.
+ * Usually in the baseline.
+ * ${Input}
+ * The absolute path name of the edited version of the file. Usually in
+ * the development directory.
+ * ${Output}
+ * The absolute path name of the file in which to write the difference
+ * listing. Usually in the development directory.
+ *
+ * An exit status of 0 means successful, even of the files differ (and they
+ * usually do). An exit status which is non-zero means something is wrong.
+ *
+ * The "merge -L" options are used to specify labels for the baseline and the
+ * development directory, respecticvely, when conflict lines are inserted
+ * into the result.
+ * The "merge -p" options is used to specify that the results are to be printed
+ * on the standard output.
+ */
+
+diff3_command =
+ "set +e; \
+merge -p -L baseline -L C$c ${quote $mostrecent} ${quote $original} \
+${quote $input} > ${quote $output}; \
+test $? -le 1";
+
+/*
+ * The diff command in Red Hat 8.0 changed the exit status so it *fails*
+ * when *it* thinks it's trying to diff a binary (non-ASCII-text) file.
+ * The -a option disables this behavior and makes diff's exit status
+ * behave like it used to, even on any binary files we have checked in.
+ */
+
+diff_command =
+ "set +e; \
+ diff -a -c ${quote $original} ${quote $input} > ${quote $output}; \
+ test $? -le 1";
+
+/*
+ * We use a runtest.py script to execute tests. This takes care of
+ * massaging environment variables and the like to test against the
+ * unpacked package in the current directory.
+ *
+ * Note that we must include $spe in the batch_test_command line (so
+ * that Aegis thinks we're smart about testing ourselves against the
+ * baseline) but we don't actually need it. Our tests always run
+ * relative to the package built under the current directory, which
+ * is set appropriately during a baseline test. So we just use the
+ * proper aesub variable to comment out the expanded $spe.
+ */
+test_command = "python1.5 ${Source runtest.py Absolute} --noqmtest -p tar-gz -t -v ${SUBSTitute '\\.[CD][0-9]+$' '' ${VERsion}} -q --sp ${Search_Path} --spe ${Search_Path_Executable} ${File_Name}";
+
+batch_test_command = "python1.5 ${Source runtest.py Absolute} --noqmtest -p tar-gz -t -v ${SUBSTitute '\\.[CD][0-9]+$' '' ${VERsion}} -o ${Output} --aegis --sp ${Search_Path} --spe ${Search_Path_Executable} ${File_Names}";
+
+new_test_filename = "test/CHANGETHIS.py";
+
+/*
+ *
+ */
+file_template =
+[
+ {
+ pattern = [ "src/engine/*__init__.py" ];
+ body = "${read_file ${source template/__init__.py abs}}";
+ },
+ {
+ pattern = [ "src/engine/*Tests.py" ];
+ body = "${read_file ${source template/Tests.py abs}}";
+ },
+ {
+ pattern = [ "src/engine/*.py" ];
+ body = "${read_file ${source template/file.py abs}}";
+ },
+ {
+ pattern = [ "test/*.py" ];
+ body = "${read_file ${source template/test.py abs}}";
+ },
+];
+
+/*
+ * Command for distributing changes from Aegis to all of the repositories
+ * we want to mirror the information.
+ *
+ * XXX Uncomment after upgrading to an Aegis version that supports this.
+
+integrate_pass_notify_command =
+ "$sh ${s bin/scons-cdist} -p $project $change";
+ *
+ */
diff --git a/doc/MANIFEST b/doc/MANIFEST
new file mode 100644
index 0000000..008afab
--- /dev/null
+++ b/doc/MANIFEST
@@ -0,0 +1 @@
+scons.mod
diff --git a/doc/SConscript b/doc/SConscript
new file mode 100644
index 0000000..fd18ef4
--- /dev/null
+++ b/doc/SConscript
@@ -0,0 +1,530 @@
+#
+# SConscript file for building SCons documentation.
+#
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import os.path
+import re
+import string
+
+Import('build_dir', 'env', 'whereis')
+
+env = env.Clone()
+
+build = os.path.join(build_dir, 'doc')
+
+#
+#
+#
+dist_doc_tar_gz = '$DISTDIR/scons-doc-${VERSION}.tar.gz'
+
+#
+# We'll only try to build text files (for some documents)
+# if lynx is available to do the dump.
+#
+fig2dev = whereis('fig2dev')
+epydoc = whereis('epydoc')
+groff = whereis('groff')
+lynx = whereis('lynx')
+man2html = whereis('man2html')
+jade_original = whereis('jade')
+jade = whereis('openjade') or jade_original
+jadetex = whereis('jadetex')
+pdfjadetex = whereis('pdfjadetex')
+jw = whereis('jw')
+tidy = whereis('tidy')
+
+tar_deps = []
+tar_list = []
+
+entity_re = re.compile(r'<!entity\s+(?:%\s+)?(?:\S+)\s+SYSTEM\s+"([^"]*)">', re.I)
+format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?')
+
+#
+# Find internal dependencies in .xml files:
+#
+# <!entity bground SYSTEM "bground.xml">
+# <graphic fileref="file.jpg">
+# <imagedata fileref="file.jpg">
+#
+# This only finds one per line, and assumes that anything
+# defined as a SYSTEM entity is, in fact, a file included
+# somewhere in the document.
+#
+def scanxml(node, env, target):
+ includes = []
+
+ contents = node.get_text_contents()
+
+ includes.extend(entity_re.findall(contents))
+
+ matches = format_re.findall(contents)
+ for m in matches:
+ file, format = m
+ if format and file[-len(format):] != format:
+ file = file + '.' + format
+ if not os.path.isabs(file):
+ a = []
+ f = file
+ while f:
+ f, tail = os.path.split(f)
+ if tail == 'doc':
+ break
+ a = [tail] + a
+ file = apply(os.path.join, a, {})
+ includes.append(file)
+
+ return includes
+
+s = Scanner(name = 'xml', function = scanxml, skeys = ['.xml', '.mod'])
+
+orig_env = env
+env = orig_env.Clone(SCANNERS = [s],
+ SCONS_PROC_PY = File('#bin/scons-proc.py').rfile(),
+ SCONSOUTPUT_PY = File('#bin/sconsoutput.py').rfile())
+
+# Fetch the list of files in the build engine that contain
+# SCons documentation XML for processing.
+def chop(s): return s[:-1]
+
+# If we ever read doc from __scons_doc__ strings in *.py files again,
+# here's how it's done:
+#manifest_in = File('#src/engine/MANIFEST.in').rstr()
+#manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr()
+#scons_doc_files = map(chop, open(manifest_in).readlines() +\
+# open(manifest_xml_in).readlines())
+#scons_doc_files = map(lambda x: '#src/engine/'+x, scons_doc_files)
+#manifest_in = File('#src/engine/MANIFEST.in').rstr()
+
+manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr()
+scons_doc_files = map(chop, open(manifest_xml_in).readlines())
+scons_doc_files = map(lambda x: File('#src/engine/'+x).rstr(), scons_doc_files)
+
+if not jw:
+ print "jw not found, skipping building User Guide."
+else:
+ #
+ # Always create a version.xml file containing the version information
+ # for this run. Ignore it for dependency purposes so we don't
+ # rebuild all the docs every time just because the date changes.
+ #
+ date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION')
+ version_xml = File(os.path.join(build, "version.xml"))
+ #version_xml = File("version.xml")
+ verfile = str(version_xml)
+ try:
+ os.unlink(verfile)
+ except OSError:
+ pass # okay if the file didn't exist
+ dir, f = os.path.split(verfile)
+ try:
+ os.makedirs(dir)
+ except OSError:
+ pass # okay if the directory already exists
+ open(verfile, "w").write("""<!--
+THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
+-->
+<!ENTITY builddate "%s">
+<!ENTITY buildversion "%s">
+<!ENTITY buildrevision "%s">
+""" % (date, ver, rev))
+
+ builders_gen = os.path.join(build, 'user', 'builders.gen')
+ builders_mod = os.path.join(build, 'user', 'builders.mod')
+ tools_gen = os.path.join(build, 'user', 'tools.gen')
+ tools_mod = os.path.join(build, 'user', 'tools.mod')
+ variables_gen = os.path.join(build, 'user', 'variables.gen')
+ variables_mod = os.path.join(build, 'user', 'variables.mod')
+
+ # We put $( - $) around $SOURCES in the command line below because
+ # the path names will change when a given input file is found in
+ # a repository one run and locally the next, and we don't want
+ # to rebuild documentation just because it's found in one location
+ # vs. the other. The *.gen and *.mod targets will still be dependent
+ # on the list of the files themselves.
+ doc_output_files = [builders_gen, builders_mod,
+ tools_gen, tools_mod,
+ variables_gen, variables_mod]
+ b = env.Command(doc_output_files,
+ scons_doc_files,
+ "$PYTHON $SCONS_PROC_PY --xml -b ${TARGETS[0]},${TARGETS[1]} -t ${TARGETS[2]},${TARGETS[3]} -v ${TARGETS[4]},${TARGETS[5]} $( $SOURCES $)")
+ env.Depends(b, "$SCONS_PROC_PY")
+
+ env.Local(b)
+
+ #
+ # Each document will live in its own subdirectory. List them here
+ # as hash keys, with a hash of the info to control its build.
+ #
+ docs = {
+ 'design' : {
+ 'htmlindex' : 'book1.html',
+ 'ps' : 1,
+ 'pdf' : 1,
+ 'text' : 0,
+ },
+ # This doesn't build on all systems, and the document is old
+ # enough that there's reallyno need to build it every time any
+ # more, so just comment it out for now.
+ #'python10' : {
+ # 'htmlindex' : 't1.html',
+ # 'html' : 1,
+ # 'ps' : 1,
+ # 'pdf' : 0,
+ # 'text' : 0,
+ # 'graphics' : [
+ # 'arch.fig',
+ # 'builder.fig',
+ # 'job-task.fig',
+ # 'node.fig',
+ # 'scanner.fig',
+ # 'sig.fig'
+ # ],
+ #},
+ 'reference' : {
+ 'htmlindex' : 'book1.html',
+ 'html' : 1,
+ 'ps' : 1,
+ 'pdf' : 1,
+ 'text' : 0,
+ },
+ # For whenever (if ever?) we start putting developer guide
+ # information in a printable document instead of the wiki.
+ #'developer' : {
+ # 'htmlindex' : 'book1.html',
+ # 'html' : 1,
+ # 'ps' : 1,
+ # 'pdf' : 1,
+ # 'text' : 0,
+ #},
+ 'user' : {
+ 'htmlindex' : 'book1.html',
+ 'html' : 1,
+ 'ps' : 1,
+ 'pdf' : 1,
+ 'text' : 1,
+ 'graphics' : [
+ 'SCons-win32-install-1.jpg',
+ 'SCons-win32-install-2.jpg',
+ 'SCons-win32-install-3.jpg',
+ 'SCons-win32-install-4.jpg',
+ ],
+ 'sconsoutput' : 1,
+ },
+ }
+
+ #
+ # We have to tell SCons to scan the top-level XML files which
+ # get included by the document XML files in the subdirectories.
+ #
+ manifest = File('MANIFEST').rstr()
+ src_files = map(lambda x: x[:-1], open(manifest).readlines())
+ for s in src_files:
+ base, ext = os.path.splitext(s)
+ if ext in ['.fig', '.jpg']:
+ orig_env.Install(build, s)
+ else:
+ orig_env.SCons_revision(os.path.join(build, s), s)
+ Local(os.path.join(build, s))
+
+ #
+ # For each document, build the document itself in HTML, Postscript,
+ # and PDF formats.
+ #
+ for doc in docs.keys():
+ manifest = File(os.path.join(doc, 'MANIFEST')).rstr()
+ src_files = map(lambda x: x[:-1],
+ open(manifest).readlines())
+ build_doc = docs[doc].get('sconsoutput') and int(ARGUMENTS.get('BUILDDOC', 0))
+ for s in src_files:
+ doc_s = os.path.join(doc, s)
+ build_s = os.path.join(build, doc, s)
+ base, ext = os.path.splitext(doc_s)
+ if ext in ['.fig', '.jpg']:
+ orig_env.InstallAs(build_s, doc_s)
+ else:
+ if build_doc and ext == '.xml':
+ env.Command(doc_s,
+ base + '.in',
+ "$PYTHON $SCONSOUTPUT_PY $SOURCE > $TARGET")
+ orig_env.SCons_revision(build_s, doc_s)
+ Local(build_s)
+
+ main = os.path.join(build, doc, 'main.xml')
+ out = 'main.out'
+
+ # Hard-coding the scons-src path is a bit of a hack. This can
+ # be reworked when a better solution presents itself.
+ scons_src_main = os.path.join(build_dir, 'scons-src', 'doc', main)
+ env.Ignore(scons_src_main, version_xml)
+
+ htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc)
+ htmlindex = os.path.join(htmldir, docs[doc]['htmlindex'])
+ html = os.path.join(build, 'HTML', 'scons-%s.html' % doc)
+ ps = os.path.join(build, 'PS', 'scons-%s.ps' % doc)
+ pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc)
+ text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc)
+
+ if docs[doc].get('html') and jade:
+ def copy_index_html(target, source, env):
+ # Older versions of DocBook|jw|jade|whatever would
+ # create a book1.html file, while newer versions create
+ # an index.html file (logically enough). The scons.org
+ # web site links expect book1.html, so we're going to
+ # leave the target as is, and run this post-processing
+ # action function to check that the target really did
+ # get created, and if it didn't, copy it from index.html.
+ t = str(target[0])
+ if not os.path.exists(t):
+ i = os.path.join(os.path.split(t)[0], 'index.html')
+ open(t, 'w').write(open(i, 'r').read())
+ return None
+
+ cmds = [
+ Delete("${TARGET.dir}/*.html"),
+ "jw -b html -o ${TARGET.dir} $SOURCES",
+ ]
+ if tidy:
+ cmds.append("tidy -m -q $TARGET || true")
+ cmds.append(Action(copy_index_html))
+ env.Command(htmlindex, File(main), cmds)
+ Local(htmlindex)
+
+ cmds = [
+ Delete("${TARGET.dir}/main.html"),
+ "jw -u -b html -o ${TARGET.dir} $SOURCES",
+ Move("$TARGET", "${TARGET.dir}/main.html"),
+ ]
+ if tidy:
+ cmds.append("tidy -m -q $TARGET || true")
+ env.Command(html, File(main), cmds)
+ Local(html)
+
+ env.Ignore([html, htmlindex], version_xml)
+
+ tar_deps.extend([html, htmlindex])
+ tar_list.extend([html, htmldir])
+
+ for g in docs[doc].get('graphics', []):
+ base, ext = os.path.splitext(g)
+ if ext == '.fig':
+ jpg = base + '.jpg'
+ htmldir_jpg = os.path.join(htmldir, jpg)
+ if fig2dev:
+ fig = os.path.join(build, doc, g)
+ env.Command(htmldir_jpg, fig,
+ "%s -L jpeg -q 100 $SOURCES $TARGET" % fig2dev)
+ else:
+ env.InstallAs(htmldir_jpg, jpg)
+ env.Depends(html, htmldir_jpg)
+ Local(htmldir_jpg)
+ else:
+ src = os.path.join(build, doc, g)
+ Local(env.Install(htmldir, src))
+
+ if docs[doc].get('ps') and jadetex and jade_original:
+ env.Command(ps, main, [
+ Delete("${TARGET.dir}/%s" % out),
+ "jw -b ps -o ${TARGET.dir} -p %s $SOURCES" % jade_original,
+ "mv ${TARGET.dir}/main.ps $TARGET",
+ Delete("${TARGET.dir}/%s" % out),
+ ])
+ Local(ps)
+
+ env.Ignore(ps, version_xml)
+
+ tar_deps.append(ps)
+ tar_list.append(ps)
+
+ for g in docs[doc].get('graphics', []):
+ base, ext = os.path.splitext(g)
+ if ext == '.fig':
+ eps = base + '.eps'
+ build_eps = os.path.join(build, 'PS', eps)
+ if fig2dev:
+ fig = os.path.join(build, doc, g)
+ env.Command(build_eps, fig, "%s -L eps $SOURCES $TARGET" % fig2dev)
+ else:
+ env.InstallAs(build_eps, eps)
+ env.Depends(ps, build_eps)
+ Local(build_eps)
+ else:
+ src = os.path.join(build, doc, g)
+ Local(env.Install(htmldir, src))
+
+ if docs[doc].get('pdf') and pdfjadetex and jade_original:
+ env.Command(pdf, main, [
+ Delete("${TARGET.dir}/%s" % out),
+ "jw -b pdf -o ${TARGET.dir} -p %s $SOURCES" % jade_original,
+ "mv ${TARGET.dir}/main.pdf $TARGET",
+ Delete("${TARGET.dir}/out"),
+ ])
+ Local(pdf)
+
+ env.Ignore(pdf, version_xml)
+
+ tar_deps.append(pdf)
+ tar_list.append(pdf)
+
+ if docs[doc].get('text') and jade and lynx:
+ env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET")
+ Local(text)
+
+ env.Ignore(text, version_xml)
+
+ tar_deps.append(text)
+ tar_list.append(text)
+
+#
+# Man page(s), in good ol' troff format.
+#
+man_page_list = ['scons.1', 'sconsign.1', 'scons-time.1']
+
+for m in man_page_list:
+ x = orig_env.SCons_revision(os.path.join(build, 'man', m),
+ os.path.join('man', m))
+
+man_i_files = ['builders.man', 'tools.man', 'variables.man']
+
+man_intermediate_files = map(lambda x: os.path.join(build, 'man', x),
+ man_i_files)
+
+cmd = "$PYTHON $SCONS_PROC_PY --man -b ${TARGETS[0]} -t ${TARGETS[1]} -v ${TARGETS[2]} $( $SOURCES $)"
+man_intermediate_files = env.Command(man_intermediate_files,
+ scons_doc_files,
+ cmd)
+env.Depends(man_intermediate_files, "$SCONS_PROC_PY")
+Local(man_intermediate_files)
+
+for man_1 in man_page_list:
+ man, _1 = os.path.splitext(man_1)
+
+ man_1 = os.path.join(build, 'man', man_1)
+
+ if groff:
+ ps = os.path.join(build, 'PS', '%s-man.ps' % man)
+ text = os.path.join(build, 'TEXT', '%s-man.txt' % man)
+
+ b = env.Command(ps, man_1, "( cd ${SOURCES.dir} && groff -man -Tps ${SOURCES.file} ) > $TARGET")
+ Local(ps)
+ env.Depends(b, man_intermediate_files)
+
+ b = env.Command(text, man_1, "( cd ${SOURCES.dir} && groff -man -Tascii ${SOURCES.file} ) > $TARGET")
+ Local(text)
+ env.Depends(b, man_intermediate_files)
+
+ tar_deps.extend([ps, text])
+ tar_list.extend([ps, text])
+
+ if man2html:
+ html = os.path.join(build, 'HTML' , '%s-man.html' % man)
+
+ def strip_to_first_html_tag(target, source, env):
+ t = str(target[0])
+ contents = open(t).read()
+ contents = contents[string.find(contents, '<HTML>'):]
+ open(t, 'w').write(contents)
+ return 0
+
+ cmds = [
+ "( cd %s/man && cp %s .. )" % (build, string.join(man_i_files)),
+ "( cd ${SOURCE.dir} && man2html ${SOURCE.file} ) > $TARGET",
+ Action(strip_to_first_html_tag),
+ ]
+ if tidy:
+ cmds.append("tidy -m -q $TARGET || true")
+ b = env.Command(html, man_1, cmds)
+ Local(html)
+ env.Depends(b, man_intermediate_files)
+
+ tar_deps.append(html)
+ tar_list.append(html)
+
+if not epydoc:
+ print "epydoc not found, skipping building API documentation."
+else:
+ # XXX Should be in common with reading the same thing in
+ # the SConstruct file.
+ e = os.path.join('#src', 'engine')
+ manifest_in = File(os.path.join(e, 'MANIFEST.in')).rstr()
+ sources = map(lambda x: x[:-1], open(manifest_in).readlines())
+ sources = filter(lambda x: string.find(x, 'Optik') == -1, sources)
+ sources = filter(lambda x: string.find(x, 'Platform') == -1, sources)
+ sources = filter(lambda x: string.find(x, 'Tool') == -1, sources)
+ # XXX
+ sources = filter(lambda x: string.find(x, 'Options') == -1, sources)
+
+ e = os.path.join(build, '..', 'scons', 'engine')
+ sources = map(lambda x, e=e: os.path.join(e, x), sources)
+
+ epydoc_commands = [
+ Delete('$OUTDIR'),
+ '$EPYDOC $EPYDOCFLAGS --debug --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES',
+ Touch('$TARGET'),
+ ]
+
+ htmldir = os.path.join(build, 'HTML', 'scons-api')
+ env.Command('${OUTDIR}/index.html', sources, epydoc_commands,
+ EPYDOC=epydoc, EPYDOCFLAGS='--html', OUTDIR=htmldir)
+ tar_deps.append(htmldir)
+ tar_list.append(htmldir)
+
+ # PDF and PostScript and TeX are built from the
+ # same invocation.
+ api_dir = os.path.join(build, 'scons-api')
+ api_pdf = os.path.join(api_dir, 'api.pdf')
+ api_ps = os.path.join(api_dir, 'api.ps')
+ api_tex = os.path.join(api_dir, 'api.tex')
+ api_targets = [api_pdf, api_ps, api_tex]
+ env.Command(api_targets, sources, epydoc_commands,
+ EPYDOC=epydoc, EPYDOCFLAGS='--pdf', OUTDIR=api_dir)
+ Local(api_targets)
+
+ pdf_install = os.path.join(build, 'PDF', 'scons-api.pdf')
+ env.InstallAs(pdf_install, api_pdf)
+ tar_deps.append(pdf_install)
+ tar_list.append(pdf_install)
+ Local(pdf_install)
+
+ ps_install = os.path.join(build, 'PS', 'scons-api.ps')
+ env.InstallAs(ps_install, api_ps)
+ tar_deps.append(ps_install)
+ tar_list.append(ps_install)
+ Local(ps_install)
+
+#
+# Now actually create the tar file of the documentation,
+# for easy distribution to the web site.
+#
+if tar_deps:
+ tar_list = string.join(map(lambda x, b=build+'/': string.replace(x, b, ''),
+ tar_list))
+ t = env.Command(dist_doc_tar_gz, tar_deps,
+ "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list))
+ AddPostAction(dist_doc_tar_gz, Chmod(dist_doc_tar_gz, 0644))
+ Local(t)
+ Alias('doc', t)
+else:
+ Alias('doc', os.path.join(build_dir, 'doc'))
diff --git a/doc/design/MANIFEST b/doc/design/MANIFEST
new file mode 100644
index 0000000..33ab8f0
--- /dev/null
+++ b/doc/design/MANIFEST
@@ -0,0 +1,14 @@
+acks.xml
+bground.xml
+copyright.xml
+engine.fig
+engine.jpg
+engine.xml
+goals.xml
+install.xml
+intro.xml
+issues.xml
+main.xml
+native.xml
+overview.xml
+scons.mod
diff --git a/doc/design/acks.xml b/doc/design/acks.xml
new file mode 100644
index 0000000..b1a8a58
--- /dev/null
+++ b/doc/design/acks.xml
@@ -0,0 +1,179 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ I'm grateful to the following people
+ for their influence, knowing or not,
+ on the design of &SCons;:
+
+ </para>
+
+ <variablelist>
+ <varlistentry>
+ <term>Bob Sidebotham</term>
+ <listitem>
+ <para>
+
+ First, as the original author of &Cons;, Bob did the real heavy
+ lifting of creating the underlying model for dependency management
+ and software construction, as well as implementing it in Perl.
+ During the first years of &Cons;' existence, Bob did a skillful
+ job of integrating input and code from the first users, and
+ consequently is a source of practical wisdom and insight into the
+ problems of real-world software construction. His continuing
+ advice has been invaluable.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>The &SCons; Development Team</term>
+ <listitem>
+ <para>
+
+ A big round of thanks go to those brave souls who have
+ gotten in on the ground floor:
+ David Abrahams,
+ Charles Crain,
+ Steven Leblanc.
+ Anthony Roach,
+ and
+ Steven Shaw.
+ Their contributions,
+ through their general knowledge of software build issues in general
+ Python in particular,
+ have made &SCons; what it is today.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>The &Cons; Community</term>
+ <listitem>
+ <para>
+
+ The real-world build problems that the users of &Cons;
+ share on the <command>cons-discuss</command> mailing list
+ have informed much of the thinking that
+ has gone into the &SCons; design.
+ In particular,
+ Rajesh Vaidheeswarran,
+ the current maintainer of &Cons;,
+ has been a very steady influence.
+ I've also picked up valuable insight from
+ mailing-list participants
+ Johan Holmberg,
+ Damien Neil,
+ Gary Oberbrunner,
+ Wayne Scott,
+ and Greg Spencer.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Peter Miller</term>
+ <listitem>
+
+ <para>
+
+ Peter has indirectly
+ influenced two aspects of the &SCons; design:
+
+ </para>
+
+ <para>
+
+ Miller's influential paper
+ <citetitle>Recursive Make Considered Harmful</citetitle>
+ was what led me, indirectly, to my involvement with &Cons;
+ in the first place.
+ Experimenting with the single-Makefile approach he describes in
+ <citetitle>RMCH</citetitle> led me to conclude that while it worked
+ as advertised, it was not an extensible scheme. This solidified
+ my frustration with Make and led me to try &Cons;, which at its
+ core shares the single-process, universal-DAG model of the "RMCH"
+ single-Makefile technique.
+
+ </para>
+
+ <para>
+
+ The testing framework that Miller created for his
+ Aegis change management system
+ changed the way I approach software development
+ by providing a framework for rigorous, repeatable
+ testing during development.
+ It was my success at using Aegis for personal projects
+ that led me to begin my involvement with &Cons;
+ by creating the <command>cons-test</command> regression suite.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Stuart Stanley</term>
+ <listitem>
+ <para>
+
+ An experienced Python programmer,
+ Stuart provided valuable advice and insight
+ into some of the more useful Python idioms at my disposal
+ during the original <literal>ScCons</literal>; design
+ for the Software Carpentry contest.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Gary Holt</term>
+ <listitem>
+ <para>
+
+ I don't know which came first,
+ the first-round Software Carpentry contest entry
+ or the tool itself,
+ but Gary's design for &Makepp;
+ showed me that it is possible to marry
+ the strengths of &Cons;-like dependency management
+ with backwards compatibility for &Makefile;s.
+ Striving to support both
+ &Makefile; compatibility and
+ a native Python interface
+ cleaned up the &SCons; design immeasurably
+ by factoring out the common elements
+ into the Build Engine.
+
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
diff --git a/doc/design/bground.xml b/doc/design/bground.xml
new file mode 100644
index 0000000..c404e86
--- /dev/null
+++ b/doc/design/bground.xml
@@ -0,0 +1,86 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ Most of the ideas in &SCons; originate with &Cons;, a Perl-based
+ software construction utility that has been in use by a small but
+ growing community since its development by Bob Sidebotham at FORE
+ Systems in 1996. The &Cons; copyright was transferred in 2000 from
+ Marconi (who purchased FORE Systems) to the Free Software Foundation.
+ I've been a principal implementer and maintainer of &Cons; for several
+ years.
+
+ </para>
+
+ <para>
+
+ &Cons; was originally designed to handle complicated software build
+ problems (multiple directories, variant builds) while keeping the
+ input files simple and maintainable. The general philosophy is that
+ the build tool should ``do the right thing'' with minimal input
+ from an unsophisticated user, while still providing a rich set of
+ underlying functionality for more complicated software construction
+ tasks needed by experts.
+
+ </para>
+
+ <para>
+
+ In 2000, the Software Carpentry sought entries in a contest for a
+ new, Python-based build tool that would provide an improvement
+ over Make for physical scientists and other non-programmers
+ struggling to use their computers more effectively. Prior to that,
+ the idea of combining the superior build architecture of &Cons;
+ with the easier syntax of Python had come up several times on
+ the <literal>cons-discuss</literal> mailing list. The Software
+ Carpentry contest provided the right motivation to spend some
+ actual time working on a design document.
+
+ </para>
+
+ <para>
+
+ After two rounds of competition, the submitted design, named
+ <application>ScCons</application>, won the competition. Software
+ Carpentry, however, did not immediately fund implementation of the
+ build tool, instead contracting for additional, more detailed draft(s)
+ of the design document. This proved to be not as strong motivation as
+ actual coding, and after several months of inactivity, I essentially
+ resigned from the Software Carpentry effort in early 2001 to start
+ working on the tool independently.
+
+ </para>
+
+ <para>
+
+ After half a year of prototyping some of the important infrastructure,
+ I accumulated enough code to take the project public at SourceForge,
+ renaming it &SCons; to distinguish it slightly from the version of the
+ design that won the Software Carpentry contest while still honoring
+ its roots there and in the original &Cons; utility. And also because
+ it would be a teensy bit easier to type.
+
+ </para>
diff --git a/doc/design/copyright.xml b/doc/design/copyright.xml
new file mode 100644
index 0000000..d73906e
--- /dev/null
+++ b/doc/design/copyright.xml
@@ -0,0 +1,39 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<blockquote>
+ <para>
+
+ Copyright (c) 2001 Steven Knight
+
+ Portions of this document, by the same author, were previously
+ published Copyright 2000 by CodeSourcery LLC, under the Software Carpentry
+ Open Publication License, the terms of which are available at
+ <ulink url="http://www.software-carpentry.com/openpub-license.html">
+ http://www.software-carpentry.com/openpub-license.html
+ </ulink>.
+
+ </para>
+</blockquote>
diff --git a/doc/design/engine.fig b/doc/design/engine.fig
new file mode 100644
index 0000000..90f3d4f
--- /dev/null
+++ b/doc/design/engine.fig
@@ -0,0 +1,179 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 2100 8700 3600 9300
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 2100 8700 3600 8700 3600 9300 2100 9300 2100 8700
+4 0 0 100 0 18 14 0.0000 4 165 900 2400 9075 Node.FS\001
+-6
+6 7050 6900 9000 7500
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 7050 6900 9000 6900 9000 7500 7050 7500 7050 6900
+4 0 0 100 0 18 14 0.0000 4 165 1530 7200 7275 Intercessor.FS\001
+-6
+6 9450 6900 11400 7500
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 9450 6900 11400 6900 11400 7500 9450 7500 9450 6900
+4 0 0 100 0 18 14 0.0000 4 165 1560 9600 7275 Intercessor.DB\001
+-6
+6 1200 4200 2400 4800
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 1200 4200 2400 4200 2400 4800 1200 4800 1200 4200
+4 0 0 100 0 18 14 0.0000 4 165 870 1350 4575 Scanner\001
+-6
+6 2400 3300 3600 3900
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 2400 3300 3600 3300 3600 3900 2400 3900 2400 3300
+4 0 0 100 0 18 14 0.0000 4 165 750 2625 3675 Builder\001
+-6
+6 8700 1650 10500 2250
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 8700 1650 10500 1650 10500 2250 8700 2250 8700 1650
+4 0 0 100 0 18 14 0.0000 4 165 1185 9000 2025 Intercessor\001
+-6
+6 1500 1650 3300 2250
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 1500 1650 3300 1650 3300 2250 1500 2250 1500 1650
+4 0 0 100 0 18 14 0.0000 4 165 1320 1725 2025 Environment\001
+-6
+6 7800 8700 9300 9300
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 7800 8700 9300 8700 9300 9300 7800 9300 7800 8700
+4 0 0 100 0 18 14 0.0000 4 165 930 8100 9075 Node.DB\001
+-6
+6 1500 10200 2400 10800
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 1500 10200 2400 10200 2400 10800 1500 10800 1500 10200
+4 0 0 100 0 18 14 0.0000 4 165 315 1800 10575 Dir\001
+-6
+6 3300 10200 4200 10800
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 3300 10200 4200 10200 4200 10800 3300 10800 3300 10200
+4 0 0 100 0 18 14 0.0000 4 165 375 3600 10575 File\001
+-6
+6 6000 10200 7200 10800
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 6000 10200 7200 10200 7200 10800 6000 10800 6000 10200
+4 0 0 100 0 18 14 0.0000 4 165 555 6300 10575 Table\001
+-6
+6 7800 10200 9300 10800
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 7800 10200 9300 10200 9300 10800 7800 10800 7800 10200
+4 0 0 100 0 18 14 0.0000 4 165 765 8100 10575 Record\001
+-6
+6 9900 10200 11100 10800
+2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 9900 10200 11100 10200 11100 10800 9900 10800 9900 10200
+4 0 0 100 0 18 14 0.0000 4 165 510 10200 10575 Field\001
+-6
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 6900 5250 6825 5175 6900 5100 6975 5175 6900 5250
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 6300 5250 6225 5175 6300 5100 6375 5175 6300 5250
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 5700 5250 5625 5175 5700 5100 5775 5175 5700 5250
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 4800 2700 7200 2700 7200 5100 4800 5100 4800 2700
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4
+ 5100 5100 5025 5250 5175 5250 5100 5100
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 5
+ 0 0 1.00 60.00 120.00
+ 6300 5250 6300 5700 8400 5700 8400 4200 7200 4200
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 5
+ 0 0 1.00 60.00 120.00
+ 5700 5250 5700 6000 9000 6000 9000 3600 7200 3600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+ 5100 5250 5100 8100
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 4725 3675 4650 3600 4725 3525 4800 3600 4725 3675
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 4725 4575 4650 4500 4725 4425 4800 4500 4725 4575
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 4650 3600 3600 3600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 4650 4500 2400 4500
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 1800 2400 1800 4200
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 3000 2400 3000 3300
+2 1 1 1 0 7 100 0 -1 4.000 0 0 7 0 0 2
+ 5850 1950 5850 2700
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 3000 2400 2925 2325 3000 2250 3075 2325 3000 2400
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+ 1800 2400 1725 2325 1800 2250 1875 2325 1800 2400
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+ 3300 1950 8700 1950
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+ 9600 2400 9600 6600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4
+ 7950 6900 7950 6600 10350 6600 10350 6900
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4
+ 9600 2250 9525 2400 9675 2400 9600 2250
+2 1 0 1 0 7 100 0 -1 4.000 0 0 7 0 0 2
+ 4800 3000 7200 3000
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 2
+ 4800 3300 7200 3300
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4
+ 2850 9300 2775 9450 2925 9450 2850 9300
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 4
+ 2100 10200 2100 9900 3750 9900 3750 10200
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5
+ 6600 10200 6600 9900 10500 9900 10500 10200 10500 10125
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 2
+ 2850 9450 2850 9900
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 2
+ 8475 9450 8475 10200
+2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4
+ 8475 9300 8400 9450 8550 9450 8475 9300
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 1
+ 2775 6825
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 1800 10200 1800 9000 2100 9000
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9900 10500 9300 10500
+2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7800 10500 7200 10500
+2 1 0 1 0 7 100 0 -1 4.000 0 0 7 0 0 4
+ 2850 8700 2850 8100 8550 8100 8550 8700
+2 1 1 1 0 7 100 0 -1 4.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 10350 7500 10350 9000 9300 9000
+2 1 1 1 0 7 100 0 -1 4.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 7050 7200 2400 7200 2400 8700
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 5
+ 0 0 1.00 60.00 120.00
+ 6900 5250 6900 5400 7800 5400 7800 4800 7200 4800
+4 0 0 100 0 18 14 0.0000 4 165 555 4950 2925 Node\001
+4 0 0 100 0 16 10 0.0000 4 150 825 7350 3525 dependency\001
+4 0 0 100 0 16 10 0.0000 4 45 60 7425 3825 *\001
+4 0 0 100 0 16 10 0.0000 4 120 555 7350 4125 srcnode\001
+4 0 0 100 0 16 10 0.0000 4 120 90 7425 4425 1\001
+4 0 0 100 0 16 10 0.0000 4 150 570 7350 4725 repnode\001
+4 0 0 100 0 16 10 0.0000 4 120 90 7425 5025 1\001
+4 0 0 100 0 16 10 0.0000 4 120 270 2550 4725 0..1\001
+4 0 0 100 0 16 10 0.0000 4 120 270 3750 3825 0..1\001
+4 0 0 100 0 0 12 0.0000 4 75 90 1875 4050 *\001
+4 0 0 100 0 0 12 0.0000 4 75 90 3075 3150 *\001
+4 0 0 100 0 16 14 0.0000 4 210 600 5100 3750 build()\001
+4 0 0 100 0 16 14 0.0000 4 210 630 5100 4260 scan()\001
+4 0 0 100 0 0 12 0.0000 4 135 90 9750 10725 1\001
+4 0 0 100 0 16 10 0.0000 4 120 90 1650 10125 1\001
+4 0 0 100 0 16 10 0.0000 4 45 60 1875 9225 *\001
+4 0 0 100 0 16 10 0.0000 4 120 90 7650 10725 1\001
+4 0 0 100 0 16 10 0.0000 4 45 60 7275 10725 *\001
+4 0 0 100 0 16 10 0.0000 4 45 60 9375 10725 *\001
diff --git a/doc/design/engine.jpg b/doc/design/engine.jpg
new file mode 100644
index 0000000..2e82232
--- /dev/null
+++ b/doc/design/engine.jpg
Binary files differ
diff --git a/doc/design/engine.xml b/doc/design/engine.xml
new file mode 100644
index 0000000..1a1e335
--- /dev/null
+++ b/doc/design/engine.xml
@@ -0,0 +1,1964 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<section id="sect-principles">
+ <title>General Principles</title>
+
+ <section>
+ <title>Keyword arguments</title>
+
+ <para>
+
+ All methods and functions in this API will support the use of keyword
+ arguments in calls, for the sake of explicitness and readability.
+ For brevity in the hands of experts, most methods and functions
+ will also support positional arguments for their most-commonly-used
+ arguments. As an explicit example, the following two lines will each
+ arrange for an executable program named <filename>foo</filename> (or
+ <filename>foo.exe</filename> on a Win32 system) to be compiled from
+ the <filename>foo.c</filename> source file:
+
+ </para>
+
+ <programlisting>
+ env.Program(target = 'foo', source = 'foo.c')
+
+ env.Program('foo', 'foo.c')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Internal object representation</title>
+
+ <para>
+
+ All methods and functions use internal (Python) objects that
+ represent the external objects (files, for example) for which they
+ perform dependency analysis.
+
+ </para>
+
+ <para>
+
+ All methods and functions in this API that accept an external object
+ as an argument will accept <emphasis>either</emphasis> a string
+ description or an object reference. For example, the two following
+ two-line examples are equivalent:
+
+ </para>
+
+ <programlisting>
+ env.Object(target = 'foo.o', source = 'foo.c')
+ env.Program(target = 'foo', 'foo.o') # builds foo from foo.o
+
+ foo_obj = env.Object(target = 'foo.o', source = 'foo.c')
+ env.Program(target = 'foo', foo_obj) # builds foo from foo.o
+ </programlisting>
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-envs">
+ <title>&ConsEnvs</title>
+
+ <para>
+
+ A &consenv; is the basic means by which a software system interacts
+ with the &SCons; Python API to control a build process.
+
+ </para>
+
+ <para>
+
+ A &consenv; is an object with associated methods for generating target
+ files of various types (&Builder; objects), other associated object
+ methods for automatically determining dependencies from the contents
+ of various types of source files (&Scanner; objects), and a dictionary
+ of values used by these methods.
+
+ </para>
+
+ <para>
+
+ Passing no arguments to the &Environment; instantiation creates a
+ &consenv; with default values for the current platform:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ </programlisting>
+
+ <section>
+ <title>&Consvars;</title>
+
+ <para>
+
+ A &consenv; has an associated dictionary of &consvars; that control how
+ the build is performed. By default, the &Environment; method creates
+ a &consenv; with values that make most software build "out of the box"
+ on the host system. These default values will be generated at the
+ time &SCons; is installed using functionality similar to that provided
+ by GNU &Autoconf;.
+ <footnote>
+ <para>
+ It would be nice if we could avoid re-inventing the wheel here by
+ using some other Python-based tool &Autoconf replacement--like what
+ was supposed to come out of the Software Carpentry configuration
+ tool contest. It will probably be most efficient to roll our own
+ logic initially and convert if something better does come along.
+ </para>
+ </footnote>
+ At a minimum, there will be pre-configured sets of default values
+ that will provide reasonable defaults for UNIX and Windows NT.
+
+ </para>
+
+ <para>
+
+ The default &consenv; values may be overridden when a new &consenv; is
+ created by specifying keyword arguments:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CC = 'gcc',
+ CCFLAGS = '-g',
+ CPPPATH = ['.', 'src', '/usr/include'],
+ LIBPATH = ['/usr/lib', '.'])
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Fetching &consvars;</title>
+
+ <para>
+
+ A copy of the dictionary of &consvars; can be returned using
+ the &Dictionary; method:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ dict = env.Dictionary()
+ </programlisting>
+
+ <para>
+
+ If any arguments are supplied, then just the corresponding value(s)
+ are returned:
+
+ </para>
+
+ <programlisting>
+ ccflags = env.Dictionary('CCFLAGS')
+ cc, ld = env.Dictionary('CC', 'LD')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Copying a &consenv;</title>
+
+ <para>
+
+ A method exists to return a copy of an existing environment, with
+ any overridden values specified as keyword arguments to the method:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ debug = env.Copy(CCFLAGS = '-g')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Multiple &consenvs;</title>
+
+ <para>
+
+ Different external objects often require different build
+ characteristics. Multiple &consenvs; may be defined, each with
+ different values:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = '')
+ debug = Environment(CCFLAGS = '-g')
+ env.Make(target = 'hello', source = 'hello.c')
+ debug.Make(target = 'hello-debug', source = 'hello.c')
+ </programlisting>
+
+ <para>
+
+ Dictionaries of values from multiple &consenvs; may be passed to the
+ &Environment; instantiation or the &Copy; method, in which case the
+ last-specified dictionary value wins:
+
+ </para>
+
+ <programlisting>
+ env1 = Environment(CCFLAGS = '-O', LDFLAGS = '-d')
+ env2 = Environment(CCFLAGS = '-g')
+ new = Environment(env1.Dictionary(), env2.Dictionary())
+ </programlisting>
+
+ <para>
+
+ The <varname>new</varname> environment in the above example retains
+ <literal>LDFLAGS = '-d'</literal> from the <varname>env1</varname>
+ environment, and <literal>CCFLAGS = '-g'</literal> from the
+ <varname>env2</varname> environment.
+
+ </para>
+
+ <!--
+
+ hardware details
+ current directory
+ OS environment variables
+ compilers and options,
+ aliases for commands,
+ versions of tools
+
+ environment overrides a la Cons
+
+ compilation options
+
+ cross compilation via selection of tool+options
+
+ paths for header files (specify alternate path)
+
+ accomodate smart compilers that can tell you
+ "I know how to turn .c or .ccp into .o",
+ "I know how to turn .f into .o"
+
+ -->
+
+ </section>
+
+ <section>
+ <title>Variable substitution</title>
+
+ <para>
+
+ Within a construction command, any variable from the &consenv; may
+ be interpolated by prefixing the name of the construction with
+ <symbol>$</symbol>:
+
+ </para>
+
+ <programlisting>
+ MyBuilder = Builder(command = "$XX $XXFLAGS -c $_INPUTS -o $target")
+
+ env.Command(targets = 'bar.out', sources = 'bar.in',
+ command = "sed '1d' < $source > $target")
+ </programlisting>
+
+ <para>
+
+ Variable substitution is recursive: the command line is expanded
+ until no more substitutions can be made.
+
+ </para>
+
+ <para>
+
+ Variable names following the <symbol>$</symbol> may be enclosed in
+ braces. This can be used to concatenate an interpolated value with an
+ alphanumeric character:
+
+ </para>
+
+ <programlisting>
+ VerboseBuilder = Builder(command = "$XX -${XXFLAGS}v > $target")
+ </programlisting>
+
+ <para>
+
+ The variable within braces may contain a pair of parentheses
+ after a Python function name to be evaluated (for example,
+ <literal>${map()}</literal>). &SCons; will interpolate the return
+ value from the function (presumably a string):
+
+ </para>
+
+ <programlisting>
+ env = Environment(FUNC = myfunc)
+ env.Command(target = 'foo.out', source = 'foo.in',
+ command = "${FUNC($<)}")
+ </programlisting>
+
+ <para>
+
+ If a referenced variable is not defined in the &consenv;,
+ the null string is interpolated.
+
+ </para>
+
+ <para>
+
+ The following special variables can also be used:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>$targets</literal></term>
+ <listitem>
+ <para>
+
+ All target file names. If multiple targets are specified in an
+ array, <literal>$targets</literal> expands to the entire list of
+ targets, separated by a single space.
+
+ </para>
+
+ <para>
+
+ Individual targets from a list may be extracted by enclosing
+ the <literal>targets</literal> keyword in braces and using the
+ appropriate Python array index or slice:
+
+ </para>
+
+ <programlisting>
+ ${targets[0]} # expands to the first target
+
+ ${targets[1:]} # expands to all but the first target
+
+ ${targets[1:-1]} # expands to all but the first and last targets
+ </programlisting>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>$target</literal></term>
+ <listitem>
+ <para>
+
+ A synonym for <literal>${targets[0]}</literal>, the first target
+ specified.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>$sources</literal></term>
+ <listitem>
+ <para>
+
+ All input file names. Any input file names that
+ are used anywhere else on the current command
+ line (via <literal>${sources[0]}</literal>,
+ <literal>${sources{[1]}</literal>, etc.) are removed from the
+ expanded list.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ Any of the above special variables may be enclosed in braces and
+ followed immediately by one of the following attributes to select just
+ a portion of the expanded path name:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>.base</literal></term>
+ <listitem>
+ <para>
+
+ Basename: the directory plus the file name, minus any file suffix.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>.dir</literal></term>
+ <listitem>
+ <para>
+
+ The directory in which the file lives. This is a relative path,
+ where appropriate.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>.file</literal></term>
+ <listitem>
+ <para>
+
+ The file name, minus any directory portion.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>.suffix</literal></term>
+ <listitem>
+ <para>
+
+ The file name suffix (that is, the right-most dot in the file name,
+ and all characters to the right of that).
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>.filebase</literal></term>
+ <listitem>
+ <para>
+
+ The file name (no directory portion), minus any file suffix.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>.abspath</literal></term>
+ <listitem>
+ <para>
+
+ The absolute path to the file.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-builders">
+ <title>&Builder; Objects</title>
+
+ <para>
+
+ By default, &SCons; supplies (and uses) a number of pre-defined
+ &Builder; objects:
+
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <tbody>
+
+ <row>
+ <entry>&Object;</entry>
+ <entry>compile or assemble an object file</entry>
+ </row>
+
+ <row>
+ <entry>&Library;</entry>
+ <entry>archive files into a library</entry>
+ </row>
+
+ <row>
+ <entry>&SharedLibrary;</entry>
+ <entry>archive files into a shared library</entry>
+ </row>
+
+ <row>
+ <entry>&Program;</entry>
+ <entry>link objects and/or libraries into an executable</entry>
+ </row>
+
+ <row>
+ <entry>&MakeBuilder;</entry>
+ <entry>build according to file suffixes; see below</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+<!--
+&Library; and &SharedLibrary; have nearly identical
+semantics, just different
+tools and &consenvs (paths, etc.) that they use.
+In other words, you can construct a shared library
+using just the &Library; &Builder; object
+with a different environment.
+I think that's a better way to do it.
+Feedback?
+-->
+
+ <para>
+
+ A &consenv; can be explicitly initialized with associated &Builder;
+ objects that will be bound to the &consenv; object:
+
+ </para>
+
+ <programlisting>
+ env = Environment(BUILDERS = ['Object', 'Program'])
+ </programlisting>
+
+ <para>
+
+ &Builder; objects bound to a &consenv; can be called directly as
+ methods. When invoked, a &Builder; object returns a (list of) objects
+ that it will build:
+
+ </para>
+
+ <programlisting>
+ obj = env.Object(target ='hello.o', source = 'hello.c')
+ lib = env.Library(target ='libfoo.a',
+ source = ['aaa.c', 'bbb.c'])
+ slib = env.SharedLibrary(target ='libbar.so',
+ source = ['xxx.c', 'yyy.c'])
+ prog = env.Program(target ='hello',
+ source = ['hello.o', 'libfoo.a', 'libbar.so'])
+ </programlisting>
+
+ <section>
+ <title>Specifying multiple inputs</title>
+
+ <para>
+
+ Multiple input files that go into creating a target file may be passed
+ in as a single string, with the individual file names separated by
+ white space:
+
+ </para>
+
+ <programlisting>
+ env.Library(target = 'foo.a', source = 'aaa.c bbb.c ccc.c')
+ env.Object(target = 'yyy.o', source = 'yyy.c')
+ env.Program(target = 'bar', source = 'xxx.c yyy.o foo.a')
+ </programlisting>
+
+ <para>
+
+ Alternatively, multiple input files that go into creating a target
+ file may be passed in as an array. This allows input files to be
+ specified using their object representation:
+
+ </para>
+
+ <programlisting>
+ env.Library(target = 'foo.a', source = ['aaa.c', 'bbb.c', 'ccc.c'])
+ yyy_obj = env.Object(target = 'yyy.o', source = 'yyy.c')
+ env.Program(target = 'bar', source = ['xxx.c', yyy_obj, 'foo.a'])
+ </programlisting>
+
+ <para>
+
+ Individual string elements within an array of input files are
+ <emphasis>not</emphasis> further split into white-space separated
+ file names. This allows file names that contain white space to
+ be specified by putting the value into an array:
+
+ <programlisting>
+ env.Program(target = 'foo', source = ['an input file.c'])
+ </programlisting>
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Specifying multiple targets</title>
+
+ <para>
+
+ Conversely, the generated target may be a string listing multiple
+ files separated by white space:
+
+ </para>
+
+ <programlisting>
+ env.Object(target = 'grammar.o y.tab.h', source = 'grammar.y')
+ </programlisting>
+
+ <para>
+
+ An array of multiple target files can be used to mix string and object
+ representations, or to accomodate file names that contain white space:
+
+ </para>
+
+ <programlisting>
+ env.Program(target = ['my program'], source = 'input.c')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>File prefixes and suffixes</title>
+
+ <para>
+
+ For portability, if the target file name does not already have an
+ appropriate file prefix or suffix, the &Builder; objects will
+ append one appropriate for the file type on the current system:
+
+ </para>
+
+ <programlisting>
+ # builds 'hello.o' on UNIX, 'hello.obj' on Windows NT:
+ obj = env.Object(target ='hello', source = 'hello.c')
+
+ # builds 'libfoo.a' on UNIX, 'foo.lib' on Windows NT:
+ lib = env.Library(target ='foo', source = ['aaa.c', 'bbb.c'])
+
+ # builds 'libbar.so' on UNIX, 'bar.dll' on Windows NT:
+ slib = env.SharedLibrary(target ='bar', source = ['xxx.c', 'yyy.c'])
+
+ # builds 'hello' on UNIX, 'hello.exe' on Windows NT:
+ prog = env.Program(target ='hello',
+ source = ['hello.o', 'libfoo.a', 'libbar.so'])
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>&Builder; object exceptions</title>
+
+ <para>
+
+ &Builder; objects raise the following exceptions on error:
+
+ <!--
+ LIST THESE ONCE WE FIGURE OUT WHAT THEY ARE FROM CODING THEM.
+ -->
+
+ </para>
+ </section>
+
+ <section>
+ <title>User-defined &Builder; objects</title>
+
+ <para>
+
+ Users can define additional &Builder; objects for specific external
+ object types unknown to &SCons;. A &Builder; object may build its
+ target by executing an external command:
+
+ </para>
+
+ <programlisting>
+ WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target',
+ suffix = '.html',
+ src_suffix = '.in')
+ </programlisting>
+
+ <para>
+
+ Alternatively, a &Builder; object may also build its target by
+ executing a Python function:
+
+ </para>
+
+ <programlisting>
+ def update(dest):
+ # [code to update the object]
+ return 1
+
+ OtherBuilder1 = Builder(function = update,
+ src_suffix = ['.in', '.input'])
+ </programlisting>
+
+ <para>
+
+ An optional argument to pass to the function may be specified:
+
+ </para>
+
+ <programlisting>
+ def update_arg(dest, arg):
+ # [code to update the object]
+ return 1
+
+ OtherBuilder2 = Builder(function = update_arg,
+ function_arg = 'xyzzy',
+ src_suffix = ['.in', '.input'])
+ </programlisting>
+
+ <para>
+
+ Both an external command and an internal function may be specified,
+ in which case the function will be called to build the object first,
+ followed by the command line.
+
+ </para>
+
+ <!--
+ NEED AN EXAMPLE HERE.
+ -->
+
+ <para>
+
+ User-defined &Builder; objects can be used like the default &Builder;
+ objects to initialize &consenvs;.
+
+ </para>
+
+ <programlisting>
+ WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target',
+ suffix = '.html',
+ src_suffix = '.in')
+ env = Environment(BUILDERS = ['WebPage'])
+ env.WebPage(target = 'foo.html', source = 'foo.in')
+ # Builds 'bar.html' on UNIX, 'bar.htm' on Windows NT:
+ env.WebPage(target = 'bar', source = 'bar.in')
+ </programlisting>
+
+ <para>
+
+ The command-line specification can interpolate variables from the
+ &consenv;; see "Variable substitution," above.
+
+ </para>
+
+ <para>
+
+ A &Builder; object may optionally be initialized with a list of:
+
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ the prefix of the target file (e.g., 'lib' for libraries)
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ the suffix of the target file (e.g., '.a' for libraries)
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ the expected suffixes of the input files
+ (e.g., '.o' for object files)
+
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+
+ These arguments are used in automatic
+ dependency analysis and to generate output file names that don't
+ have suffixes supplied explicitly.
+
+ </para>
+ </section>
+
+ <section>
+ <title>Copying &Builder; Objects</title>
+
+ <para>
+
+ A &Copy; method exists to return a copy of an existing &Builder;
+ object, with any overridden values specified as keyword arguments to
+ the method:
+
+ </para>
+
+ <programlisting>
+ build = Builder(function = my_build)
+ build_out = build.Copy(suffix = '.out')
+ </programlisting>
+
+ <para>
+
+ Typically, &Builder; objects will be supplied by a tool-master or
+ administrator through a shared &consenv;.
+
+ </para>
+ </section>
+
+ <section>
+ <title>Special-purpose build rules</title>
+
+ <para>
+
+ A pre-defined &Command; builder exists to associate a target file with
+ a specific command or list of commands for building the file:
+
+ </para>
+
+ <programlisting>
+ env.Command(target = 'foo.out', source =
+ command = 'foo.in', "foo.process $sources > $target")
+
+ commands = [ "bar.process -o .tmpfile $sources",
+ "mv .tmpfile $target" ]
+ env.Command(target = 'bar.out', source = 'bar.in', command = commands)
+ </programlisting>
+
+ <para>
+ This is useful when it's too cumbersome to create a &Builder;
+ object just to build a single file in a special way.
+
+ </para>
+ </section>
+
+ <section>
+ <title>The &MakeBuilder; &Builder;</title>
+
+ <para>
+
+ A pre-defined &Builder; object named &MakeBuilder; exists to make
+ simple builds as easy as possible for users, at the expense of
+ sacrificing some build portability.
+
+ </para>
+
+ <para>
+
+ The following minimal example builds the 'hello' program from the
+ 'hello.c' source file:
+
+ </para>
+
+ <programlisting>
+ Environment().Make('hello', 'hello.c')
+ </programlisting>
+
+ <para>
+
+ Users of the &MakeBuilder; &Builder; object are not required to
+ understand intermediate steps involved in generating a file--for
+ example, the distinction between compiling source code into an object
+ file, and then linking object files into an executable. The details
+ of intermediate steps are handled by the invoked method. Users that
+ need to, however, can specify intermediate steps explicitly:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Make(target = 'hello.o', source = 'hello.c')
+ env.Make(target = 'hello', source = 'hello.o')
+ </programlisting>
+
+ <para>
+
+ The &MakeBuilder; method understands the file suffixes specified and
+ "does the right thing" to generate the target object and program
+ files, respectively. It does this by examining the specified output
+ suffixes for the &Builder; objects bound to the environment.
+
+ </para>
+
+ <para>
+
+ Because file name suffixes in the target and source file names
+ must be specified, the &MakeBuilder; method can't be used
+ portably across operating systems. In other words, for the
+ example above, the &MakeBuilder; builder will not generate
+ <filename>hello.exe</filename> on Windows NT.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>&Builder; maps</title>
+
+<!--
+Do we even need this anymore?
+Now that the individual builders
+have specified <literal>suffix</literal>
+and <literal>src_suffix</literal> values,
+all of the information we need to support
+the &MakeBuilder; builder is right there in the environment.
+I think this is a holdover from before I
+added the <literal>suffix</literal> arguments.
+If you want &MakeBuilder; to do something different,
+you set it up with another environment...
+-->
+
+ <para>
+
+ The <function>env.Make</function> method "does the right thing" to
+ build different file types because it uses a dictionary from the
+ &consenv; that maps file suffixes to the appropriate &Builder; object.
+ This &BUILDERMAP; can be initialized at instantiation:
+
+ </para>
+
+ <programlisting>
+ env = Environment(BUILDERMAP = {
+ '.o' : Object,
+ '.a' : Library,
+ '.html' : WebPage,
+ '' : Program,
+ })
+ </programlisting>
+
+ <para>
+
+ With the &BUILDERMAP; properly initialized, the
+ <function>env.Make</function> method can be used to build additional
+ file types:
+
+ </para>
+
+ <programlisting>
+ env.Make(target = 'index.html', source = 'index.input')
+ </programlisting>
+
+ <para>
+
+ &Builder; objects referenced in the &BUILDERMAP; do not need to be
+ listed separately in the &BUILDERS; variable. The &consenv; will
+ bind the union of the &Builder; objects listed in both variables.
+
+ </para>
+
+ <!--
+
+ YYY support scanners which detect files which haven't been generated yet
+
+ -->
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-deps">
+ <title>Dependencies</title>
+
+ <section>
+ <title>Automatic dependencies</title>
+
+ <para>
+
+ By default, &SCons; assumes that a target file has <literal>automatic
+ dependencies</literal> on the:
+
+ </para>
+
+ <blockquote>
+ <simplelist>
+
+ <member>tool used to build the target file</member>
+
+ <member>contents of the input files</member>
+
+ <member>command line used to build the target file</member>
+
+ </simplelist>
+ </blockquote>
+
+ <para>
+
+ If any of these changes, the target file will be rebuilt.
+
+ </para>
+ </section>
+
+ <section>
+ <title>Implicit dependencies</title>
+
+ <para>
+
+ Additionally, &SCons; can scan the contents of files for
+ <literal>implicit dependencies</literal> on other files. For
+ example, &SCons; will scan the contents of a <filename>.c</filename>
+ file and determine that any object created from it is
+ dependent on any <filename>.h</filename> files specified via
+ <literal>#include</literal>. &SCons;, therefore, "does the right
+ thing" without needing to have these dependencies listed explicitly:
+
+ </para>
+
+ <programlisting>
+ % cat Construct
+ env = Environment()
+ env.Program('hello', 'hello.c')
+ % cat hello.c
+ #include "hello_string.h"
+ main()
+ {
+ printf("%s\n", STRING);
+ }
+ % cat > hello_string.h
+ #define STRING "Hello, world!\n"
+ % scons .
+ gcc -c hello.c -o hello.o
+ gcc -o hello hello.c
+ % ./hello
+ Hello, world!
+ % cat > hello_string.h
+ #define STRING "Hello, world, hello!\n"
+ % scons .
+ gcc -c hello.c -o hello.o
+ gcc -o hello hello.c
+ % ./hello
+ Hello, world, hello!
+ %
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Ignoring dependencies</title>
+
+ <para>
+
+ Undesirable <literal>automatic dependencies</literal> or
+ <literal>implicit dependencies</literal> may be ignored:
+
+ </para>
+
+ <programlisting>
+ env.Program(target = 'bar', source = 'bar.c')
+ env.Ignore('bar', '/usr/bin/gcc', 'version.h')
+ </programlisting>
+
+ <para>
+
+ In the above example, the <filename>bar</filename> program will not
+ be rebuilt if the <filename>/usr/bin/gcc</filename> compiler or the
+ <filename>version.h</filename> file change.
+
+ </para>
+ </section>
+
+ <section>
+ <title>Explicit dependencies</title>
+
+ <para>
+
+ Dependencies that are unknown to &SCons; may be specified explicitly
+ in an &SCons; configuration file:
+
+ </para>
+
+ <programlisting>
+ env.Dependency(target = 'output1', dependency = 'input_1 input_2')
+ env.Dependency(target = 'output2', dependency = ['input_1', 'input_2'])
+ env.Dependency(target = 'output3', dependency = ['white space input'])
+
+ env.Dependency(target = 'output_a output_b', dependency = 'input_3')
+ env.Dependency(target = ['output_c', 'output_d'], dependency = 'input_4')
+ env.Dependency(target = ['white space output'], dependency = 'input_5')
+ </programlisting>
+
+ <para>
+
+ Just like the <literal>target</literal> keyword argument, the
+ <literal>dependency</literal> keyword argument may be specified as a
+ string of white-space separated file names, or as an array.
+
+ </para>
+
+ <para>
+
+ A dependency on an &SCons; configuration file itself may be specified
+ explicitly to force a rebuild whenever the configuration file changes:
+
+ </para>
+
+ <programlisting>
+ env.Dependency(target = 'archive.tar.gz', dependency = 'SConstruct')
+ </programlisting>
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-scanners">
+ <title>&Scanner; Objects</title>
+
+ <para>
+
+ Analagous to the previously-described &Builder; objects, &SCons;
+ supplies (and uses) &Scanner; objects to search the contents of
+ a file for implicit dependency files:
+
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <tbody>
+
+ <row>
+ <entry>CScan</entry>
+ <entry>scan .{c,C,cc,cxx,cpp} files for #include dependencies</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>
+
+ A &consenv; can be explicitly initialized with
+ associated &Scanner; objects:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SCANNERS = ['CScan', 'M4Scan'])
+ </programlisting>
+
+ <para>
+
+ &Scanner; objects bound to a &consenv; can be
+ associated directly with specified files:
+
+ </para>
+
+ <programlisting>
+ env.CScan('foo.c', 'bar.c')
+ env.M4Scan('input.m4')
+ </programlisting>
+
+ <section>
+ <title>User-defined &Scanner; objects</title>
+
+ <para>
+
+ A user may define a &Scanner; object to scan a type of file for
+ implicit dependencies:
+
+ </para>
+
+ <programlisting>
+ def scanner1(file_contents):
+ # search for dependencies
+ return dependency_list
+
+ FirstScan = Scanner(function = scanner1)
+ </programlisting>
+
+ <para>
+
+ The scanner function must return a list of dependencies that its finds
+ based on analyzing the file contents it is passed as an argument.
+
+ </para>
+
+ <para>
+
+ The scanner function, when invoked, will be passed the calling
+ environment. The scanner function can use &consenvs; from the passed
+ environment to affect how it performs its dependency scan--the
+ canonical example being to use some sort of search-path construction
+ variable to look for dependency files in other directories:
+
+ </para>
+
+ <programlisting>
+ def scanner2(file_contents, env):
+ path = env.{'SCANNERPATH'} # XXX
+ # search for dependencies using 'path'
+ return dependency_list
+
+ SecondScan = Scanner(function = scanner2)
+ </programlisting>
+
+ <para>
+
+ The user may specify an additional argument when the &Scanner; object
+ is created. When the scanner is invoked, the additional argument
+ will be passed to the scanner funciton, which can be used in any way
+ the scanner function sees fit:
+
+ </para>
+
+ <programlisting>
+ def scanner3(file_contents, env, arg):
+ # skip 'arg' lines, then search for dependencies
+ return dependency_list
+
+ Skip_3_Lines_Scan = Scanner(function = scanner2, argument = 3)
+ Skip_6_Lines_Scan = Scanner(function = scanner2, argument = 6)
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Copying &Scanner; Objects</title>
+
+ <para>
+
+ A method exists to return a copy of an existing &Scanner; object,
+ with any overridden values specified as keyword arguments to the
+ method:
+
+ </para>
+
+ <programlisting>
+ scan = Scanner(function = my_scan)
+ scan_path = scan.Copy(path = '%SCANNERPATH')
+ </programlisting>
+
+ <para>
+
+ Typically, &Scanner; objects will be supplied by a tool-master or
+ administrator through a shared &consenv;.
+
+ </para>
+ </section>
+
+ <section>
+ <title>&Scanner; maps</title>
+
+<!--
+If the &BUILDERMAP; proves unnecessary,
+we could/should get rid of this one, too,
+by adding a parallel <literal>src_suffix</literal>
+argument to the &Scanner; factory...
+Comments?
+-->
+
+ <para>
+
+ Each &consenv; has a &SCANNERMAP;, a dictionary that associates
+ different file suffixes with a scanner object that can be used to
+ generate a list of dependencies from the contents of that file. This
+ &SCANNERMAP; can be initialized at instantiation:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SCANNERMAP = {
+ '.c' : CScan,
+ '.cc' : CScan,
+ '.m4' : M4Scan,
+ })
+ </programlisting>
+
+ <para>
+
+ &Scanner; objects referenced in the &SCANNERMAP; do not need to
+ be listed separately in the &SCANNERS; variable. The &consenv;
+ will bind the union of the &Scanner; objects listed
+ in both variables.
+
+ </para>
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-targets">
+ <title>Targets</title>
+
+ <para>
+
+ The methods in the build engine API described so far merely
+ establish associations that describe file dependencies, how a
+ file should be scanned, etc. Since the real point is to actually
+ <emphasis>build</emphasis> files, &SCons; also has methods that
+ actually direct the build engine to build, or otherwise manipulate,
+ target files.
+
+ </para>
+
+ <section>
+ <title>Building targets</title>
+ <para>
+
+ One or more targets may be built as follows:
+
+ </para>
+
+ <programlisting>
+ env.Build(target = ['foo', 'bar'])
+ </programlisting>
+
+ <para>
+
+ Note that specifying a directory (or other collective object) will
+ cause all subsidiary/dependent objects to be built as well:
+
+ </para>
+
+ <programlisting>
+ env.Build(target = '.')
+
+ env.Build(target = 'builddir')
+ </programlisting>
+
+ <para>
+
+ By default, &SCons; explicitly removes a target file before
+ invoking the underlying function or command(s) to build it.
+
+ </para>
+ </section>
+
+ <section>
+ <title>Removing targets</title>
+
+ <para>
+
+ A "cleanup" operation of removing generated (target) files is
+ performed as follows:
+
+ </para>
+
+ <programlisting>
+ env.Clean(target = ['foo', 'bar'])
+ </programlisting>
+
+ <para>
+
+ Like the &Build; method, the &Clean; method may be passed a
+ directory or other collective object, in which case the subsidiary
+ target objects under the directory will be removed:
+
+ </para>
+
+ <programlisting>
+ env.Clean(target = '.')
+
+ env.Clean(target = 'builddir')
+ </programlisting>
+
+ <para>
+
+ (The directories themselves are not removed.)
+
+ </para>
+ </section>
+
+ <section>
+ <title>Suppressing cleanup removal of build-targets</title>
+
+ <para>
+
+ By default, &SCons; explicitly removes all build-targets
+ when invoked to perform "cleanup". Files that should not be
+ removed during "cleanup" can be specified via the
+ &NoClean; method:
+
+ </para>
+
+ <programlisting>
+ env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c'])
+ env.NoClean('libfoo.a')
+ </programlisting>
+
+ <para>
+
+ The NoClean operation has precedence over the Clean operation.
+ A target that is specified as both Clean and NoClean, will not
+ be removed during a clean.
+
+ In the following example, target 'foo' will not be removed
+ during "cleanup":
+
+ <programlisting>
+ env.Clean(target = 'foo')
+ env.NoClean('foo')
+ </programlisting>
+
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Suppressing build-target removal</title>
+
+ <para>
+
+ As mentioned, by default, &SCons; explicitly removes a target
+ file before invoking the underlying function or command(s) to build
+ it. Files that should not be removed before rebuilding can be
+ specified via the &Precious; method:
+
+ </para>
+
+ <programlisting>
+ env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c'])
+ env.Precious('libfoo.a')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Default targets</title>
+
+ <para>
+
+ The user may specify default targets that will be built if there are no
+ targets supplied on the command line:
+
+ </para>
+
+ <programlisting>
+ env.Default('install', 'src')
+ </programlisting>
+
+ <para>
+
+ Multiple calls to the &Default; method (typically one per &SConscript;
+ file) append their arguments to the list of default targets.
+
+ </para>
+ </section>
+
+ <section>
+ <title>File installation</title>
+
+ <para>
+
+ Files may be installed in a destination directory:
+
+ </para>
+
+ <programlisting>
+ env.Install('/usr/bin', 'program1', 'program2')
+ </programlisting>
+
+ <para>
+
+ Files may be renamed on installation:
+
+ </para>
+
+ <programlisting>
+ env.InstallAs('/usr/bin/xyzzy', 'xyzzy.in')
+ </programlisting>
+
+ <para>
+
+ Multiple files may be renamed on installation by specifying
+ equal-length lists of target and source files:
+
+ </para>
+
+ <programlisting>
+ env.InstallAs(['/usr/bin/foo', '/usr/bin/bar'],
+ ['foo.in', 'bar.in'])
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Target aliases</title>
+
+ <para>
+
+ In order to provide convenient "shortcut" target names that expand to
+ a specified list of targets, aliases may be established:
+
+ </para>
+
+ <programlisting>
+ env.Alias(alias = 'install',
+ targets = ['/sbin', '/usr/lib', '/usr/share/man'])
+ </programlisting>
+
+ <para>
+
+ In this example, specifying a target of <literal>install</literal>
+ will cause all the files in the associated directories to be built
+ (that is, installed).
+
+ </para>
+
+ <para>
+
+ An &Alias; may include one or more other &Aliases; in its list:
+
+ </para>
+
+ <programlisting>
+ env.Alias(alias = 'libraries', targets = ['lib'])
+ env.Alias(alias = 'programs', targets = ['libraries', 'src'])
+ </programlisting>
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-custom">
+ <title>Customizing output</title>
+
+<!--
+Take this whole section with a grain of salt.
+I whipped it up without a great deal of thought
+to try to add a "competitive advantage"
+for the second round of the Software Carpentry contest.
+In particular, hard-coding the
+analysis points and the keywords that specify them
+feels inflexible,
+but I can't think of another way it would be
+done effectively.
+I dunno, maybe this is fine as it is...
+-->
+
+ <para>
+
+ The &SCons; API supports the ability to customize, redirect, or
+ suppress its printed output through user-defined functions.
+ &SCons; has several pre-defined points in its build process at
+ which it calls a function to (potentially) print output. User-defined
+ functions can be specified for these call-back points when &Build;
+ or &Clean;is invoked:
+
+ </para>
+
+ <programlisting>
+ env.Build(target = '.',
+ on_analysis = dump_dependency,
+ pre_update = my_print_command,
+ post_update = my_error_handler)
+ on_error = my_error_handler)
+ </programlisting>
+
+ <para>
+
+ The specific call-back points are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>on_analysis</literal></term>
+ <listitem>
+ <para>
+
+ Called for every object, immediately after the object has been
+ analyzed to see if it's out-of-date. Typically used to print a
+ trace of considered objects for debugging of unexpected dependencies.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>pre_update</literal></term>
+ <listitem>
+ <para>
+
+ Called for every object that has been determined to be out-of-date
+ before its update function or command is executed. Typically used
+ to print the command being called to update a target.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>post_update</literal></term>
+ <listitem>
+ <para>
+
+ Called for every object after its update function or command has
+ been executed. Typically used to report that a top-level specified
+ target is up-to-date or was not remade.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>on_error</literal></term>
+ <listitem>
+ <para>
+
+ Called for every error returned by an update function or command.
+ Typically used to report errors with some string that will be
+ identifiable to build-analysis tools.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ Functions for each of these call-back points all take the same
+ arguments:
+
+ </para>
+
+ <programlisting>
+ my_dump_dependency(target, level, status, update, dependencies)
+ </programlisting>
+
+ <para>
+
+ where the arguments are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>target</literal></term>
+ <listitem>
+ <para>
+
+ The target object being considered.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>level</literal></term>
+ <listitem>
+ <para>
+
+ Specifies how many levels the dependency analysis has
+ recursed in order to consider the <literal>target</literal>.
+ A value of <literal>0</literal> specifies a top-level
+ <literal>target</literal> (that is, one passed to the
+ &Build; or &Clean; method). Objects which a top-level
+ <literal>target</literal> is directly dependent upon have a
+ <literal>level</literal> of <1>, their direct dependencies have a
+ <literal>level</literal> of <2>, etc. Typically used to indent
+ output to reflect the recursive levels.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>status</literal></term>
+ <listitem>
+ <para>
+
+ A string specifying the current status of the target
+ (<literal>"unknown"</literal>, <literal>"built"</literal>,
+ <literal>"error"</literal>, <literal>"analyzed"</literal>, etc.). A
+ complete list will be enumerated and described during implementation.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>update</literal></term>
+ <listitem>
+ <para>
+
+ The command line or function name that will be (or has been) executed
+ to update the <literal>target</literal>.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>dependencies</literal></term>
+ <listitem>
+ <para>
+
+ A list of direct dependencies of the target.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+</section>
+
+
+
+<section id="separate">
+ <title>Separate source and build trees</title>
+
+<!--
+I've never liked Cons' use of the name <literal>Link</literal>
+for this functionality,
+mainly because the term is overloaded
+with linking object files into an executable.
+Yet I've never come up with anything better.
+Any suggestions?
+-->
+
+<!--
+Also, I made this an &Environment; method because
+it logically belongs in the API reference
+(the build engine needs to know about it),
+and I thought it was clean to have
+everything in the build-engine API
+be called through an &Environment; object.
+But <literal>&Link</literal> isn't really
+associated with a specific environment
+(the &Cons; classic implementation just
+leaves it as a bare function call),
+so maybe we should just follow that example
+and not call it through an environment...
+-->
+
+ <para>
+
+ &SCons; allows target files to be built completely separately from
+ the source files by "linking" a build directory to an underlying
+ source directory:
+
+ </para>
+
+ <programlisting>
+ env.Link('build', 'src')
+
+ SConscript('build/SConscript')
+ </programlisting>
+
+ <para>
+
+ &SCons; will copy (or hard link) necessary files (including the
+ &SConscript; file) into the build directory hierarchy. This allows the
+ source directory to remain uncluttered by derived files.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-variant">
+ <title>Variant builds</title>
+
+ <para>
+
+ The &Link; method may be used in conjunction with multiple
+ &consenvs; to support variant builds. The following
+ &SConstruct; and &SConscript; files would build separate debug and
+ production versions of the same program side-by-side:
+
+ </para>
+
+ <programlisting>
+ % cat SConstruct
+ env = Environment()
+ env.Link('build/debug', 'src')
+ env.Link('build/production', 'src')
+ flags = '-g'
+ SConscript('build/debug/SConscript', Export(env))
+ flags = '-O'
+ SConscript('build/production/SConscript', Export(env))
+ % cat src/SConscript
+ env = Environment(CCFLAGS = flags)
+ env.Program('hello', 'hello.c')
+ </programlisting>
+
+ <para>
+
+ The following example would build the appropriate program for the current
+ compilation platform, without having to clean any directories of object
+ or executable files for other architectures:
+
+ </para>
+
+ <programlisting>
+ % cat SConstruct
+ build_platform = os.path.join('build', sys.platform)
+ Link(build_platform, 'src')
+ SConscript(os.path.join(build_platform, 'SConscript'))
+ % cat src/SConscript
+ env = Environment
+ env.Program('hello', 'hello.c')
+ </programlisting>
+
+</section>
+
+
+
+<section id="sect-repositories">
+ <title>Code repositories</title>
+
+<!--
+Like &Link;, &Repository; and &Local; are part of the
+API reference, but not really tied to any specific environment.
+Is it better to be consistent about calling
+everything in the API through an environment,
+or to leave these independent so as
+not to complicate their calling interface?
+-->
+
+ <para>
+
+ &SCons; may use files from one or more shared code repositories in order
+ to build local copies of changed target files. A repository would
+ typically be a central directory tree, maintained by an integrator,
+ with known good libraries and executables.
+
+ </para>
+
+ <programlisting>
+ Repository('/home/source/1.1', '/home/source/1.0')
+ </programlisting>
+
+ <para>
+
+ Specified repositories will be searched in-order for any file
+ (configuration file, input file, target file) that does not exist
+ in the local directory tree. When building a local target file,
+ &SCons; will rewrite path names in the build command to use the
+ necessary repository files. This includes modifying lists of
+ <option>-I</option> or <option>-L</option> flags to specify an
+ appropriate set of include paths for dependency analysis.
+
+ </para>
+ <para>
+
+ &SCons; will modify the Python <varname>sys.path</varname> variable to
+ reflect the addition of repositories to the search path, so that any
+ imported modules or packages necessary for the build can be found in a
+ repository, as well.
+
+ </para>
+ <para>
+
+ If an up-to-date target file is found in a code repository, the file
+ will not be rebuilt or copied locally. Files that must exist locally
+ (for example, to run tests) may be specified:
+
+ </para>
+
+ <programlisting>
+ Local('program', 'libfoo.a')
+ </programlisting>
+
+ <para>
+
+ in which case &SCons; will copy or link an up-to-date copy of the
+ file from the appropriate repository.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-caching">
+ <title>Derived-file caching</title>
+
+<!--
+There should be extensions to this part of the API for
+auxiliary functions like cleaning the cache.
+-->
+
+ <para>
+
+ &SCons; can maintain a cache directory of target files which may be
+ shared among multiple builds. This reduces build times by allowing
+ developers working on a project together to share common target
+ files:
+
+ </para>
+
+ <programlisting>
+ Cache('/var/tmp/build.cache/i386')
+ </programlisting>
+
+ <para>
+
+ When a target file is generated, a copy is added to the cache.
+ When generating a target file, if &SCons; determines that a file
+ that has been built with the exact same dependencies already exists
+ in the specified cache, &SCons; will copy the cached file rather
+ than re-building the target.
+
+ </para>
+ <para>
+
+ Command-line options exist to modify the &SCons; caching behavior
+ for a specific build, including disabling caching, building
+ dependencies in random order, and displaying commands as if cached
+ files were built.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-jobs">
+ <title>Job management</title>
+
+<!--
+This has been completely superseded by
+the more sophisticated &Task; manager
+that Anthony Roach has contributed.
+I need to write that up...
+-->
+
+ <para>
+
+ A simple API exists to inform the Build Engine how many jobs may
+ be run simultaneously:
+
+ </para>
+
+ <programlisting>
+ Jobs(limit = 4)
+ </programlisting>
+
+</section>
diff --git a/doc/design/goals.xml b/doc/design/goals.xml
new file mode 100644
index 0000000..2a7b69b
--- /dev/null
+++ b/doc/design/goals.xml
@@ -0,0 +1,216 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ As a next-generation build tool,
+ &SCons should fundamentally
+ improve on its predecessors.
+ Rather than simply being driven by trying to
+ <emphasis>not</emphasis> be like previous tools,
+ &SCons; aims to satisfy the following goals:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>Practicality</literal></term>
+ <listitem>
+ <para>
+
+ The &SCons; design emphasizes
+ an implementable feature set
+ that lets users get practical, useful work done.
+ &SCons; is helped in this regard by its roots in &Cons;,
+ which has had its feature set honed by
+ several years of input
+ from a dedicated band of users.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Portability</literal></term>
+ <listitem>
+ <para>
+
+ &SCons; is intended as a portable build tool,
+ able to handle software construction tasks
+ on a variety of operating systems.
+ It should be possible (although not mandatory)
+ to use &SCons; so that the same configuration file
+ builds the same software correctly on,
+ for example, both Linux and Windows NT.
+ Consequently, &SCons; should hide from users
+ operating-system-dependent details
+ such as filename extensions
+ (for example, <filename>.o</filename>
+ vs. <filename>.obj</filename>).
+
+ <!--
+ XXX: enable writing portable builds without forcing it
+ -->
+
+ <!--
+ XXX: still write non-portably for quick-and-dirty
+ -->
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Usability</literal></term>
+ <listitem>
+ <para>
+
+ Novice users should be able to grasp quickly
+ the rudiments of using &SCons; to build their software.
+ This extends to installing &SCons;, too.
+ Installation should be painless,
+ and the installed &SCons;
+ should work "out of the box"
+ to build most software.
+
+ </para>
+
+ <para>
+
+ This goal should be kept in mind during implementation,
+ when there is always a tendency to try to optimize too early.
+ Speed is nice, but not as important as clarity
+ and ease of use.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Utility</literal></term>
+ <listitem>
+ <para>
+
+ &SCons; should also provide a rich enough set of features
+ to accommodate building more complicated software projects.
+ However, the features required for
+ building complicated software projects
+ should not get in the way of novice users.
+ (See the previous goal.)
+ In other words, complexity should be available
+ when it's needed
+ but not required to get work done.
+ Practically, this implies that &SCons;
+ shouldn't be dumbed down to the point it
+ excludes complicated software builds.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Sharability</literal></term>
+ <listitem>
+ <para>
+
+ As a key element in balancing the conflicting
+ needs of <literal>Usability</literal> and <literal>Utility</literal>,
+ &SCons; should provide mechanisms to
+ allow &SCons; users to share build rules,
+ dependency scanners, and other objects and recipes
+ for constructing software.
+ A good sharing mechanism should support
+ the model wherein most developers on a project
+ use rules and templates
+ that are created
+ and maintained by a local integrator or build-master,
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Extensibility</literal></term>
+ <listitem>
+ <para>
+
+ &SCons; should provide mechanisms for
+ easily extending its capabilities,
+ including building new types of files,
+ adding new types of dependency scanning,
+ being able to accomodate dependencies
+ between objects other than files,
+ etc.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Flexibility</literal></term>
+ <listitem>
+ <para>
+
+ In addition to providing a useful command-line interface,
+ &SCons; should provide the right architectural
+ framework for embedding its dependency management
+ in other interfaces.
+ &SCons; would help strengthen other GUIs or IDEs
+ and the additional requirements of the
+ other interfaces would help broaden and solidify
+ the core &SCons; dependency management.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+<section id="sect-fix-make">
+ <title>Fixing &Make;'s problems</title>
+
+ <para>
+
+<!--
+To be written.
+-->
+
+ </para>
+
+</section>
+
+<section id="sect-fix-cons">
+ <title>Fixing &Cons;'s problems</title>
+
+ <para>
+
+<!--
+To be written.
+-->
+
+ </para>
+
+</section>
diff --git a/doc/design/install.xml b/doc/design/install.xml
new file mode 100644
index 0000000..e670e83
--- /dev/null
+++ b/doc/design/install.xml
@@ -0,0 +1,28 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+THIS CHAPTER NEEDS TO BE DISCUSSED AND WRITTEN.
+-->
diff --git a/doc/design/intro.xml b/doc/design/intro.xml
new file mode 100644
index 0000000..561baa4
--- /dev/null
+++ b/doc/design/intro.xml
@@ -0,0 +1,111 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ The &SCons; tool provides an easy-to-use, feature-rich interface
+ for constructing software. Architecturally, &SCons; separates
+ its dependency analysis and external object management into an
+ interface-independent Build Engine that could be embedded in any
+ software system that can run Python.
+
+ </para>
+
+ <para>
+
+ At the command line, &SCons; presents an easily-grasped tool
+ where configuration files are Python scripts, reducing the need
+ to learn new build-tool syntax. Inexperienced users can use
+ intelligent methods that ``do the right thing'' to build software
+ with a minimum of fuss. Sophisticated users can use a rich set
+ of underlying features for finer control of the build process,
+ including mechanisms for easily extending the build process to new
+ file types.
+
+ </para>
+
+ <para>
+
+ Dependencies are tracked using digital signatures,
+ which provide more robust dependency analysis than file time
+ stamps. Implicit dependencies are determined automatically by
+ scanning the contents of source files, avoiding the need for
+ laborious and fragile maintenance of static lists of dependencies in
+ configuration files.
+
+ </para>
+
+ <para>
+
+ The &SCons; tool supports use of files from one or more central code
+ repositories, a mechanism for caching derived files, and parallel
+ builds. The tool also includes a framework for sharing build
+ environments, which allows system administrators or integrators to
+ define appropriate build parameters for use by other users.
+
+ </para>
+
+<section id="sect-document">
+ <title>About This Document</title>
+
+ <para>
+
+ This document is an ongoing work-in-progress to write down the ideas
+ and tradeoffs that have gone, and will go into, the &SCons; design.
+ As such, this is intended primarily for use by developers and others
+ working on &SCons;, although it is also intended to serve as a
+ detailed overview of &SCons; for other interested parties. It will
+ be continually updated and evolve, and will likely overlap with other
+ documentation produced by the project. Sections of this document
+ that deal with syntax, for example, may move or be copied into a user
+ guide or reference manual.
+
+ </para>
+
+ <para>
+
+ So please don't assume that everything mentioned here has been
+ decided and carved in stone. If you have ideas for improvements, or
+ questions about things that don't seem to make any sense, please help
+ improve the design by speaking up about them.
+
+ </para>
+
+<!--
+Sections marked like this
+(prefixed with <literal>RATIONALE:</literal> in the HTML,
+surrounded by <literal>BEGIN RATIONALE:</literal>
+and <literal>END RATIONALE:</literal>
+in the printed documentatio)
+are DocBook REMARKs,
+comments about the document
+rather than actual document.
+I've used these to mark sections that need work,
+but also to cite some open design issues.
+If you have input on any of these marked issues,
+I'm especially eager to hear it.
+-->
+
+</section>
diff --git a/doc/design/issues.xml b/doc/design/issues.xml
new file mode 100644
index 0000000..1f9a78c
--- /dev/null
+++ b/doc/design/issues.xml
@@ -0,0 +1,195 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+ <para>
+
+ No build tools is perfect.
+ Here are some &SCons; issues that
+ do not yet have solutions.
+
+ </para>
+
+ <section>
+ <title>Interaction with SC-config</title>
+
+ <para>
+
+ The SC-config tool will be used in the &SCons; installation
+ process to generate an appropriate default construction environment
+ so that building most software works "out of the box" on the
+ installed platform. The SC-config tool will find reasonable default
+ compilers (C, C++, Fortran), linkers/loaders, library archive tools,
+ etc. for specification in the default &SCons; construction
+ environment.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Interaction with test infrastructures</title>
+
+ <para>
+
+ &SCons; can be configured to use SC-test (or some other test tool)
+ to provide controlled, automated testing of software. The &Link;
+ method could link a <filename>test</filename> subdirectory to a build
+ subdirectory:
+
+ </para>
+
+ <programlisting>
+ Link('test', 'build')
+ SConscript('test/SConscript')</programlisting>
+
+ <para>
+
+ Any test cases checked in with the source code will be linked
+ into the <filename>test</filename> subdirectory and executed. If
+ &SConscript; files and test cases are written with this in mind, then
+ invoking:
+
+ </para>
+
+ <programlisting>
+ % sccons test</programlisting>
+
+ <para>
+
+ Would run all the automated test cases that depend on any changed
+ software.
+
+ </para>
+
+
+ <!--
+
+ YYY integrate with SC-test to provide sanity check on new tools
+ "discovery testing" of new tools
+ results recorded in a central database
+ central database can be somewhere else on web
+
+ -->
+
+ </section>
+
+ <section>
+ <title>Java dependencies</title>
+
+ <para>
+
+ Java dependencies are difficult for an external dependency-based
+ construction tool to accomodate. Determining Java class dependencies
+ is more complicated than the simple pattern-matching of C or C++
+ <literal>#include</literal> files. From the point of view of an
+ external build tool, the Java compiler behaves "unpredictably"
+ because it may create or update multiple output class files and
+ directories as a result of its internal class dependencies.
+
+ </para>
+
+ <para>
+
+ An obvious &SCons; implementation would be to have the &Scanner;
+ object parse output from <command>Java -depend -verbose</command> to
+ calculate dependencies, but this has the distinct disadvantage of
+ requiring two separate compiler invocations, thereby slowing down
+ builds.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Limitations of digital signature calculation</title>
+
+ <para>
+
+ In practice, calculating digital signatures of a file's contents is a
+ more robust mechanism than time stamps for determining what needs
+ building. However:
+
+ </para>
+
+ <orderedlist numeration="arabic">
+
+ <listitem>
+ <para>
+
+ Developers used to the time stamp model of &Make; can initially
+ find digital signatures counter-intuitive. The assumption that:
+
+ <programlisting>
+ % touch file.c</programlisting>
+
+ will cause a rebuild of <filename>file</filename> is strong...
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Abstracting dependency calculation into a single digital signature
+ loses a little information: It is no longer possible to tell
+ (without laborious additional calculation) which input file dependency
+ caused a rebuild of a given target file. A feature that could
+ report, "I'm rebuilding file X because it's out-of-date with respect
+ to file Y," would be good, but an digital-signature implementation of
+ such a feature is non-obvious.
+
+ </para>
+ </listitem>
+
+ </orderedlist>
+
+ </section>
+
+ <section>
+ <title>Remote execution</title>
+
+ <para>
+
+ The ability to use multiple build systems through remote execution
+ of tools would be good. This should be implementable through the
+ &Job; class. Construction environments would need modification
+ to specify build systems.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Conditional builds</title>
+
+ <para>
+
+ The ability to check run-time conditions as suggested on the
+ sc-discuss mailing list ("build X only if: the machine is idle /
+ the file system has Y megabytes free space") would also be good,
+ but is not part of the current design.
+
+ </para>
+
+ </section>
diff --git a/doc/design/main.xml b/doc/design/main.xml
new file mode 100644
index 0000000..e991b36
--- /dev/null
+++ b/doc/design/main.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+
+ <!--
+ We haven't updated the design document in ages.
+ Rather than fool people into thinking that it's
+ actually up-to-date and reflects the current design,
+ hard-code the version from back when we last updated it.
+ <!ENTITY % version SYSTEM "../version.xml">
+ %version;
+ -->
+ <!ENTITY builddate "2001/12/13 20:55:46">
+ <!ENTITY buildversion "0.91">
+ <!ENTITY buildrevision "0.01.D177">
+
+ <!--
+ Also freeze the scons.mod DTD extensions
+ to what they were way back when.
+ <!ENTITY % scons SYSTEM "../scons.mod">
+ -->
+ <!ENTITY % scons SYSTEM "scons.mod">
+ %scons;
+
+ <!ENTITY acks SYSTEM "acks.xml">
+ <!ENTITY bground SYSTEM "bground.xml">
+ <!ENTITY copyright SYSTEM "copyright.xml">
+ <!ENTITY engine SYSTEM "engine.xml">
+ <!ENTITY goals SYSTEM "goals.xml">
+ <!ENTITY install SYSTEM "install.xml">
+ <!ENTITY intro SYSTEM "intro.xml">
+ <!ENTITY issues SYSTEM "issues.xml">
+ <!ENTITY native SYSTEM "native.xml">
+ <!ENTITY overview SYSTEM "overview.xml">
+
+]>
+
+<book>
+ <bookinfo>
+ <title>SCons Design version &buildversion;</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <edition>Revision &buildrevision; (&builddate;)</edition>
+
+ <pubdate>2001</pubdate>
+
+ <copyright>
+ <year>2001</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <legalnotice>
+ &copyright;
+ </legalnotice>
+
+ <releaseinfo>version &buildversion;</releaseinfo>
+
+ </bookinfo>
+
+ <chapter id="chap-intro">
+ <title>Introduction</title>
+ &intro;
+ </chapter>
+
+ <chapter id="chap-goals">
+ <title>Goals</title>
+ &goals;
+ </chapter>
+
+ <chapter id="chap-overview">
+ <title>Overview</title>
+ &overview;
+ </chapter>
+
+ <chapter id="chap-engine">
+ <title>Build Engine API</title>
+ &engine;
+ </chapter>
+
+ <chapter id="chap-native">
+ <title>Native Python Interface</title>
+ &native;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-install">
+ <title>Installation</title>
+ &install;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-issues">
+ <title>Other Issues</title>
+ &issues;
+ </chapter>
+
+ <chapter id="chap-background">
+ <title>Background</title>
+ &bground;
+ </chapter>
+
+ <chapter id="chap-summary">
+ <title>Summary</title>
+ <para>
+
+ &SCons; offers a robust and feature-rich design for an SC-build
+ tool. With a Build Engine based on the proven design of
+ the &Cons; utility, it offers increased simplification of the
+ user interface for unsophisticated users with the addition
+ of the "do-the-right-thing" <function>env.Make</function>
+ method, increased flexibility for sophisticated users with the
+ addition of &Builder; and &Scanner; objects, a mechanism to
+ allow tool-masters (and users) to share working construction
+ environments, and embeddability to provide reliable dependency
+ management in a variety of environments and interfaces.
+
+ </para>
+ </chapter>
+
+ <chapter id="chap-acks">
+ <title>Acknowledgements</title>
+ &acks;
+ </chapter>
+
+</book>
diff --git a/doc/design/native.xml b/doc/design/native.xml
new file mode 100644
index 0000000..8cdd867
--- /dev/null
+++ b/doc/design/native.xml
@@ -0,0 +1,364 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+
+ The "Native Python" interface is the interface
+ that the actual &SCons; utility will present to users.
+ Because it exposes the Python Build Engine API,
+ &SCons; users will have direct access to the complete
+ functionality of the Build Engine.
+ In contrast, a different user interface such as a GUI
+ may choose to only use, and present to the end-user,
+ a subset of the Build Engine functionality.
+
+</para>
+
+<section id="sect-config">
+ <title>Configuration files</title>
+
+ <para>
+
+ &SCons; configuration files are simply Python scripts that invoke
+ methods to specify target files to be built, rules for building the
+ target files, and dependencies. Common build rules are available by
+ default and need not be explicitly specified in the configuration
+ files.
+
+ </para>
+
+ <para>
+
+ By default, the &SCons; utility searches for a file named
+ &SConstruct;, &Sconstruct; or &sconstruct (in that order) in the
+ current directory, and reads its configuration from the first file
+ found. A <option>-f</option> command-line option exists to read a
+ different file name.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-syntax">
+ <title>Python syntax</title>
+
+ <para>
+
+ Because &SCons; configuration files are Python scripts, normal Python
+ syntax can be used to generate or manipulate lists of targets or
+ dependencies:
+
+ </para>
+
+ <programlisting>
+ sources = ['aaa.c', 'bbb.c', 'ccc.c']
+ env.Make('bar', sources)
+ </programlisting>
+
+ <para>
+
+ Python flow-control can be used to iterate through invocations of
+ build rules:
+
+ </para>
+
+ <programlisting>
+ objects = ['aaa.o', 'bbb.o', 'ccc.o']
+ for obj in objects:
+ src = replace(obj, '.o', '.c')
+ env.Make(obj, src)
+ </programlisting>
+
+ <para>
+
+ or to handle more complicated conditional invocations:
+
+ </para>
+
+ <programlisting>
+ # only build 'foo' on Linux systems
+ if sys.platform == 'linux1':
+ env.Make('foo', 'foo.c')
+ </programlisting>
+
+ <para>
+
+ Because &SCons; configuration files are Python scripts, syntax errors
+ will be caught by the Python parser. Target-building does not begin
+ until after all configuration files are read, so a syntax error will
+ not cause a build to fail half-way.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-subsidiary">
+ <title>Subsidiary configuration Files</title>
+
+ <para>
+
+ A configuration file can instruct &SCons; to read up subsidiary
+ configuration files. Subsidiary files are specified explicitly in a
+ configuration file via the &SConscript; method. As usual, multiple
+ file names may be specified with white space separation, or in an
+ array:
+
+ </para>
+
+ <programlisting>
+ SConscript('other_file')
+ SConscript('file1 file2')
+ SConscript(['file3', 'file4'])
+ SConscript(['file name with white space'])
+ </programlisting>
+
+ <para>
+
+ An explicit <literal>sconscript</literal> keyword may be used:
+
+ </para>
+
+ <programlisting>
+ SConscript(sconscript = 'other_file')
+ </programlisting>
+
+ <para>
+
+ Including subsidiary configuration files is recursive: a configuration
+ file included via &SConscript; may in turn &SConscript; other
+ configuration files.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-scoping">
+ <title>Variable scoping in subsidiary files</title>
+
+ <para>
+
+ When a subsidiary configuration file is read, it is given its own
+ namespace; it does not have automatic access to variables from the parent
+ configuration file.
+
+ </para>
+
+ <para>
+
+ Any variables (not just &SCons; objects) that are to be shared between configuration files must be
+ explicitly passed in the &SConscript; call
+ using the &Export method:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ debug = Environment(CCFLAGS = '-g')
+ installdir = '/usr/bin'
+ SConscript('src/SConscript', Export(env=env, debug=debug, installdir=installdir))
+ </programlisting>
+
+<!--
+The <literal>env=env</literal> stuff bugs me
+because it imposes extra work on the normal
+case where you <emphasis>don't</emphasis> rename
+the variables.
+Can we simplify the &Export; method
+so that a string
+without a keyword assignment
+is split into variables that are passed
+through transparently?
+Equivalent to the above example:
+<literal>SConscript('src/SConscript', Export('env debug installdir'))</literal>
+-->
+
+ <para>
+
+ Which may be specified explicitly using a keyword argument:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ debug = Environment(CCFLAGS = '-g')
+ installdir = '/usr/bin'
+ SConscript(sconscript = 'src/SConscript',
+ export = Export(env=env, debug=debug, installdir=installdir))
+ </programlisting>
+
+ <para>
+
+ Explicit variable-passing provides control over exactly what is available
+ to a subsidiary file, and avoids unintended side effects of changes in
+ one configuration file affecting other far-removed configuration files
+ (a very hard-to-debug class of build problem).
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-hierarchy">
+ <title>Hierarchical builds</title>
+
+ <para>
+
+ The &SConscript; method is so named because, by convention, subsidiary
+ configuration files in subdirectories are named &SConscript;:
+
+ </para>
+
+ <programlisting>
+ SConscript('src/SConscript')
+ SConscript('lib/build_me')
+ </programlisting>
+
+ <para>
+
+ When a subsidiary configuration file is read from a subdirectory, all
+ of that configuration file's targets and build rules are interpreted
+ relative to that directory (as if &SCons; had changed its working
+ directory to that subdirectory). This allows for easy support of
+ hierarchical builds of directory trees for large projects.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-sharing">
+ <title>Sharing &consenvs;</title>
+
+ <para>
+
+ &SCons; will allow users to share &consenvs, as well as other &SCons;
+ objects and Python variables, by importing them from a central, shared
+ repository using normal Python syntax:
+
+ </para>
+
+ <programlisting>
+ from LocalEnvironments import optimized, debug
+
+ optimized.Make('foo', 'foo.c')
+ debug.Make('foo-d', 'foo.c')
+ </programlisting>
+
+ <para>
+
+ The expectation is that some local tool-master, integrator or
+ administrator will be responsible for assembling environments (creating
+ the &Builder; objects that specify the tools, options, etc.) and make
+ these available for sharing by all users.
+
+ </para>
+
+ <para>
+
+ The modules containing shared &consenvs;
+ (<literal>LocalEnvironments</literal> in the above example) can be
+ checked in and controlled with the rest of the source files. This
+ allows a project to track the combinations of tools and command-line
+ options that work on different platforms, at different times, and with
+ different tool versions, by using already-familiar revision control
+ tools.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-help">
+ <title>Help</title>
+
+ <para>
+
+ The &SCons; utility provides a &Help; function to allow the writer
+ of a &SConstruct; file to provide help text that is specific to
+ the local build tree:
+
+ </para>
+
+ <programlisting>
+ Help("""
+ Type:
+ scons . build and test everything
+ scons test build the software
+ scons src run the tests
+ scons web build the web pages
+ """)
+ </programlisting>
+
+ <para>
+
+ This help text is displayed in response to the <option>-h</option>
+ command-line option. Calling the &Help; function more than once is an
+ error.
+
+ </para>
+
+</section>
+
+
+
+<section id="sect-debug">
+ <title>Debug</title>
+
+ <para>
+
+ &SCons; supports several command-line options for printing extra
+ information with which to debug build problems.
+
+ </para>
+
+<!--
+These need to be specified and explained
+beyond what the man page will have.
+-->
+
+ <!-- BEGIN HTML -->
+
+ <para>
+
+ See the -d, -p, -pa, and -pw options
+ in the <!--<A HREF="#sccons_Man_page">man page</A>-->, below.
+ All of these options make use of call-back functions to
+ <!--<A HREF="reference.html#Customizing_output">control the output</A>-->
+ printed by the Build Engine.
+
+ </para>
+
+ <!-- END HTML -->
+
+</section>
diff --git a/doc/design/overview.xml b/doc/design/overview.xml
new file mode 100644
index 0000000..38e4258
--- /dev/null
+++ b/doc/design/overview.xml
@@ -0,0 +1,498 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<section id="sect-architecture">
+ <title>Architecture</title>
+
+ <para>
+
+ The heart of &SCons; is its <emphasis>Build Engine</emphasis>.
+ The &SCons; Build Engine is a Python module
+ that manages dependencies between
+ external objects
+ such as files or database records.
+ The Build Engine is designed to
+ be interface-neutral
+ and easily embeddable in any
+ software system that needs dependency
+ analysis between updatable objects.
+
+ </para>
+
+ <para>
+
+ The key parts of the Build Engine architecture
+ are captured in the following quasi-UML diagram:
+
+ </para>
+
+<!--
+Including this figure makes our PDF build blow up.
+The figure, however,
+is left over from the Software Carpentry contest
+and is therefore old, out-of-date, and needs to be redone anyway.
+This is where it will go, anyway...
+-->
+
+ <!--
+ YARG! THIS MAKES THE PDF BUILD BLOW UP. HELP!
+ <figure>
+ <title>&SCons; Architecture</title>
+ <graphic fileref="engine.jpg">
+ </figure>
+ -->
+
+ <para>
+
+ The point of &SCons; is to manage
+ dependencies between arbitrary external objects.
+ Consequently, the Build Engine does not restrict or specify
+ the nature of the external objects it manages,
+ but instead relies on subclass of the &Node;
+ class to interact with the external system or systems
+ (file systems, database management systems)
+ that maintain the objects being examined or updated.
+
+ </para>
+
+ <para>
+
+ The Build Engine presents to the software system in
+ which it is embedded
+ a Python API for specifying source (input) and target (output) objects,
+ rules for building/updating objects,
+ rules for scanning objects for dependencies, etc.
+ Above its Python API,
+ the Build Engine is completely
+ interface-independent,
+ and can be encapsulated by any other software
+ that supports embedded Python.
+
+ </para>
+
+ <para>
+
+ Software that chooses to use the Build Engine
+ for dependency management
+ interacts with it
+ through <emphasis>Construction Environments</emphasis>.
+ A Construction Environment consists
+ of a dictionary of environment variables,
+ and one or more associated
+ &Scanner; objects
+ and &Builder; objects.
+ The Python API is used to
+ form these associations.
+
+ </para>
+
+ <para>
+
+ A &Scanner; object specifies
+ how to examine a type of source object
+ (C source file, database record)
+ for dependency information.
+ A &Scanner; object may use
+ variables from the associated
+ Construction Environment
+ to modify how it scans an object:
+ specifying a search path for included files,
+ which field in a database record to consult,
+ etc.
+
+ </para>
+
+ <para>
+
+ A &Builder; object specifies
+ how to update a type of target object:
+ executable program, object file, database field, etc.
+ Like a &Scanner; object,
+ a &Builder; object may use
+ variables from the associated
+ Construction Environment
+ to modify how it builds an object:
+ specifying flags to a compiler,
+ using a different update function,
+ etc.
+
+ </para>
+
+ <para>
+
+ &Scanner; and &Builder; objects will return one or more
+ &Node; objects that represent external objects.
+ &Node; objects are the means by which the
+ Build Engine tracks dependencies:
+ A &Node; may represent a source (input) object that
+ should already exist,
+ or a target (output) object which may be built,
+ or both.
+ The &Node; class is sub-classed to
+ represent external objects of specific type:
+ files, directories, database fields or records, etc.
+ Because dependency information, however,
+ is tracked by the top-level &Node; methods and attributes,
+ dependencies can exist
+ between nodes representing different external object types.
+ For example,
+ building a file could be made
+ dependent on the value of a given
+ field in a database record,
+ or a database table could depend
+ on the contents of an external file.
+
+ </para>
+
+ <para>
+
+ The Build Engine uses a &Job; class (not displayed)
+ to manage the actual work of updating external target objects:
+ spawning commands to build files,
+ submitting the necessary commands to update a database record,
+ etc.
+ The &Job; class has sub-classes
+ to handle differences between spawning
+ jobs in parallel and serially.
+
+ </para>
+
+ <para>
+
+ The Build Engine also uses a
+ &Signature; class (not displayed)
+ to maintain information about whether
+ an external object is up-to-date.
+ Target objects with out-of-date signatures
+ are updated using the appropriate
+ &Builder; object.
+
+ </para>
+
+ <!-- BEGIN HTML -->
+
+ <!--
+ Details on the composition, methods,
+ and attributes of these classes
+ are available in the A HREF="internals.html" Internals /A page.
+ -->
+
+ <!-- END HTML -->
+
+</section>
+
+
+
+<section id="sect-engine">
+ <title>Build Engine</title>
+
+ <para>
+
+ More detailed discussion of some of the
+ Build Engine's characteristics:
+
+ </para>
+
+ <section>
+ <title>Python API</title>
+
+ <para>
+
+ The Build Engine can be embedded in any other software
+ that supports embedding Python:
+ in a GUI,
+ in a wrapper script that
+ interprets classic <filename>Makefile</filename> syntax,
+ or in any other software that
+ can translate its dependency representation
+ into the appropriate calls to the Build Engine API.
+ <!--<xref linkend="chap-native">--> describes in detail
+ the specification for a "Native Python" interface
+ that will drive the &SCons; implementation effort.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Single-image execution</title>
+
+ <para>
+
+ When building/updating the objects,
+ the Build Engine operates as a single executable
+ with a complete Directed Acyclic Graph (DAG)
+ of the dependencies in the entire build tree.
+ This is in stark contrast to the
+ commonplace recursive use of Make
+ to handle hierarchical directory-tree builds.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Dependency analysis</title>
+
+ <para>
+
+ Dependency analysis is carried out via digital signatures
+ (a.k.a. "fingerprints").
+ Contents of object are examined and reduced
+ to a number that can be stored and compared to
+ see if the object has changed.
+ Additionally, &SCons; uses the same
+ signature technique on the command-lines that
+ are executed to update an object.
+ If the command-line has changed since the last time,
+ then the object must be rebuilt.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Customized output</title>
+
+ <para>
+
+ The output of Build Engine is customizable
+ through user-defined functions.
+ This could be used to print additional desired
+ information about what &SCons; is doing,
+ or tailor output to a specific build analyzer,
+ GUI, or IDE.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Build failures</title>
+
+ <para>
+
+ &SCons; detects build failures via the exit status from the tools
+ used to build the target files. By default, a failed exit status
+ (non-zero on UNIX systems) terminates the build with an appropriate
+ error message. An appropriate class from the Python library will
+ interpret build-tool failures via an OS-independent API.
+
+ </para>
+
+ <para>
+
+ If multiple tasks are executing in a parallel build, and one tool
+ returns failure, &SCons; will not initiate any further build tasks,
+ but allow the other build tasks to complete before terminating.
+
+ </para>
+
+ <para>
+
+ A <option>-k</option> command-line option may be used to ignore
+ errors and continue building other targets. In no case will a target
+ that depends on a failed build be rebuilt.
+
+ </para>
+
+ </section>
+
+</section>
+
+
+
+<section id="sect-interfaces">
+ <title>Interfaces</title>
+
+ <para>
+
+ As previously described,
+ the &SCons; Build Engine
+ is interface-independent above its Python API,
+ and can be embedded in any software system
+ that can translate its dependency requirements
+ into the necessary Python calls.
+
+ </para>
+
+ <para>
+
+ The "main" &SCons; interface
+ for implementation purposes,
+ uses Python scripts as configuration files.
+ Because this exposes the Build Engine's Python API to the user,
+ it is current called the "Native Python" interface.
+
+ </para>
+
+ <para>
+
+ This section will also discuss
+ how &SCons; will function in the context
+ of two other interfaces:
+ the &Makefile; interface of the classic &Make; utility,
+ and a hypothetical graphical user interface (GUI).
+
+ </para>
+
+ <section>
+ <title>Native Python interface</title>
+
+ <para>
+
+ The Native Python interface is intended to be the primary interface
+ by which users will know &SCons;--that is,
+ it is the interface they will use
+ if they actually type &SCons; at a command-line prompt.
+
+ </para>
+
+ <para>
+
+ In the Native Python interface, &SCons; configuration files are simply
+ Python scripts that directly invoke methods from the Build Engine's
+ Python API to specify target files to be built, rules for building
+ the target files, and dependencies. Additional methods, specific to
+ this interface, are added to handle functionality that is specific to
+ the Native Python interface: reading a subsidiary configuration file;
+ copying target files to an installation directory; etc.
+
+ </para>
+
+ <para>
+
+ Because configuration files are Python scripts, Python flow control
+ can be used to provide very flexible manipulation of objects and
+ dependencies. For example, a function could be used to invoke a common
+ set of methods on a file, and called iteratively over an array of
+ files.
+
+ </para>
+
+ <para>
+
+ As an additional advantage, syntax errors in &SCons; Native Python
+ configuration files will be caught by the Python parser. Target-building
+ does not begin until after all configuration files are read, so a syntax
+ error will not cause a build to fail half-way.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Makefile interface</title>
+
+ <para>
+
+ An alternate &SCons; interface would provide backwards
+ compatibility with the classic &Make utility.
+ This would be done by embedding the &SCons; Build Engine
+ in a Python script that can translate existing
+ &Makefile;s into the underlying calls to the
+ Build Engine's Python API
+ for building and tracking dependencies.
+ Here are approaches to solving some of the issues
+ that arise from marrying these two pieces:
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+ &Makefile; suffix rules can be translated
+ into an appropriate &Builder; object
+ with suffix maps from the Construction Environment.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Long lists of static dependences
+ appended to a &Makefile; by
+ various <command>"make depend"</command> schemes
+ can be preserved
+ but supplemented by
+ the more accurate dependency information
+ provided by &Scanner; objects.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Recursive invocations of &Make;
+ can be avoided by reading up
+ the subsidiary &Makefile; instead.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>
+
+ Lest this seem like too outlandish an undertaking,
+ there is a working example of this approach:
+ Gary Holt's &Makepp; utility
+ is a Perl script that provides
+ admirably complete parsing of complicated &Makefile;s
+ around an internal build engine inspired,
+ in part, by the classic <application>Cons</application> utility.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Graphical interfaces</title>
+
+ <para>
+
+ The &SCons; Build Engine
+ is designed from the ground up to be embedded
+ into multiple interfaces.
+ Consequently, embedding the dependency capabilities
+ of &SCons; into graphical interface
+ would be a matter of mapping the
+ GUI's dependency representation
+ (either implicit or explicit)
+ into corresponding calls to the Python API
+ of the &SCons; Build Engine.
+
+ </para>
+
+ <para>
+
+ Note, however, that this proposal leaves the problem of
+ designed a good graphical interface
+ for representing software build dependencies
+ to people with actual GUI design experience...
+
+ </para>
+
+ </section>
+
+</section>
diff --git a/doc/design/scons.mod b/doc/design/scons.mod
new file mode 100644
index 0000000..d805210
--- /dev/null
+++ b/doc/design/scons.mod
@@ -0,0 +1,429 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ An SCons-specific DTD module, for use with SCons DocBook
+ documentation, that contains names, phrases, acronyms, etc. used
+ throughout the SCons documentation.
+
+-->
+
+
+
+<!--
+
+ Other applications that we reference.
+
+-->
+
+<!ENTITY Aegis "<application>Aegis</application>">
+<!ENTITY Ant "<application>Ant</application>">
+<!ENTITY Autoconf "<application>Autoconf</application>">
+<!ENTITY Automake "<application>Automake</application>">
+<!ENTITY cc "<application>cc</application>">
+<!ENTITY Cons "<application>Cons</application>">
+<!ENTITY cp "<application>cp</application>">
+<!ENTITY csh "<application>csh</application>">
+<!ENTITY gcc "<application>gcc</application>">
+<!ENTITY Jam "<application>Jam</application>">
+<!ENTITY jar "<application>jar</application>">
+<!ENTITY javac "<application>javac</application>">
+<!ENTITY javah "<application>javah</application>">
+<!ENTITY Make "<application>Make</application>">
+<!ENTITY Makepp "<application>Make++</application>">
+<!ENTITY Python "<application>Python</application>">
+<!ENTITY ranlib "<application>ranlib</application>">
+<!ENTITY rmic "<application>rmic</application>">
+<!ENTITY SCons "<application>SCons</application>">
+<!ENTITY scons "<application>scons</application>">
+<!ENTITY ScCons "<application>ScCons</application>">
+<!ENTITY tar "<application>tar</application>">
+<!ENTITY touch "<application>touch</application>">
+<!ENTITY zip "<application>zip</application>">
+
+
+<!--
+
+ Classes.
+
+-->
+
+<!ENTITY Action "<classname>Action</classname>">
+<!ENTITY ActionBase "<classname>ActionBase</classname>">
+<!ENTITY CommandAction "<classname>CommandAction</classname>">
+<!ENTITY FunctionAction "<classname>FunctionAction</classname>">
+<!ENTITY ListAction "<classname>ListAction</classname>">
+<!ENTITY Builder "<classname>Builder</classname>">
+<!ENTITY BuilderBase "<classname>BuilderBase</classname>">
+<!ENTITY CompositeBuilder "<classname>CompositeBuilder</classname>">
+<!ENTITY MultiStepBuilder "<classname>MultiStepBuilder</classname>">
+<!ENTITY Job "<classname>Job</classname>">
+<!ENTITY Jobs "<classname>Jobs</classname>">
+<!ENTITY Serial "<classname>Serial</classname>">
+<!ENTITY Parallel "<classname>Parallel</classname>">
+<!ENTITY Node "<classname>Node</classname>">
+<!ENTITY Node_FS "<classname>Node.FS</classname>">
+<!ENTITY Scanner "<classname>Scanner</classname>">
+<!ENTITY Sig "<classname>Sig</classname>">
+<!ENTITY Signature "<classname>Signature</classname>">
+<!ENTITY Taskmaster "<classname>Taskmaster</classname>">
+<!ENTITY TimeStamp "<classname>TimeStamp</classname>">
+<!ENTITY Walker "<classname>Walker</classname>">
+<!ENTITY Wrapper "<classname>Wrapper</classname>">
+
+
+
+<!--
+
+ Options, command-line.
+
+-->
+
+<!ENTITY debug-explain "<literal>--debug=explain</literal>">
+<!ENTITY implicit-cache "<literal>--implicit-cache</literal>">
+<!ENTITY implicit-deps-changed "<literal>--implicit-deps-changed</literal>">
+<!ENTITY implicit-deps-unchanged "<literal>--implicit-deps-unchanged</literal>">
+<!ENTITY Q "<literal>-Q</literal>">
+
+<!--
+
+ Options, SConscript-settable.
+
+-->
+
+<!ENTITY implicit_cache "<literal>implicit_cache</literal>">
+<!ENTITY implicit_deps_changed "<literal>implicit_deps_changed</literal>">
+<!ENTITY implicit_deps_unchanged "<literal>implicit_deps_unchanged</literal>">
+
+
+
+<!--
+
+ File and directory names.
+
+-->
+
+<!ENTITY build "<filename>build</filename>">
+<!ENTITY Makefile "<filename>Makefile</filename>">
+<!ENTITY Makefiles "<filename>Makefiles</filename>">
+<!ENTITY SConscript "<filename>SConscript</filename>">
+<!ENTITY SConstruct "<filename>SConstruct</filename>">
+<!ENTITY Sconstruct "<filename>Sconstruct</filename>">
+<!ENTITY sconstruct "<filename>sconstruct</filename>">
+<!ENTITY sconsign "<filename>.sconsign</filename>">
+<!ENTITY src "<filename>src</filename>">
+
+
+
+<!--
+
+ Methods and functions. This includes functions from both
+ the Build Engine and the Native Python Interface.
+
+-->
+
+<!ENTITY Add "<function>Add</function>">
+<!ENTITY AddOptions "<function>AddOptions</function>">
+<!ENTITY Alias "<function>Alias</function>">
+<!ENTITY Aliases "<function>Aliases</function>">
+<!ENTITY Append "<function>Append</function>">
+<!ENTITY BoolOption "<function>BoolOption</function>">
+<!ENTITY Build "<function>Build</function>">
+<!ENTITY CacheDir "<function>CacheDir</function>">
+<!ENTITY Clean "<function>Clean</function>">
+<!ENTITY Clone "<function>Clone</function>">
+<!ENTITY Command "<function>Command</function>">
+<!ENTITY Configure "<function>Configure</function>">
+<!ENTITY Copy "<function>Copy</function>">
+<!ENTITY Default "<function>Default</function>">
+<!ENTITY DefaultRules "<function>DefaultRules</function>">
+<!ENTITY Depends "<function>Depends</function>">
+<!ENTITY Dir "<function>Dir</function>">
+<!ENTITY Entry "<function>Entry</function>">
+<!ENTITY EnumOption "<function>EnumOption</function>">
+<!ENTITY Environment "<function>Environment</function>">
+<!ENTITY Export "<function>Export</function>">
+<!ENTITY File "<function>File</function>">
+<!ENTITY Finish "<function>Finish</function>">
+<!ENTITY GenerateHelpText "<function>GenerateHelpText</function>">
+<!ENTITY Help "<function>Help</function>">
+<!ENTITY Ignore "<function>Ignore</function>">
+<!ENTITY Import "<function>Import</function>">
+<!ENTITY Install "<function>Install</function>">
+<!ENTITY InstallAs "<function>InstallAs</function>">
+<!ENTITY Link "<function>Link</function>">
+<!ENTITY ListOption "<function>ListOption</function>">
+<!ENTITY Local "<function>Local</function>">
+<!ENTITY Module "<function>Module</function>">
+<!ENTITY NoClean "<function>NoClean</function>">
+<!ENTITY Objects "<function>Objects</function>">
+<!ENTITY Options "<function>Options</function>">
+<!ENTITY PackageOption "<function>PackageOption</function>">
+<!ENTITY PathOption "<function>PathOption</function>">
+<!ENTITY Precious "<function>Precious</function>">
+<!ENTITY Prepend "<function>Prepend</function>">
+<!ENTITY Replace "<function>Replace</function>">
+<!ENTITY Repository "<function>Repository</function>">
+<!ENTITY Return "<function>Return</function>">
+<!ENTITY RuleSet "<function>RuleSet</function>">
+<!ENTITY Salt "<function>Salt</function>">
+<!ENTITY SetBuildSignatureType "<function>SetBuildSignatureType</function>">
+<!ENTITY SetContentSignatureType "<function>SetContentSignatureType</function>">
+<!ENTITY SourceSignature "<function>SourceSignature</function>">
+<!ENTITY SourceSignatures "<function>SourceSignatures</function>">
+<!ENTITY Split "<function>Split</function>">
+<!ENTITY TargetSignatures "<function>TargetSignatures</function>">
+<!ENTITY Task "<function>Task</function>">
+
+<!-- Environment methods -->
+<!ENTITY subst "<function>subst</function>">
+
+<!-- Configure context functions -->
+<!ENTITY Message "<function>Message</function>">
+<!ENTITY Result "<function>Result</function>">
+<!ENTITY CheckCHeader "<function>CheckCHeader</function>">
+<!ENTITY CheckCXXHeader "<function>CheckCXXHeader</function>">
+<!ENTITY CheckFunc "<function>CheckFunc</function>">
+<!ENTITY CheckHeader "<function>CheckHeader</function>">
+<!ENTITY CheckLib "<function>CheckLib</function>">
+<!ENTITY CheckLibWithHeader "<function>CheckLibWithHeader</function>">
+<!ENTITY CheckType "<function>CheckType</function>">
+<!ENTITY TryAction "<function>TryAction</function>">
+<!ENTITY TryBuild "<function>TryBuild</function>">
+<!ENTITY TryCompile "<function>TryCompile</function>">
+<!ENTITY TryLink "<function>TryLink</function>">
+<!ENTITY TryRun "<function>TryRun</function>">
+
+<!-- Python functions -->
+<!ENTITY str "<function>str</function>">
+<!ENTITY zipfile "<function>zipfile</function>">
+
+<!-- Obsolete, but referenced in old documents. -->
+<!ENTITY Cache "<function>Cache</function>">
+
+
+
+<!--
+
+ Global variables.
+
+-->
+
+<!ENTITY ARGUMENTS "<varname>ARGUMENTS</varname>">
+<!ENTITY BUILD_TARGETS "<varname>BUILD_TARGETS</varname>">
+<!ENTITY COMMAND_LINE_TARGETS "<varname>COMMAND_LINE_TARGETS</varname>">
+<!ENTITY DEFAULT_TARGETS "<varname>DEFAULT_TARGETS</varname>">
+
+
+
+<!--
+
+ Construction variables.
+
+-->
+
+<!ENTITY BUILDERMAP "<varname>BUILDERMAP</varname>">
+<!ENTITY BUILDERS "<varname>BUILDERS</varname>">
+<!ENTITY CC "<varname>CC</varname>">
+<!ENTITY CCFLAGS "<varname>CCFLAGS</varname>">
+<!ENTITY CCCOM "<varname>CCCOM</varname>">
+<!ENTITY COLOR "<varname>COLOR</varname>">
+<!ENTITY COLORS "<varname>COLORS</varname>">
+<!ENTITY CONFIG "<varname>CONFIG</varname>">
+<!ENTITY CPPDEFINES "<varname>CPPDEFINES</varname>">
+<!ENTITY ENV "<varname>ENV</varname>">
+<!ENTITY JAVACLASSDIR "<varname>JAVACLASSDIR</varname>">
+<!ENTITY LIBDIRPREFIX "<varname>LIBDIRPREFIX</varname>">
+<!ENTITY LIBDIRSUFFIX "<varname>LIBDIRSUFFIX</varname>">
+<!ENTITY LIBLINKPREFIX "<varname>LIBLINKPREFIX</varname>">
+<!ENTITY LIBLINKSUFFIX "<varname>LIBLINKSUFFIX</varname>">
+<!ENTITY LIBPATH "<varname>LIBPATH</varname>">
+<!ENTITY LIBS "<varname>LIBS</varname>">
+<!ENTITY LINK "<varname>LINK</varname>">
+<!ENTITY LINKCOM "<varname>LINKCOM</varname>">
+<!ENTITY LINKFLAGS "<varname>LINKFLAGS</varname>">
+<!ENTITY RELEASE "<varname>RELEASE</varname>">
+<!ENTITY RELEASE_BUILD "<varname>RELEASE_BUILD</varname>">
+<!ENTITY SCANNERMAP "<varname>SCANNERMAP</varname>">
+<!ENTITY SCANNERS "<varname>SCANNERS</varname>">
+<!ENTITY TARFLAGS "<varname>TARFLAGS</varname>">
+<!ENTITY TARSUFFIX "<varname>TARSUFFIX</varname>">
+
+
+
+<!--
+
+ Environment variables.
+
+-->
+
+<!ENTITY PATH "<varname>PATH</varname>">
+<!ENTITY PYTHONPATH "<varname>PYTHONPATH</varname>">
+<!ENTITY SCONSFLAGS "<varname>SCONSFLAGS</varname>">
+
+
+
+<!--
+
+ Function and method arguments.
+
+-->
+
+<!ENTITY allowed_values "<varname>allowed_values</varname>">
+<!ENTITY build_dir "<varname>build_dir</varname>">
+<!ENTITY map "<varname>map</varname>">
+<!ENTITY ignorecase "<varname>ignorecase</varname>">
+<!ENTITY options "<varname>options</varname>">
+<!ENTITY exports "<varname>exports</varname>">
+<!ENTITY source "<varname>source</varname>">
+<!ENTITY target "<varname>target</varname>">
+
+
+
+<!--
+
+ Values of function and method arguments.
+
+-->
+
+<!ENTITY all "<literal>all</literal>">
+<!ENTITY none "<literal>none</literal>">
+
+
+
+<!--
+
+ Builder and Scanner objects.
+
+-->
+
+<!ENTITY BuildDir "<function>BuildDir</function>">
+<!ENTITY CFile "<function>CFile</function>">
+<!ENTITY CXXFile "<function>CXXFile</function>">
+<!ENTITY DVI "<function>DVI</function>">
+<!ENTITY Jar "<function>Jar</function>">
+<!ENTITY Java "<function>Java</function>">
+<!ENTITY JavaH "<function>JavaH</function>">
+<!ENTITY Library "<function>Library</function>">
+<!ENTITY Object "<function>Object</function>">
+<!ENTITY PCH "<function>PCH</function>">
+<!ENTITY PDF "<function>PDF</function>">
+<!ENTITY PostScript "<function>PostScript</function>">
+<!ENTITY Program "<function>Program</function>">
+<!ENTITY RES "<function>RES</function>">
+<!ENTITY RMIC "<function>RMIC</function>">
+<!ENTITY SharedLibrary "<function>SharedLibrary</function>">
+<!ENTITY SharedObject "<function>SharedObject</function>">
+<!ENTITY StaticLibrary "<function>StaticLibrary</function>">
+<!ENTITY StaticObject "<function>StaticObject</function>">
+<!ENTITY Tar "<function>Tar</function>">
+<!ENTITY Zip "<function>Zip</function>">
+
+<!-- Obsolete, but referenced in old documents. -->
+<!ENTITY MakeBuilder "<function>Make</function>">
+
+
+
+<!--
+
+ Terms. Define both singular and plural forms in various
+ case-sensitive combinations for use in titles, in-line, etc.
+
+-->
+
+<!ENTITY buildfunc "<literal>builder function</literal>">
+<!ENTITY builder_method "<literal>builder method</literal>">
+
+<!ENTITY Configure_Contexts "<literal>Configure Contexts</literal>">
+<!ENTITY configure_context "<literal>configure context</literal>">
+
+<!ENTITY ConsEnv "<literal>Construction Environment</literal>">
+<!ENTITY ConsEnvs "<literal>Construction Environments</literal>">
+<!ENTITY Consenv "<literal>Construction environment</literal>">
+<!ENTITY Consenvs "<literal>Construction environments</literal>">
+<!ENTITY consenv "<literal>construction environment</literal>">
+<!ENTITY consenvs "<literal>construction environments</literal>">
+
+<!ENTITY ConsVar "<literal>Construction Variable</literal>">
+<!ENTITY ConsVars "<literal>Construction Variables</literal>">
+<!ENTITY Consvar "<literal>Construction variable</literal>">
+<!ENTITY Consvars "<literal>Construction variables</literal>">
+<!ENTITY consvar "<literal>construction variable</literal>">
+<!ENTITY consvars "<literal>construction variables</literal>">
+
+<!ENTITY CPPPATH "<literal>CPPPATH</literal>">
+
+<!ENTITY Dictionary "<literal>Dictionary</literal>">
+
+<!ENTITY Emitter "<literal>Emitter</literal>">
+<!ENTITY emitter "<literal>emitter</literal>">
+<!ENTITY Generator "<literal>Generator</literal>">
+<!ENTITY generator "<literal>generator</literal>">
+
+<!ENTITY Nodes "<literal>Nodes</literal>">
+
+<!ENTITY signature "<literal>signature</literal>">
+<!ENTITY buildsignature "<literal>build signature</literal>">
+
+<!ENTITY true "<literal>true</literal>">
+<!ENTITY false "<literal>false</literal>">
+
+<!ENTITY typedef "<literal>typedef</literal>">
+
+<!--
+
+ File and program names used in examples.
+
+-->
+
+<!ENTITY bar "<application>bar</application>">
+<!ENTITY common1_c "<filename>common1.c</filename>">
+<!ENTITY common2_c "<filename>common2.c</filename>">
+<!ENTITY custom_py "<filename>custom.py</filename>">
+<!ENTITY goodbye "<application>goodbye</application>">
+<!ENTITY goodbye_o "<filename>goodbye.o</filename>">
+<!ENTITY goodbye_obj "<filename>goodbye.obj</filename>">
+<!ENTITY file_dll "<filename>file.dll</filename>">
+<!ENTITY file_in "<filename>file.in</filename>">
+<!ENTITY file_lib "<filename>file.lib</filename>">
+<!ENTITY file_o "<filename>file.o</filename>">
+<!ENTITY file_obj "<filename>file.obj</filename>">
+<!ENTITY file_out "<filename>file.out</filename>">
+<!ENTITY foo "<application>foo</application>">
+<!ENTITY foo_o "<filename>foo.o</filename>">
+<!ENTITY foo_obj "<filename>foo.obj</filename>">
+<!ENTITY hello "<application>hello</application>">
+<!ENTITY hello_c "<filename>hello.c</filename>">
+<!ENTITY hello_exe "<filename>hello.exe</filename>">
+<!ENTITY hello_h "<filename>hello.h</filename>">
+<!ENTITY hello_o "<filename>hello.o</filename>">
+<!ENTITY hello_obj "<filename>hello.obj</filename>">
+<!ENTITY libfile_a "<filename>libfile_a</filename>">
+<!ENTITY libfile_so "<filename>libfile_so</filename>">
+<!ENTITY new_hello "<application>new_hello</application>">
+<!ENTITY new_hello_exe "<application>new_hello.exe</application>">
+<!ENTITY prog "<filename>prog</filename>">
+<!ENTITY prog1 "<filename>prog1</filename>">
+<!ENTITY prog2 "<filename>prog2</filename>">
+<!ENTITY prog_c "<filename>prog.c</filename>">
+<!ENTITY prog_exe "<filename>prog.exe</filename>">
+<!ENTITY stdio_h "<filename>stdio.h</filename>">
+
+<!--
+
+ Punctuation.
+
+-->
+
+<!ENTITY plus "<literal>+</literal>">
+<!ENTITY hash "<literal>#</literal>">
+
+<!--
+
+ Mailing lists
+
+-->
+
+<!ENTITY scons-announce "<literal>announce@scons.tigris.org</literal>">
+<!ENTITY scons-devel "<literal>dev@scons.tigris.org</literal>">
+<!ENTITY scons-users "<literal>users@scons.tigris.org</literal>">
diff --git a/doc/developer/MANIFEST b/doc/developer/MANIFEST
new file mode 100644
index 0000000..eece338
--- /dev/null
+++ b/doc/developer/MANIFEST
@@ -0,0 +1,9 @@
+architecture.xml
+branches.xml
+copyright.xml
+cycle.xml
+main.xml
+packaging.xml
+preface.xml
+sourcetree.xml
+testing.xml
diff --git a/doc/developer/architecture.xml b/doc/developer/architecture.xml
new file mode 100644
index 0000000..3a6e18c
--- /dev/null
+++ b/doc/developer/architecture.xml
@@ -0,0 +1,40 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Architecture</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
diff --git a/doc/developer/branches.xml b/doc/developer/branches.xml
new file mode 100644
index 0000000..e46b616
--- /dev/null
+++ b/doc/developer/branches.xml
@@ -0,0 +1,40 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>&SCons; Branches</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
diff --git a/doc/developer/copyright.xml b/doc/developer/copyright.xml
new file mode 100644
index 0000000..7a37cb9
--- /dev/null
+++ b/doc/developer/copyright.xml
@@ -0,0 +1,32 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<blockquote>
+ <para>
+
+ SCons Developer's Guide Copyright (c) 2007 Steven Knight
+
+ </para>
+</blockquote>
diff --git a/doc/developer/cycle.xml b/doc/developer/cycle.xml
new file mode 100644
index 0000000..b56edd2
--- /dev/null
+++ b/doc/developer/cycle.xml
@@ -0,0 +1,40 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Development Cycle</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
diff --git a/doc/developer/main.xml b/doc/developer/main.xml
new file mode 100644
index 0000000..0ebed33
--- /dev/null
+++ b/doc/developer/main.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+
+ <!ENTITY % version SYSTEM "../version.xml">
+ %version;
+
+ <!ENTITY % scons SYSTEM "../scons.mod">
+ %scons;
+
+ <!ENTITY architecture SYSTEM "architecture.xml">
+ <!ENTITY branches SYSTEM "branches.xml">
+ <!ENTITY copyright SYSTEM "copyright.xml">
+ <!ENTITY cycle SYSTEM "cycle.xml">
+ <!ENTITY packaging SYSTEM "packaging.xml">
+ <!ENTITY preface SYSTEM "preface.xml">
+ <!ENTITY sourcetree SYSTEM "sourcetree.xml">
+ <!ENTITY testing SYSTEM "testing.xml">
+
+]>
+
+<book>
+ <bookinfo>
+ <title>SCons Developer's Guide &buildversion;</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <edition>Revision &buildrevision; (&builddate;)</edition>
+
+ <pubdate>2007</pubdate>
+
+ <copyright>
+ <year>2007</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <legalnotice>
+ &copyright;
+ </legalnotice>
+
+ <releaseinfo>version &buildversion;</releaseinfo>
+
+ </bookinfo>
+
+ <preface id="chap-preface">
+ <title>Preface</title>
+ &preface;
+ </preface>
+
+ <chapter id="chap-development-cycle">
+ <title>Development Cycle</title>
+ &cycle;
+ </chapter>
+
+ <chapter id="chap-source-tree">
+ <title>Source Tree</title>
+ &sourcetree;
+ </chapter>
+
+ <chapter id="chap-testing">
+ <title>Testing</title>
+ &testing;
+ </chapter>
+
+ <chapter id="chap-branches">
+ <title>Branches</title>
+ &branches;
+ </chapter>
+
+ <chapter id="chap-packaging">
+ <title>Packaging</title>
+ &packaging;
+ </chapter>
+
+ <chapter id="chap-architecture">
+ <title>Architecture</title>
+ &architecture;
+ </chapter>
+
+</book>
diff --git a/doc/developer/packaging.xml b/doc/developer/packaging.xml
new file mode 100644
index 0000000..92b6a31
--- /dev/null
+++ b/doc/developer/packaging.xml
@@ -0,0 +1,40 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Packaging</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
diff --git a/doc/developer/preface.xml b/doc/developer/preface.xml
new file mode 100644
index 0000000..ece9cdc
--- /dev/null
+++ b/doc/developer/preface.xml
@@ -0,0 +1,175 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ This document assumes that you already know how &SCons;
+ and that you want to learn how to work on the code.
+
+ </para>
+
+ <section>
+ <title>&SCons; Principles</title>
+
+ <para>
+
+ There are a few overriding principles
+ we try to live up to in designing and implementing &SCons:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Correctness</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Performance</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Convenience</term>
+
+ <listitem>
+ <para>
+
+ &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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ In a nutshell, we try hard to make &SCons; just
+ "do the right thing" and build software correctly,
+ with a minimum of hassles.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Acknowledgements</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
diff --git a/doc/developer/sourcetree.xml b/doc/developer/sourcetree.xml
new file mode 100644
index 0000000..97bfee0
--- /dev/null
+++ b/doc/developer/sourcetree.xml
@@ -0,0 +1,40 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Source Tree</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
diff --git a/doc/developer/testing.xml b/doc/developer/testing.xml
new file mode 100644
index 0000000..a41c61b
--- /dev/null
+++ b/doc/developer/testing.xml
@@ -0,0 +1,40 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Testing</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
diff --git a/doc/man/MANIFEST b/doc/man/MANIFEST
new file mode 100644
index 0000000..8e69d1c
--- /dev/null
+++ b/doc/man/MANIFEST
@@ -0,0 +1,2 @@
+scons.1
+sconsign.1
diff --git a/doc/man/scons-time.1 b/doc/man/scons-time.1
new file mode 100644
index 0000000..8231c9d
--- /dev/null
+++ b/doc/man/scons-time.1
@@ -0,0 +1,1017 @@
+.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation the rights to use, copy, modify, merge, publish,
+.\" distribute, sublicense, and/or sell copies of the Software, and to
+.\" permit persons to whom the Software is furnished to do so, subject to
+.\" the following conditions:
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" doc/man/scons-time.1 4577 2009/12/27 19:44:43 scons
+.\"
+.\" ES - Example Start - indents and turns off line fill
+.de ES
+.RS
+.nf
+..
+.\" EE - Example End - ends indent and turns line fill back on
+.de EE
+.RE
+.fi
+..
+'\"==========================================================================
+.de SF
+.B scons-time func
+[\fB-h\fR]
+[\fB--chdir=\fIDIR\fR]
+[\fB-f \fIFILE\fR]
+[\fB--fmt=\fIFORMAT\fR]
+[\fB--func=\fINAME\fR]
+[\fB-p \fISTRING\fR]
+[\fB-t \fINUMBER\fR]
+[\fB--title= TITLE\fR]
+[\fIARGUMENTS\fR]
+..
+'\"--------------------------------------------------------------------------
+.de SY
+.B scons-time mem
+[\fB-h\fR]
+[\fB--chdir=\fIDIR\fR]
+[\fB-f \fIFILE\fR]
+[\fB--fmt=\fIFORMAT\fR]
+[\fB-p \fISTRING\fR]
+[\fB--stage=\fISTAGE\fR]
+[\fB-t \fINUMBER\fR]
+[\fB--title=\fITITLE\fR]
+[\fIARGUMENTS\fR]
+..
+'\"--------------------------------------------------------------------------
+.de SO
+.B scons-time obj
+[\fB-h\fR]
+[\fB--chdir=\fIDIR\fR]
+[\fB-f \fIFILE\fR]
+[\fB--fmt=\fIFORMAT\fR]
+[\fB-p \fISTRING\fR]
+[\fB--stage=\fISTAGE\fR]
+[\fB-t \fINUMBER\fR]
+[\fB--title=\fITITLE\fR]
+[\fIARGUMENTS\fR]
+..
+'\"--------------------------------------------------------------------------
+.de SR
+.B scons-time run
+[\fB-hnqv\fR]
+[\fB--aegis=\fIPROJECT\fR]
+[\fB-f \fIFILE\fR]
+[\fB--number=\fINUMBER\fR]
+[\fB--outdir=\fIOUTDIR\fR]
+[\fB-p \fISTRING\fR]
+[\fB--python=\fIPYTHON\fR]
+[\fB-s \fIDIR\fR]
+[\fB--scons=\fISCONS\fR]
+[\fB--svn=\fIURL\fR]
+[\fIARGUMENTS\fR]
+..
+'\"--------------------------------------------------------------------------
+.de ST
+.B scons-time time
+[\fB-h\fR]
+[\fB--chdir=\fIDIR\fR]
+[\fB-f \fIFILE\fR]
+[\fB--fmt=\fIFORMAT\fR]
+[\fB-p \fISTRING\fR]
+[\fB-t \fINUMBER\fR]
+[\fB--title=\fITITLE\fR]
+[\fB--which=\fIWHICH\fR]
+[\fIARGUMENTS\fR]
+..
+.TH SCONS-TIME 1 "December 2009"
+.SH NAME
+scons-time \- generate and display SCons timing information
+'\"==========================================================================
+.SH SYNOPSIS
+.B scons-time
+.IR subcommand
+[
+.IR options ...
+]
+[
+.IR arguments ...
+]
+'\"--------------------------------------------------------------------------
+.SS "Generating Timing Information"
+.SR
+'\"--------------------------------------------------------------------------
+.SS "Extracting Function Timings"
+.SF
+'\"--------------------------------------------------------------------------
+.SS "Extracting Memory Statistics"
+.SY
+'\"--------------------------------------------------------------------------
+.SS "Extracting Object Counts"
+.SO
+'\"--------------------------------------------------------------------------
+.SS "Extracting Execution Times"
+.ST
+'\"--------------------------------------------------------------------------
+.SS "Help Text"
+.B scons-time help
+.I SUBCOMMAND
+[...]
+'\"==========================================================================
+.SH DESCRIPTION
+The
+.B scons-time
+command runs an SCons configuration
+through a standard set of profiled timings
+and can extract and graph information from the
+resulting profiles and log files of those timings.
+The action to be performed by the
+.B scons-time
+script is specified
+by a subcommand, the first argument on the command line.
+See the
+.B SUBCOMMANDS
+section below for information about the operation
+of specific subcommands.
+.P
+The basic way to use
+.B scons-time
+is to run the
+.B scons-time run
+subcommand
+(possibly multiple times)
+to generate profile and log file output,
+and then use one of the other
+subcommands to display the results
+captured in the profiles and log files
+for a particular kind of information:
+function timings
+(the
+.B scons-time func
+subcommand),
+total memory used
+(the
+.B scons-time mem
+subcommand),
+object counts
+(the
+.B scons-time obj
+subcommand)
+and overall execution time
+(the
+.B scons-time time
+subcommand).
+Options exist to place and find the
+profiles and log files in separate directories,
+to generate the output in a format suitable
+for graphing with the
+.BR gnuplot (1)
+program,
+and so on.
+.P
+There are two basic ways the
+.B scons-time run
+subcommand
+is intended to be used
+to gather timing statistics
+for a configuration.
+One is to use the
+.B --svn=
+option to test a configuration against
+a list of revisions from the SCons Subversion repository.
+This will generate a profile and timing log file
+for every revision listed with the
+.B --number=
+option,
+and can be used to look at the
+impact of commited changes to the
+SCons code base on a particular
+configuration over time.
+.P
+The other way is to profile incremental changes to a
+local SCons code base during a development cycle--that is,
+to look at the performance impact of changes
+you're making in the local tree.
+In this mode,
+you run the
+.B scons-time run
+subcommand
+.I without
+the
+.B --svn=
+option,
+in which case it simply looks in the profile/log file output directory
+(the current directory by default)
+and automatically figures out the
+.I next
+run number for the output profile and log file.
+Used in this way,
+the development cycle goes something like:
+make a change to SCons;
+run
+.B scons-time run
+to profile it against a specific configuration;
+make another change to SCons;
+run
+.B scons-time run
+again to profile it;
+etc.
+'\"==========================================================================
+.SH OPTIONS
+The
+.B scons-time
+command only supports a few global options:
+.TP
+-h, --help
+Displays the global help text and exits,
+identical to the
+.B scons-time help
+subcommand.
+.TP
+-V, --version
+Displays the
+.B scons-time
+version and exits.
+.P
+Most functionality is controlled by options
+to the individual subcommands.
+See the next section for information
+about individual subcommand options.
+'\"==========================================================================
+.SH SUBCOMMANDS
+The
+.B scons-time
+command supports the following
+individual subcommands.
+'\"--------------------------------------------------------------------------
+.SS "The func Subcommand"
+.SF
+.P
+The
+.B scons-time func
+subcommand displays timing information
+for a specific Python function within SCons.
+By default, it extracts information about the
+.BR _main ()
+function,
+which includes the Python profiler timing
+for all of SCons.
+.P
+The
+.B scons-time func
+subcommand extracts function timing information
+from all the specified file arguments,
+which should be Python profiler output files.
+(Normally, these would be
+.B *.prof
+files generated by the
+.B scons-time run
+subcommand,
+but they can actually be generated
+by any Python profiler invocation.)
+All file name arguments will be
+globbed for on-disk files.
+.P
+If no arguments are specified,
+then function timing information
+will be extracted from all
+.B *.prof
+files,
+or the subset of them
+with a prefix specified by the
+.B -p
+option.
+.P
+Options include:
+.TP
+-C DIRECTORY, --chdir=DIRECTORY
+Changes to the specified
+.I DIRECTORY
+before looking for the specified files
+(or files that match the specified patterns).
+.TP
+-f FILE, --file=FILE
+Reads configuration information from the specified
+.IR FILE .
+.TP
+-fmt=FORMAT, --format=FORMAT
+Reports the output in the specified
+.IR FORMAT .
+The formats currently supported are
+.B ascii
+(the default)
+and
+.BR gnuplot .
+.TP
+--func=NAME
+Extracts timings for the specified function
+.IR NAME .
+The default is to report cumulative timings for the
+.BR _main ()
+function,
+which contains the entire SCons run.
+.TP
+-h, --help
+Displays help text for the
+.B scons-time func
+subcommand.
+.TP
+-p STRING, --prefix=STRING
+Specifies the prefix string for profiles
+from which to extract function timing information.
+This will be used to search for profiles
+if no arguments are specified on the command line.
+.TP
+-t NUMBER, --tail=NUMBER
+Only extracts function timings from the last
+.I NUMBER
+files.
+'\"--------------------------------------------------------------------------
+.SS "The help Subcommand"
+.B scons-time help
+.I SUBCOMMAND
+[...]
+The
+.B help
+subcommand prints help text for any
+other subcommands listed as later arguments on the command line.
+'\"--------------------------------------------------------------------------
+.SS "The mem Subcommand"
+.SY
+.P
+The
+.B scons-time mem
+subcommand displays how much memory SCons uses.
+.P
+The
+.B scons-time mem
+subcommand extracts memory use information
+from all the specified file arguments,
+which should be files containing output from
+running SCons with the
+.B --debug=memory
+option.
+(Normally, these would be
+.B *.log
+files generated by the
+.B scons-time run
+subcommand.)
+All file name arguments will be
+globbed for on-disk files.
+.P
+If no arguments are specified,
+then memory information
+will be extracted from all
+.B *.log
+files,
+or the subset of them
+with a prefix specified by the
+.B -p
+option.
+.P
+.TP
+-C DIR, --chdir=DIR
+Changes to the specified
+.I DIRECTORY
+before looking for the specified files
+(or files that match the specified patterns).
+.TP
+-f FILE, --file=FILE
+Reads configuration information from the specified
+.IR FILE .
+.TP
+-fmt=FORMAT, --format=FORMAT
+Reports the output in the specified
+.IR FORMAT .
+The formats currently supported are
+.B ascii
+(the default)
+and
+.BR gnuplot .
+.TP
+-h, --help
+Displays help text for the
+.B scons-time mem
+subcommand.
+.TP
+-p STRING, --prefix=STRING
+Specifies the prefix string for log files
+from which to extract memory usage information.
+This will be used to search for log files
+if no arguments are specified on the command line.
+.TP
+--stage=STAGE
+Prints the memory used at the end of the specified
+.IR STAGE :
+.B pre-read
+(before the SConscript files are read),
+.B post-read ,
+(after the SConscript files are read),
+.B pre-build
+(before any targets are built)
+or
+.B post-build
+(after any targets are built).
+If no
+.B --stage
+option is specified,
+the default behavior is
+.BR post-build ,
+which reports the final amount of memory
+used by SCons during each run.
+.TP
+-t NUMBER, --tail=NUMBER
+Only reports memory statistics from the last
+.I NUMBER
+files.
+'\"--------------------------------------------------------------------------
+.SS "The obj Subcommand"
+.SO
+.P
+The
+.B scons-time obj
+subcommand displays how many objects of a specific named type
+are created by SCons.
+.P
+The
+.B scons-time obj
+subcommand extracts object counts
+from all the specified file arguments,
+which should be files containing output from
+running SCons with the
+.B --debug=count
+option.
+(Normally, these would be
+.B *.log
+files generated by the
+.B scons-time run
+subcommand.)
+All file name arguments will be
+globbed for on-disk files.
+.P
+If no arguments are specified,
+then object counts
+will be extracted from all
+.B *.log
+files,
+or the subset of them
+with a prefix specified by the
+.B -p
+option.
+.TP
+-C DIR, --chdir=DIR
+Changes to the specified
+.I DIRECTORY
+before looking for the specified files
+(or files that match the specified patterns).
+.TP
+-f FILE, --file=FILE
+Reads configuration information from the specified
+.IR FILE .
+.TP
+-fmt=FORMAT, --format=FORMAT
+Reports the output in the specified
+.IR FORMAT .
+The formats currently supported are
+.B ascii
+(the default)
+and
+.BR gnuplot .
+.TP
+-h, --help
+Displays help text for the
+.B scons-time obj
+subcommand.
+.TP
+-p STRING, --prefix=STRING
+Specifies the prefix string for log files
+from which to extract object counts.
+This will be used to search for log files
+if no arguments are specified on the command line.
+.TP
+--stage=STAGE
+Prints the object count at the end of the specified
+.IR STAGE :
+.B pre-read
+(before the SConscript files are read),
+.B post-read ,
+(after the SConscript files are read),
+.B pre-build
+(before any targets are built)
+or
+.B post-build
+(after any targets are built).
+If no
+.B --stage
+option is specified,
+the default behavior is
+.BR post-build ,
+which reports the final object count during each run.
+.TP
+-t NUMBER, --tail=NUMBER
+Only reports object counts from the last
+.I NUMBER
+files.
+'\"--------------------------------------------------------------------------
+.SS "The run Subcommand"
+.SR
+The
+.B scons-time run
+subcommand is the basic subcommand
+for profiling a specific configuration
+against a version of SCons.
+.P
+The configuration to be tested
+is specified as a list of files
+or directories that will be unpacked or copied
+into a temporary directory
+in which SCons will be invoked.
+The
+.B scons-time run
+subcommand understands file suffixes like
+.BR .tar ,
+.BR .tar.gz ,
+.BR .tgz
+and
+.BR .zip
+and will unpack their contents into a temporary directory.
+If more than one argument is specified,
+each one will be unpacked or copied
+into the temporary directory "on top of"
+the previous archives or directories,
+so the expectation is that multiple
+specified archives share the same directory layout.
+.P
+Once the file or directory arguments are unpacked or
+copied to the temporary directory,
+the
+.B scons-time run
+subcommand runs the
+requested version of SCons
+against the configuration
+three times:
+.TP
+Startup
+SCons is run with the
+.B --help
+option so that just the SConscript files are read,
+and then the default help text is printed.
+This profiles just the perceived "overhead" of starting up SCons
+and processing the SConscript files.
+.TP
+Full build
+SCons is run to build everything specified in the configuration.
+Specific targets to be passed in on the command l ine
+may be specified by the
+.B targets
+keyword in a configuration file; see below for details.
+.TP
+Rebuild
+SCons is run again on the same just-built directory.
+If the dependencies in the SCons configuration are correct,
+this should be an up-to-date, "do nothing" rebuild.
+.P
+Each invocation captures the output log file and a profile.
+.P
+The
+.B scons-time run
+subcommand supports the following options:
+.TP
+--aegis=PROJECT
+Specifies the Aegis
+.I PROJECT
+from which the
+version(s) of
+.B scons
+being timed will be extracted.
+When
+.B --aegis
+is specified, the
+.BI --number= NUMBER
+option specifies delta numbers
+that will be tested.
+Output from each invocation run will be placed in file
+names that match the Aegis delta numbers.
+If the
+.B --number=
+option is not specified,
+then the default behavior is to time the
+tip of the specified
+.IR PROJECT .
+.TP
+-f FILE, --file=FILE
+Reads configuration information from the specified
+.IR FILE .
+This often provides a more convenient way to specify and
+collect parameters associated with a specific timing configuration
+than specifying them on the command line.
+See the
+.B CONFIGURATION FILE
+section below
+for information about the configuration file parameters.
+.TP
+-h, --help
+Displays help text for the
+.B scons-time run
+subcommand.
+.TP
+-n, --no-exec
+Do not execute commands,
+just printing the command-line equivalents of what would be executed.
+Note that the
+.B scons-time
+script actually executes its actions in Python,
+where possible,
+for portability.
+The commands displayed are UNIX
+.I equivalents
+of what it's doing.
+.TP
+--number=NUMBER
+Specifies the run number to be used in the names of
+the log files and profile outputs generated by this run.
+.IP
+When used in conjuction with the
+.BI --aegis= PROJECT
+option,
+.I NUMBER
+specifies one or more comma-separated Aegis delta numbers
+that will be retrieved automatically from the specified Aegis
+.IR PROJECT .
+.IP
+When used in conjuction with the
+.BI --svn= URL
+option,
+.I NUMBER
+specifies one or more comma-separated Subversion revision numbers
+that will be retrieved automatically from the Subversion
+repository at the specified
+.IR URL .
+Ranges of delta or revision numbers
+may be specified be separating two numbers
+with a hyphen
+.RB ( \- ).
+.P
+Example:
+.ES
+% scons-time run --svn=http://scons.tigris.org/svn/trunk --num=1247,1249-1252 .
+.EE
+.TP
+-p STRING, --prefix=STRING
+Specifies the prefix string to be used for all of the log files
+and profiles generated by this run.
+The default is derived from the first
+specified argument:
+if the first argument is a directory,
+the default prefix is the name of the directory;
+if the first argument is an archive
+(tar or zip file),
+the default prefix is the the base name of the archive,
+that is, what remains after stripping the archive suffix
+.RB ( .tgz ", " .tar.gz " or " .zip ).
+.TP
+--python=PYTHON
+Specifies a path to the Python executable to be used
+for the timing runs.
+The default is to use the same Python executable that
+is running the
+.B scons-time
+command itself.
+.TP
+-q, --quiet
+Suppresses display of the command lines being executed.
+.TP
+-s DIR, --subdir=DIR
+Specifies the name of directory or subdirectory
+from which the commands should be executed.
+The default is XXX
+.TP
+--scons=SCONS
+Specifies a path to the SCons script to be used
+for the timing runs.
+The default is XXX
+.TP
+--svn=URL, --subversion=URL
+Specifies the
+.I URL
+of the Subversion repository from which the
+version(s) of
+.B scons
+being timed will be extracted.
+When
+.B --svn
+is specified, the
+.BI --number= NUMBER
+option specifies revision numbers
+that will be tested.
+Output from each invocation run will be placed in file
+names that match the Subversion revision numbers.
+If the
+.B --number=
+option is not specified,
+then the default behavior is to time the
+.B HEAD
+of the specified
+.IR URL .
+.TP
+-v, --verbose
+Displays the output from individual commands to the screen
+(in addition to capturing the output in log files).
+'\"--------------------------------------------------------------------------
+.SS "The time Subcommand"
+.ST
+.P
+The
+.B scons-time time
+subcommand displays SCons execution times
+as reported by the
+.B scons --debug=time
+option.
+.P
+The
+.B scons-time time
+subcommand extracts SCons timing
+from all the specified file arguments,
+which should be files containing output from
+running SCons with the
+.B --debug=time
+option.
+(Normally, these would be
+.B *.log
+files generated by the
+.B scons-time run
+subcommand.)
+All file name arguments will be
+globbed for on-disk files.
+.P
+If no arguments are specified,
+then execution timings
+will be extracted from all
+.B *.log
+files,
+or the subset of them
+with a prefix specified by the
+.B -p
+option.
+.TP
+-C DIR, --chdir=DIR
+Changes to the specified
+.I DIRECTORY
+before looking for the specified files
+(or files that match the specified patterns).
+.TP
+-f FILE, --file=FILE
+Reads configuration information from the specified
+.IR FILE .
+.TP
+-fmt=FORMAT, --format=FORMAT
+Reports the output in the specified
+.IR FORMAT .
+The formats currently supported are
+.B ascii
+(the default)
+and
+.BR gnuplot .
+.TP
+-h, --help
+Displays help text for the
+.B scons-time time
+subcommand.
+.TP
+-p STRING, --prefix=STRING
+Specifies the prefix string for log files
+from which to extract execution timings.
+This will be used to search for log files
+if no arguments are specified on the command line.
+.TP
+-t NUMBER, --tail=NUMBER
+Only reports object counts from the last
+.I NUMBER
+files.
+.TP
+--which=WHICH
+Prints the execution time for the specified
+.IR WHICH
+value:
+.B total
+(the total execution time),
+.B SConscripts
+(total execution time for the SConscript files themselves),
+.B SCons
+(exectuion time in SCons code itself)
+or
+.B commands
+(execution time of the commands and other actions
+used to build targets).
+If no
+.B --which
+option is specified,
+the default behavior is
+.BR total ,
+which reports the total execution time for each run.
+'\"==========================================================================
+.SH CONFIGURATION FILE
+Various
+.B scons-time
+subcommands can read information from a specified
+configuration file when passed the
+.B \-f
+or
+.B \--file
+options.
+The configuration file is actually executed as a Python script.
+Setting Python variables in the configuration file
+controls the behavior of the
+.B scons-time
+script more conveniently than having to specify
+command-line options or arguments for every run,
+and provides a handy way to "shrink-wrap"
+the necessary information for producing (and reporting)
+consistent timing runs for a given configuration.
+.TP
+.B aegis
+The Aegis executable for extracting deltas.
+The default is simply
+.BR aegis .
+.TP
+.B aegis_project
+The Aegis project from which deltas should be extracted.
+The default is whatever is specified
+with the
+.B --aegis=
+command-line option.
+.TP
+.B archive_list
+A list of archives (files or directories)
+that will be copied to the temporary directory
+in which SCons will be invoked.
+.BR .tar ,
+.BR .tar.gz ,
+.BR .tgz
+and
+.BR .zip
+files will have their contents unpacked in
+the temporary directory.
+Directory trees and files will be copied as-is.
+.TP
+.B initial_commands
+A list of commands that will be executed
+before the actual timed
+.B scons
+runs.
+This can be used for commands that are necessary
+to prepare the source tree\-for example,
+creating a configuration file
+that should not be part of the timed run.
+.TP
+.B key_location
+The location of the key on Gnuplot graphing information
+generated with the
+.BR --format=gnuplot
+option.
+The default is
+.BR "bottom left" .
+.TP
+.B prefix
+The file name prefix to be used when
+running or extracting timing for this configuration.
+.TP
+.B python
+The path name of the Python executable
+to be used when running or extracting information
+for this configuration.
+The default is the same version of Python
+used to run the SCons
+.TP
+.B scons
+The path name of the SCons script to be used
+when running or extracting information
+for this configuration.
+The default is simply
+.BR scons .
+.TP
+.B scons_flags
+The
+.B scons
+flags used when running SCons to collect timing information.
+The default value is
+.BR "--debug=count --debug=memory --debug=time --debug=memoizer" .
+.TP
+.B scons_lib_dir
+.TP
+.B scons_wrapper
+.TP
+.B startup_targets
+.TP
+.B subdir
+The subdirectory of the project into which the
+.B scons-time
+script should change
+before executing the SCons commands to time.
+.TP
+.B subversion_url
+The Subversion URL from
+.TP
+.B svn
+The subversion executable used to
+check out revisions of SCons to be timed.
+The default is simple
+.BR svn .
+.TP
+.B svn_co_flag
+.TP
+.B tar
+.TP
+.B targets
+A string containing the targets that should be added to
+the command line of every timed
+.B scons
+run.
+This can be used to restrict what's being timed to a
+subset of the full build for the configuration.
+.TP
+.B targets0
+.TP
+.B targets1
+.TP
+.B targets2
+.TP
+.B title
+.TP
+.B unzip
+.TP
+.B verbose
+.TP
+.B vertical_bars
+'\"--------------------------------------------------------------------------
+.SS Example
+Here is an example
+.B scons-time
+configuration file
+for a hypothetical sample project:
+.P
+.ES
+# The project doesn't use SCons natively (yet), so we're
+# timing a separate set of SConscript files that we lay
+# on top of the vanilla unpacked project tarball.
+arguments = ['project-1.2.tgz', 'project-SConscripts.tar']
+
+# The subdirectory name contains the project version number,
+# so tell scons-time to chdir there before building.
+subdir = 'project-1.2'
+
+# Set the prefix so output log files and profiles are named:
+# project-000-[012].{log,prof}
+# project-001-[012].{log,prof}
+# etc.
+prefix = 'project'
+
+# The SConscript files being tested don't do any SConf
+# configuration, so run their normal ./configure script
+# before we invoke SCons.
+initial_commands = [
+ './configure',
+]
+
+# Only time building the bin/project executable.
+targets = 'bin/project'
+
+# Time against SCons revisions of the branches/core branch
+subversion_url = 'http://scons.tigris.org/svn/scons/branches/core'
+.EE
+'\"==========================================================================
+.SH ENVIRONMENT
+The
+.B scons-time
+script uses the following environment variables:
+.TP
+.B PRESERVE
+If this value is set,
+the
+.B scons-time
+script will
+.I not
+remove the temporary directory or directories
+in which it builds the specified configuration
+or downloads a specific version of SCons.
+'\"==========================================================================
+.SH "SEE ALSO"
+.BR gnuplot (1),
+.BR scons (1)
+
+.SH AUTHORS
+Steven Knight <knight at baldmt dot com>
diff --git a/doc/man/scons.1 b/doc/man/scons.1
new file mode 100644
index 0000000..f57169b
--- /dev/null
+++ b/doc/man/scons.1
@@ -0,0 +1,10149 @@
+.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation the rights to use, copy, modify, merge, publish,
+.\" distribute, sublicense, and/or sell copies of the Software, and to
+.\" permit persons to whom the Software is furnished to do so, subject to
+.\" the following conditions:
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" doc/man/scons.1 4577 2009/12/27 19:44:43 scons
+.\"
+.TH SCONS 1 "December 2009"
+.\" ES - Example Start - indents and turns off line fill
+.rm ES
+.de ES
+.RS
+.nf
+..
+.\" EE - Example End - ends indent and turns line fill back on
+.rm EE
+.de EE
+.fi
+.RE
+..
+.SH NAME
+scons \- a software construction tool
+.SH SYNOPSIS
+.B scons
+[
+.IR options ...
+]
+[
+.IR name = val ...
+]
+[
+.IR targets ...
+]
+.SH DESCRIPTION
+
+The
+.B scons
+utility builds software (or other files) by determining which
+component pieces must be rebuilt and executing the necessary commands to
+rebuild them.
+
+By default,
+.B scons
+searches for a file named
+.IR SConstruct ,
+.IR Sconstruct ,
+or
+.I sconstruct
+(in that order) in the current directory and reads its
+configuration from the first file found.
+An alternate file name may be
+specified via the
+.B -f
+option.
+
+The
+.I SConstruct
+file can specify subsidiary
+configuration files using the
+.B SConscript()
+function.
+By convention,
+these subsidiary files are named
+.IR SConscript ,
+although any name may be used.
+(Because of this naming convention,
+the term "SConscript files"
+is sometimes used to refer
+generically to all
+.B scons
+configuration files,
+regardless of actual file name.)
+
+The configuration files
+specify the target files to be built, and
+(optionally) the rules to build those targets. Reasonable default
+rules exist for building common software components (executable
+programs, object files, libraries), so that for most software
+projects, only the target and input files need be specified.
+
+Before reading the
+.I SConstruct
+file,
+.B scons
+looks for a directory named
+.I site_scons
+in the directory containing the
+.I SConstruct
+file; if it exists,
+.I site_scons
+is added to sys.path,
+the file
+.IR site_scons/site_init.py ,
+is evaluated if it exists,
+and the directory
+.I site_scons/site_tools
+is added to the default toolpath if it exist.
+See the
+.I --no-site-dir
+and
+.I --site-dir
+options for more details.
+
+.B scons
+reads and executes the SConscript files as Python scripts,
+so you may use normal Python scripting capabilities
+(such as flow control, data manipulation, and imported Python libraries)
+to handle complicated build situations.
+.BR scons ,
+however, reads and executes all of the SConscript files
+.I before
+it begins building any targets.
+To make this obvious,
+.B scons
+prints the following messages about what it is doing:
+
+.ES
+$ scons foo.out
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+cp foo.in foo.out
+scons: done building targets.
+$
+.EE
+
+The status messages
+(everything except the line that reads "cp foo.in foo.out")
+may be suppressed using the
+.B -Q
+option.
+
+.B scons
+does not automatically propagate
+the external environment used to execute
+.B scons
+to the commands used to build target files.
+This is so that builds will be guaranteed
+repeatable regardless of the environment
+variables set at the time
+.B scons
+is invoked.
+This also means that if the compiler or other commands
+that you want to use to build your target files
+are not in standard system locations,
+.B scons
+will not find them unless
+you explicitly set the PATH
+to include those locations.
+Whenever you create an
+.B scons
+construction environment,
+you can propagate the value of PATH
+from your external environment as follows:
+
+.ES
+import os
+env = Environment(ENV = {'PATH' : os.environ['PATH']})
+.EE
+
+Similarly, if the commands use external environment variables
+like $PATH, $HOME, $JAVA_HOME, $LANG, $SHELL, $TERM, etc.,
+these variables can also be explicitly propagated:
+
+.ES
+import os
+env = Environment(ENV = {'PATH' : os.environ['PATH'],
+ 'HOME' : os.environ['HOME']})
+.EE
+
+Or you may explicitly propagate the invoking user's
+complete external environment:
+
+.ES
+import os
+env = Environment(ENV = os.environ)
+.EE
+
+This comes at the expense of making your build
+dependent on the user's environment being set correctly,
+but it may be more convenient for many configurations.
+
+.B scons
+can scan known input files automatically for dependency
+information (for example, #include statements
+in C or C++ files) and will rebuild dependent files appropriately
+whenever any "included" input file changes.
+.B scons
+supports the
+ability to define new scanners for unknown input file types.
+
+.B scons
+knows how to fetch files automatically from
+SCCS or RCS subdirectories
+using SCCS, RCS or BitKeeper.
+
+.B scons
+is normally executed in a top-level directory containing a
+.I SConstruct
+file, optionally specifying
+as command-line arguments
+the target file or files to be built.
+
+By default, the command
+
+.ES
+scons
+.EE
+
+will build all target files in or below the current directory.
+Explicit default targets
+(to be built when no targets are specified on the command line)
+may be defined the SConscript file(s)
+using the
+.B Default()
+function, described below.
+
+Even when
+.B Default()
+targets are specified in the SConscript file(s),
+all target files in or below the current directory
+may be built by explicitly specifying
+the current directory (.)
+as a command-line target:
+
+.ES
+scons .
+.EE
+
+Building all target files,
+including any files outside of the current directory,
+may be specified by supplying a command-line target
+of the root directory (on POSIX systems):
+
+.ES
+scons /
+.EE
+
+or the path name(s) of the volume(s) in which all the targets
+should be built (on Windows systems):
+
+.ES
+scons C:\\ D:\\
+.EE
+
+To build only specific targets,
+supply them as command-line arguments:
+
+.ES
+scons foo bar
+.EE
+
+in which case only the specified targets will be built
+(along with any derived files on which they depend).
+
+Specifying "cleanup" targets in SConscript files is not usually necessary.
+The
+.B -c
+flag removes all files
+necessary to build the specified target:
+
+.ES
+scons -c .
+.EE
+
+to remove all target files, or:
+
+.ES
+scons -c build export
+.EE
+
+to remove target files under build and export.
+Additional files or directories to remove can be specified using the
+.BR Clean()
+function.
+Conversely, targets that would normally be removed by the
+.B -c
+invocation
+can be prevented from being removed by using the
+.BR NoClean ()
+function.
+
+A subset of a hierarchical tree may be built by
+remaining at the top-level directory (where the
+.I SConstruct
+file lives) and specifying the subdirectory as the target to be
+built:
+
+.ES
+scons src/subdir
+.EE
+
+or by changing directory and invoking scons with the
+.B -u
+option, which traverses up the directory
+hierarchy until it finds the
+.I SConstruct
+file, and then builds
+targets relatively to the current subdirectory:
+
+.ES
+cd src/subdir
+scons -u .
+.EE
+
+.B scons
+supports building multiple targets in parallel via a
+.B -j
+option that takes, as its argument, the number
+of simultaneous tasks that may be spawned:
+
+.ES
+scons -j 4
+.EE
+
+builds four targets in parallel, for example.
+
+.B scons
+can maintain a cache of target (derived) files that can
+be shared between multiple builds. When caching is enabled in a
+SConscript file, any target files built by
+.B scons
+will be copied
+to the cache. If an up-to-date target file is found in the cache, it
+will be retrieved from the cache instead of being rebuilt locally.
+Caching behavior may be disabled and controlled in other ways by the
+.BR --cache-force ,
+.BR --cache-disable ,
+and
+.B --cache-show
+command-line options. The
+.B --random
+option is useful to prevent multiple builds
+from trying to update the cache simultaneously.
+
+Values of variables to be passed to the SConscript file(s)
+may be specified on the command line:
+
+.ES
+scons debug=1 .
+.EE
+
+These variables are available in SConscript files
+through the ARGUMENTS dictionary,
+and can be used in the SConscript file(s) to modify
+the build in any way:
+
+.ES
+if ARGUMENTS.get('debug', 0):
+ env = Environment(CCFLAGS = '-g')
+else:
+ env = Environment()
+.EE
+
+The command-line variable arguments are also available
+in the ARGLIST list,
+indexed by their order on the command line.
+This allows you to process them in order rather than by name,
+if necessary.
+ARGLIST[0] returns a tuple
+containing (argname, argvalue).
+A Python exception is thrown if you
+try to access a list member that
+does not exist.
+
+.B scons
+requires Python version 1.5.2 or later.
+There should be no other dependencies or requirements to run
+.B scons.
+
+.\" The following paragraph reflects the default tool search orders
+.\" currently in SCons/Tool/__init__.py. If any of those search orders
+.\" change, this documentation should change, too.
+By default,
+.B scons
+knows how to search for available programming tools
+on various systems.
+On Windows systems,
+.B scons
+searches in order for the
+Microsoft Visual C++ tools,
+the MinGW tool chain,
+the Intel compiler tools,
+and the PharLap ETS compiler.
+On OS/2 systems,
+.B scons
+searches in order for the
+OS/2 compiler,
+the GCC tool chain,
+and the Microsoft Visual C++ tools,
+On SGI IRIX, IBM AIX, Hewlett Packard HP-UX, and Sun Solaris systems,
+.B scons
+searches for the native compiler tools
+(MIPSpro, Visual Age, aCC, and Forte tools respectively)
+and the GCC tool chain.
+On all other platforms,
+including POSIX (Linux and UNIX) platforms,
+.B scons
+searches in order
+for the GCC tool chain,
+the Microsoft Visual C++ tools,
+and the Intel compiler tools.
+You may, of course, override these default values
+by appropriate configuration of
+Environment construction variables.
+
+.SH OPTIONS
+In general,
+.B scons
+supports the same command-line options as GNU
+.BR make ,
+and many of those supported by
+.BR cons .
+
+.TP
+-b
+Ignored for compatibility with non-GNU versions of
+.BR make.
+
+.TP
+-c, --clean, --remove
+Clean up by removing all target files for which a construction
+command is specified.
+Also remove any files or directories associated to the construction command
+using the
+.BR Clean ()
+function.
+Will not remove any targets specified by the
+.BR NoClean ()
+function.
+
+.TP
+.RI --cache-debug= file
+Print debug information about the
+.BR CacheDir ()
+derived-file caching
+to the specified
+.IR file .
+If
+.I file
+is
+.B \-
+(a hyphen),
+the debug information are printed to the standard output.
+The printed messages describe what signature file names are
+being looked for in, retrieved from, or written to the
+.BR CacheDir ()
+directory tree.
+
+.TP
+--cache-disable, --no-cache
+Disable the derived-file caching specified by
+.BR CacheDir ().
+.B scons
+will neither retrieve files from the cache
+nor copy files to the cache.
+
+.TP
+--cache-force, --cache-populate
+When using
+.BR CacheDir (),
+populate a cache by copying any already-existing, up-to-date
+derived files to the cache,
+in addition to files built by this invocation.
+This is useful to populate a new cache with
+all the current derived files,
+or to add to the cache any derived files
+recently built with caching disabled via the
+.B --cache-disable
+option.
+
+.TP
+--cache-show
+When using
+.BR CacheDir ()
+and retrieving a derived file from the cache,
+show the command
+that would have been executed to build the file,
+instead of the usual report,
+"Retrieved `file' from cache."
+This will produce consistent output for build logs,
+regardless of whether a target
+file was rebuilt or retrieved from the cache.
+
+.TP
+.RI --config= mode
+This specifies how the
+.B Configure
+call should use or generate the
+results of configuration tests.
+The option should be specified from
+among the following choices:
+
+.TP
+--config=auto
+scons will use its normal dependency mechanisms
+to decide if a test must be rebuilt or not.
+This saves time by not running the same configuration tests
+every time you invoke scons,
+but will overlook changes in system header files
+or external commands (such as compilers)
+if you don't specify those dependecies explicitly.
+This is the default behavior.
+
+.TP
+--config=force
+If this option is specified,
+all configuration tests will be re-run
+regardless of whether the
+cached results are out of date.
+This can be used to explicitly
+force the configuration tests to be updated
+in response to an otherwise unconfigured change
+in a system header file or compiler.
+
+.TP
+--config=cache
+If this option is specified,
+no configuration tests will be rerun
+and all results will be taken from cache.
+Note that scons will still consider it an error
+if --config=cache is specified
+and a necessary test does not
+yet have any results in the cache.
+
+.TP
+.RI "-C" " directory" ", --directory=" directory
+Change to the specified
+.I directory
+before searching for the
+.IR SConstruct ,
+.IR Sconstruct ,
+or
+.I sconstruct
+file, or doing anything
+else. Multiple
+.B -C
+options are interpreted
+relative to the previous one, and the right-most
+.B -C
+option wins. (This option is nearly
+equivalent to
+.BR "-f directory/SConstruct" ,
+except that it will search for
+.IR SConstruct ,
+.IR Sconstruct ,
+or
+.I sconstruct
+in the specified directory.)
+
+.\" .TP
+.\" -d
+.\" Display dependencies while building target files. Useful for
+.\" figuring out why a specific file is being rebuilt, as well as
+.\" general debugging of the build process.
+
+.TP
+-D
+Works exactly the same way as the
+.B -u
+option except for the way default targets are handled.
+When this option is used and no targets are specified on the command line,
+all default targets are built, whether or not they are below the current
+directory.
+
+.TP
+.RI --debug= type
+Debug the build process.
+.I type
+specifies what type of debugging:
+
+.TP
+--debug=count
+Print how many objects are created
+of the various classes used internally by SCons
+before and after reading the SConscript files
+and before and after building targets.
+This is not supported when run under Python versions earlier than 2.1,
+when SCons is executed with the Python
+.B -O
+(optimized) option,
+or when the SCons modules
+have been compiled with optimization
+(that is, when executing from
+.B *.pyo
+files).
+
+.TP
+--debug=dtree
+A synonym for the newer
+.B --tree=derived
+option.
+This will be deprecated in some future release
+and ultimately removed.
+
+.TP
+--debug=explain
+Print an explanation of precisely why
+.B scons
+is deciding to (re-)build any targets.
+(Note: this does not print anything
+for targets that are
+.I not
+rebuilt.)
+
+.TP
+--debug=findlibs
+Instruct the scanner that searches for libraries
+to print a message about each potential library
+name it is searching for,
+and about the actual libraries it finds.
+
+.TP
+--debug=includes
+Print the include tree after each top-level target is built.
+This is generally used to find out what files are included by the sources
+of a given derived file:
+
+.ES
+$ scons --debug=includes foo.o
+.EE
+
+.TP
+--debug=memoizer
+Prints a summary of hits and misses using the Memoizer,
+an internal subsystem that counts
+how often SCons uses cached values in memory
+instead of recomputing them each time they're needed.
+Only available when using Python 2.2 or later.
+
+.TP
+--debug=memory
+Prints how much memory SCons uses
+before and after reading the SConscript files
+and before and after building targets.
+
+.TP
+--debug=nomemoizer
+A deprecated option preserved for backwards compatibility.
+
+.TP
+--debug=objects
+Prints a list of the various objects
+of the various classes used internally by SCons.
+This only works when run under Python 2.1 or later.
+
+.TP
+--debug=pdb
+Re-run SCons under the control of the
+.RI pdb
+Python debugger.
+
+.TP
+--debug=presub
+Print the raw command line used to build each target
+before the construction environment variables are substituted.
+Also shows which targets are being built by this command.
+Output looks something like this:
+.ES
+$ scons --debug=presub
+Building myprog.o with action(s):
+ $SHCC $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES
+\&...
+.EE
+
+.TP
+--debug=stacktrace
+Prints an internal Python stack trace
+when encountering an otherwise unexplained error.
+
+.TP
+--debug=stree
+A synonym for the newer
+.B --tree=all,status
+option.
+This will be deprecated in some future release
+and ultimately removed.
+
+.TP
+--debug=time
+Prints various time profiling information:
+the time spent executing each individual build command;
+the total build time (time SCons ran from beginning to end);
+the total time spent reading and executing SConscript files;
+the total time spent SCons itself spend running
+(that is, not counting reading and executing SConscript files);
+and both the total time spent executing all build commands
+and the elapsed wall-clock time spent executing those build commands.
+(When
+.B scons
+is executed without the
+.B -j
+option,
+the elapsed wall-clock time will typically
+be slightly longer than the total time spent
+executing all the build commands,
+due to the SCons processing that takes place
+in between executing each command.
+When
+.B scons
+is executed
+.I with
+the
+.B -j
+option,
+and your build configuration allows good parallelization,
+the elapsed wall-clock time should
+be significantly smaller than the
+total time spent executing all the build commands,
+since multiple build commands and
+intervening SCons processing
+should take place in parallel.)
+
+.TP
+--debug=tree
+A synonym for the newer
+.B --tree=all
+option.
+This will be deprecated in some future release
+and ultimately removed.
+
+.TP
+.RI --diskcheck= types
+Enable specific checks for
+whether or not there is a file on disk
+where the SCons configuration expects a directory
+(or vice versa),
+and whether or not RCS or SCCS sources exist
+when searching for source and include files.
+The
+.I types
+argument can be set to:
+.BR all ,
+to enable all checks explicitly
+(the default behavior);
+.BR none ,
+to disable all such checks;
+.BR match ,
+to check that files and directories on disk
+match SCons' expected configuration;
+.BR rcs ,
+to check for the existence of an RCS source
+for any missing source or include files;
+.BR sccs ,
+to check for the existence of an SCCS source
+for any missing source or include files.
+Multiple checks can be specified separated by commas;
+for example,
+.B --diskcheck=sccs,rcs
+would still check for SCCS and RCS sources,
+but disable the check for on-disk matches of files and directories.
+Disabling some or all of these checks
+can provide a performance boost for large configurations,
+or when the configuration will check for files and/or directories
+across networked or shared file systems,
+at the slight increased risk of an incorrect build
+or of not handling errors gracefully
+(if include files really should be
+found in SCCS or RCS, for example,
+or if a file really does exist
+where the SCons configuration expects a directory).
+
+.TP
+.RI --duplicate= ORDER
+There are three ways to duplicate files in a build tree: hard links,
+soft (symbolic) links and copies. The default behaviour of SCons is to
+prefer hard links to soft links to copies. You can specify different
+behaviours with this option.
+.IR ORDER
+must be one of
+.IR hard-soft-copy
+(the default),
+.IR soft-hard-copy ,
+.IR hard-copy ,
+.IR soft-copy
+or
+.IR copy .
+SCons will attempt to duplicate files using
+the mechanisms in the specified order.
+
+.\" .TP
+.\" -e, --environment-overrides
+.\" Variables from the execution environment override construction
+.\" variables from the SConscript files.
+
+.TP
+.RI -f " file" ", --file=" file ", --makefile=" file ", --sconstruct=" file
+Use
+.I file
+as the initial SConscript file.
+Multiple
+.B -f
+options may be specified,
+in which case
+.B scons
+will read all of the specified files.
+
+.TP
+-h, --help
+Print a local help message for this build, if one is defined in
+the SConscript file(s), plus a line that describes the
+.B -H
+option for command-line option help. If no local help message
+is defined, prints the standard help message about command-line
+options. Exits after displaying the appropriate message.
+
+.TP
+-H, --help-options
+Print the standard help message about command-line options and
+exit.
+
+.TP
+-i, --ignore-errors
+Ignore all errors from commands executed to rebuild files.
+
+.TP
+.RI -I " directory" ", --include-dir=" directory
+Specifies a
+.I directory
+to search for
+imported Python modules. If several
+.B -I
+options
+are used, the directories are searched in the order specified.
+
+.TP
+--implicit-cache
+Cache implicit dependencies.
+This causes
+.B scons
+to use the implicit (scanned) dependencies
+from the last time it was run
+instead of scanning the files for implicit dependencies.
+This can significantly speed up SCons,
+but with the following limitations:
+.IP
+.B scons
+will not detect changes to implicit dependency search paths
+(e.g.
+.BR CPPPATH ", " LIBPATH )
+that would ordinarily
+cause different versions of same-named files to be used.
+.IP
+.B scons
+will miss changes in the implicit dependencies
+in cases where a new implicit
+dependency is added earlier in the implicit dependency search path
+(e.g.
+.BR CPPPATH ", " LIBPATH )
+than a current implicit dependency with the same name.
+
+.TP
+--implicit-deps-changed
+Forces SCons to ignore the cached implicit dependencies. This causes the
+implicit dependencies to be rescanned and recached. This implies
+.BR --implicit-cache .
+
+.TP
+--implicit-deps-unchanged
+Force SCons to ignore changes in the implicit dependencies.
+This causes cached implicit dependencies to always be used.
+This implies
+.BR --implicit-cache .
+
+.TP
+--interactive
+Starts SCons in interactive mode.
+The SConscript files are read once and a
+.B "scons>>>"
+prompt is printed.
+Targets may now be rebuilt by typing commands at interactive prompt
+without having to re-read the SConscript files
+and re-initialize the dependency graph from scratch.
+
+SCons interactive mode supports the following commands:
+
+.RS 10
+.TP 6
+.BI build "[OPTIONS] [TARGETS] ..."
+Builds the specified
+.I TARGETS
+(and their dependencies)
+with the specified
+SCons command-line
+.IR OPTIONS .
+.B b
+and
+.B scons
+are synonyms.
+
+The following SCons command-line options affect the
+.B build
+command:
+
+.ES
+--cache-debug=FILE
+--cache-disable, --no-cache
+--cache-force, --cache-populate
+--cache-show
+--debug=TYPE
+-i, --ignore-errors
+-j N, --jobs=N
+-k, --keep-going
+-n, --no-exec, --just-print, --dry-run, --recon
+-Q
+-s, --silent, --quiet
+--taskmastertrace=FILE
+--tree=OPTIONS
+.EE
+
+.IP "" 6
+Any other SCons command-line options that are specified
+do not cause errors
+but have no effect on the
+.B build
+command
+(mainly because they affect how the SConscript files are read,
+which only happens once at the beginning of interactive mode).
+
+.TP 6
+.BI clean "[OPTIONS] [TARGETS] ..."
+Cleans the specified
+.I TARGETS
+(and their dependencies)
+with the specified options.
+.B c
+is a synonym.
+This command is itself a synonym for
+.B "build --clean"
+
+.TP 6
+.BI exit
+Exits SCons interactive mode.
+You can also exit by terminating input
+(CTRL+D on UNIX or Linux systems,
+CTRL+Z on Windows systems).
+
+.TP 6
+.BI help "[COMMAND]"
+Provides a help message about
+the commands available in SCons interactive mode.
+If
+.I COMMAND
+is specified,
+.B h
+and
+.B ?
+are synonyms.
+
+.TP 6
+.BI shell "[COMMANDLINE]"
+Executes the specified
+.I COMMANDLINE
+in a subshell.
+If no
+.I COMMANDLINE
+is specified,
+executes the interactive command interpreter
+specified in the
+.B SHELL
+environment variable
+(on UNIX and Linux systems)
+or the
+.B COMSPEC
+environment variable
+(on Windows systems).
+.B sh
+and
+.B !
+are synonyms.
+
+.TP 6
+.B version
+Prints SCons version information.
+.RE
+
+.IP
+An empty line repeats the last typed command.
+Command-line editing can be used if the
+.B readline
+module is available.
+
+.ES
+$ scons --interactive
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons>>> build -n prog
+scons>>> exit
+.EE
+
+.TP
+.RI -j " N" ", --jobs=" N
+Specifies the number of jobs (commands) to run simultaneously.
+If there is more than one
+.B -j
+option, the last one is effective.
+.\" ??? If the
+.\" .B -j
+.\" option
+.\" is specified without an argument,
+.\" .B scons
+.\" will not limit the number of
+.\" simultaneous jobs.
+
+.TP
+-k, --keep-going
+Continue as much as possible after an error. The target that
+failed and those that depend on it will not be remade, but other
+targets specified on the command line will still be processed.
+
+.\" .TP
+.\" .RI -l " N" ", --load-average=" N ", --max-load=" N
+.\" No new jobs (commands) will be started if
+.\" there are other jobs running and the system load
+.\" average is at least
+.\" .I N
+.\" (a floating-point number).
+
+.\"
+.\" .TP
+.\" --list-derived
+.\" List derived files (targets, dependencies) that would be built,
+.\" but do not build them.
+.\" [XXX This can probably go away with the right
+.\" combination of other options. Revisit this issue.]
+.\"
+.\" .TP
+.\" --list-actions
+.\" List derived files that would be built, with the actions
+.\" (commands) that build them. Does not build the files.
+.\" [XXX This can probably go away with the right
+.\" combination of other options. Revisit this issue.]
+.\"
+.\" .TP
+.\" --list-where
+.\" List derived files that would be built, plus where the file is
+.\" defined (file name and line number). Does not build the files.
+.\" [XXX This can probably go away with the right
+.\" combination of other options. Revisit this issue.]
+
+.TP
+-m
+Ignored for compatibility with non-GNU versions of
+.BR make .
+
+.TP
+.RI --max-drift= SECONDS
+Set the maximum expected drift in the modification time of files to
+.IR SECONDS .
+This value determines how long a file must be unmodified
+before its cached content signature
+will be used instead of
+calculating a new content signature (MD5 checksum)
+of the file's contents.
+The default value is 2 days, which means a file must have a
+modification time of at least two days ago in order to have its
+cached content signature used.
+A negative value means to never cache the content
+signature and to ignore the cached value if there already is one. A value
+of 0 means to always use the cached signature,
+no matter how old the file is.
+
+.TP
+.RI --md5-chunksize= KILOBYTES
+Set the block size used to compute MD5 signatures to
+.IR KILOBYTES .
+This value determines the size of the chunks which are read in at once when
+computing MD5 signatures. Files below that size are fully stored in memory
+before performing the signature computation while bigger files are read in
+block-by-block. A huge block-size leads to high memory consumption while a very
+small block-size slows down the build considerably.
+
+The default value is to use a chunk size of 64 kilobytes, which should
+be appropriate for most uses.
+
+.TP
+-n, --just-print, --dry-run, --recon
+No execute. Print the commands that would be executed to build
+any out-of-date target files, but do not execute the commands.
+
+.TP
+.RI --no-site-dir
+Prevents the automatic addition of the standard
+.I site_scons
+dir to
+.IR sys.path .
+Also prevents loading the
+.I site_scons/site_init.py
+module if it exists, and prevents adding
+.I site_scons/site_tools
+to the toolpath.
+
+.\" .TP
+.\" .RI -o " file" ", --old-file=" file ", --assume-old=" file
+.\" Do not rebuild
+.\" .IR file ,
+.\" and do
+.\" not rebuild anything due to changes in the contents of
+.\" .IR file .
+.\" .TP
+.\" .RI --override " file"
+.\" Read values to override specific build environment variables
+.\" from the specified
+.\" .IR file .
+.\" .TP
+.\" -p
+.\" Print the data base (construction environments,
+.\" Builder and Scanner objects) that are defined
+.\" after reading the SConscript files.
+.\" After printing, a normal build is performed
+.\" as usual, as specified by other command-line options.
+.\" This also prints version information
+.\" printed by the
+.\" .B -v
+.\" option.
+.\"
+.\" To print the database without performing a build do:
+.\"
+.\" .ES
+.\" scons -p -q
+.\" .EE
+
+.TP
+.RI --profile= file
+Run SCons under the Python profiler
+and save the results in the specified
+.IR file .
+The results may be analyzed using the Python
+pstats module.
+
+.TP
+-q, --question
+Do not run any commands, or print anything. Just return an exit
+status that is zero if the specified targets are already up to
+date, non-zero otherwise.
+.TP
+-Q
+Quiets SCons status messages about
+reading SConscript files,
+building targets
+and entering directories.
+Commands that are executed
+to rebuild target files are still printed.
+
+.\" .TP
+.\" -r, -R, --no-builtin-rules, --no-builtin-variables
+.\" Clear the default construction variables. Construction
+.\" environments that are created will be completely empty.
+
+.TP
+--random
+Build dependencies in a random order. This is useful when
+building multiple trees simultaneously with caching enabled,
+to prevent multiple builds from simultaneously trying to build
+or retrieve the same target files.
+
+.TP
+-s, --silent, --quiet
+Silent. Do not print commands that are executed to rebuild
+target files.
+Also suppresses SCons status messages.
+
+.TP
+-S, --no-keep-going, --stop
+Ignored for compatibility with GNU
+.BR make .
+
+.TP
+.RI --site-dir= dir
+Uses the named dir as the site dir rather than the default
+.I site_scons
+dir. This dir will get prepended to
+.IR sys.path ,
+the module
+.IR dir /site_init.py
+will get loaded if it exists, and
+.IR dir /site_tools
+will get added to the default toolpath.
+
+.TP
+.RI --stack-size= KILOBYTES
+Set the size stack used to run threads to
+.IR KILOBYTES .
+This value determines the stack size of the threads used to run jobs.
+These are the threads that execute the actions of the builders for the
+nodes that are out-of-date.
+Note that this option has no effect unless the
+.B num_jobs
+option, which corresponds to -j and --jobs, is larger than one. Using
+a stack size that is too small may cause stack overflow errors. This
+usually shows up as segmentation faults that cause scons to abort
+before building anything. Using a stack size that is too large will
+cause scons to use more memory than required and may slow down the entire
+build process.
+
+The default value is to use a stack size of 256 kilobytes, which should
+be appropriate for most uses. You should not need to increase this value
+unless you encounter stack overflow errors.
+
+.TP
+-t, --touch
+Ignored for compatibility with GNU
+.BR make .
+(Touching a file to make it
+appear up-to-date is unnecessary when using
+.BR scons .)
+
+.TP
+.RI --taskmastertrace= file
+Prints trace information to the specified
+.I file
+about how the internal Taskmaster object
+evaluates and controls the order in which Nodes are built.
+A file name of
+.B -
+may be used to specify the standard output.
+
+.TP
+.RI -tree= options
+Prints a tree of the dependencies
+after each top-level target is built.
+This prints out some or all of the tree,
+in various formats,
+depending on the
+.I options
+specified:
+
+.TP
+--tree=all
+Print the entire dependency tree
+after each top-level target is built.
+This prints out the complete dependency tree,
+including implicit dependencies and ignored dependencies.
+
+.TP
+--tree=derived
+Restricts the tree output to only derived (target) files,
+not source files.
+
+.TP
+--tree=status
+Prints status information for each displayed node.
+
+.TP
+--tree=prune
+Prunes the tree to avoid repeating dependency information
+for nodes that have already been displayed.
+Any node that has already been displayed
+will have its name printed in
+.BR "[square brackets]" ,
+as an indication that the dependencies
+for that node can be found by searching
+for the relevant output higher up in the tree.
+
+.IP
+Multiple options may be specified,
+separated by commas:
+
+.ES
+# Prints only derived files, with status information:
+scons --tree=derived,status
+
+# Prints all dependencies of target, with status information
+# and pruning dependencies of already-visited Nodes:
+scons --tree=all,prune,status target
+.EE
+
+.TP
+-u, --up, --search-up
+Walks up the directory structure until an
+.I SConstruct ,
+.I Sconstruct
+or
+.I sconstruct
+file is found, and uses that
+as the top of the directory tree.
+If no targets are specified on the command line,
+only targets at or below the
+current directory will be built.
+
+.TP
+-U
+Works exactly the same way as the
+.B -u
+option except for the way default targets are handled.
+When this option is used and no targets are specified on the command line,
+all default targets that are defined in the SConscript(s) in the current
+directory are built, regardless of what directory the resultant targets end
+up in.
+
+.TP
+-v, --version
+Print the
+.B scons
+version, copyright information,
+list of authors, and any other relevant information.
+Then exit.
+
+.TP
+-w, --print-directory
+Print a message containing the working directory before and
+after other processing.
+
+.TP
+--no-print-directory
+Turn off -w, even if it was turned on implicitly.
+
+.TP
+.RI --warn= type ", --warn=no-" type
+Enable or disable warnings.
+.I type
+specifies the type of warnings to be enabled or disabled:
+
+.TP
+--warn=all, --warn=no-all
+Enables or disables all warnings.
+
+.TP
+--warn=cache-write-error, --warn=no-cache-write-error
+Enables or disables warnings about errors trying to
+write a copy of a built file to a specified
+.BR CacheDir ().
+These warnings are disabled by default.
+
+.TP
+--warn=corrupt-sconsign, --warn=no-corrupt-sconsign
+Enables or disables warnings about unfamiliar signature data in
+.B .sconsign
+files.
+These warnings are enabled by default.
+
+.TP
+--warn=dependency, --warn=no-dependency
+Enables or disables warnings about dependencies.
+These warnings are disabled by default.
+
+.TP
+--warn=deprecated, --warn=no-deprecated
+Enables or disables all warnings about use of
+currently deprecated features.
+These warnings are enabled by default.
+Note that the
+.B --warn=no-deprecated
+option does not disable warnings about absolutely all deprecated features.
+Warnings for some deprecated features that have already been through
+several releases with deprecation warnings
+may be mandatory for a release or two
+before they are officially no longer supported by SCons.
+Warnings for some specific deprecated features
+may be enabled or disabled individually;
+see below.
+
+.RS
+.TP
+--warn=deprecated-copy, --warn=no-deprecated-copy
+Enables or disables warnings about use of the deprecated
+.B env.Copy()
+method.
+
+.TP
+--warn=deprecated-source-signatures, --warn=no-deprecated-source-signatures
+Enables or disables warnings about use of the deprecated
+.B SourceSignatures()
+function or
+.B env.SourceSignatures()
+method.
+
+.TP
+--warn=deprecated-target-signatures, --warn=no-deprecated-target-signatures
+Enables or disables warnings about use of the deprecated
+.B TargetSignatures()
+function or
+.B env.TargetSignatures()
+method.
+.RE
+
+.TP
+--warn=duplicate-environment, --warn=no-duplicate-environment
+Enables or disables warnings about attempts to specify a build
+of a target with two different construction environments
+that use the same action.
+These warnings are enabled by default.
+
+.TP
+--warn=fortran-cxx-mix, --warn=no-fortran-cxx-mix
+Enables or disables the specific warning about linking
+Fortran and C++ object files in a single executable,
+which can yield unpredictable behavior with some compilers.
+
+.TP
+--warn=future-deprecated, --warn=no-future-deprecated
+Enables or disables warnings about features
+that will be deprecated in the future.
+These warnings are disabled by default.
+Enabling this warning is especially
+recommended for projects that redistribute
+SCons configurations for other users to build,
+so that the project can be warned as soon as possible
+about to-be-deprecated features
+that may require changes to the configuration.
+
+.TP
+--warn=link, --warn=no-link
+Enables or disables warnings about link steps.
+
+.TP
+--warn=misleading-keywords, --warn=no-misleading-keywords
+Enables or disables warnings about use of the misspelled keywords
+.B targets
+and
+.B sources
+when calling Builders.
+(Note the last
+.B s
+characters, the correct spellings are
+.B target
+and
+.B source.)
+These warnings are enabled by default.
+
+.TP
+--warn=missing-sconscript, --warn=no-missing-sconscript
+Enables or disables warnings about missing SConscript files.
+These warnings are enabled by default.
+
+.TP
+--warn=no-md5-module, --warn=no-no-md5-module
+Enables or disables warnings about the version of Python
+not having an MD5 checksum module available.
+These warnings are enabled by default.
+
+.TP
+--warn=no-metaclass-support, --warn=no-no-metaclass-support
+Enables or disables warnings about the version of Python
+not supporting metaclasses when the
+.B --debug=memoizer
+option is used.
+These warnings are enabled by default.
+
+.TP
+--warn=no-object-count, --warn=no-no-object-count
+Enables or disables warnings about the
+.B --debug=object
+feature not working when
+.B scons
+is run with the python
+.B \-O
+option or from optimized Python (.pyo) modules.
+
+.TP
+--warn=no-parallel-support, --warn=no-no-parallel-support
+Enables or disables warnings about the version of Python
+not being able to support parallel builds when the
+.B -j
+option is used.
+These warnings are enabled by default.
+
+.TP
+--warn=python-version, --warn=no-python-version
+Enables or disables the warning about running
+SCons with a deprecated version of Python.
+These warnings are enabled by default.
+
+.TP
+--warn=reserved-variable, --warn=no-reserved-variable
+Enables or disables warnings about attempts to set the
+reserved construction variable names
+.BR CHANGED_SOURCES ,
+.BR CHANGED_TARGETS ,
+.BR TARGET ,
+.BR TARGETS ,
+.BR SOURCE ,
+.BR SOURCES ,
+.BR UNCHANGED_SOURCES
+or
+.BR UNCHANGED_TARGETS .
+These warnings are disabled by default.
+
+.TP
+--warn=stack-size, --warn=no-stack-size
+Enables or disables warnings about requests to set the stack size
+that could not be honored.
+These warnings are enabled by default.
+
+.\" .TP
+.\" .RI --write-filenames= file
+.\" Write all filenames considered into
+.\" .IR file .
+.\"
+.\" .TP
+.\" .RI -W " file" ", --what-if=" file ", --new-file=" file ", --assume-new=" file
+.\" Pretend that the target
+.\" .I file
+.\" has been
+.\" modified. When used with the
+.\" .B -n
+.\" option, this
+.\" show you what would be rebuilt if you were to modify that file.
+.\" Without
+.\" .B -n
+.\" ... what? XXX
+.\"
+.\" .TP
+.\" --warn-undefined-variables
+.\" Warn when an undefined variable is referenced.
+
+.TP
+.RI -Y " repository" ", --repository=" repository ", --srcdir=" repository
+Search the specified repository for any input and target
+files not found in the local directory hierarchy. Multiple
+.B -Y
+options may be specified, in which case the
+repositories are searched in the order specified.
+
+.SH CONFIGURATION FILE REFERENCE
+.\" .SS Python Basics
+.\" XXX Adding this in the future would be a help.
+.SS Construction Environments
+A construction environment is the basic means by which the SConscript
+files communicate build information to
+.BR scons .
+A new construction environment is created using the
+.B Environment
+function:
+
+.ES
+env = Environment()
+.EE
+
+Variables, called
+.I construction
+.IR variables ,
+may be set in a construction environment
+either by specifying them as keywords when the object is created
+or by assigning them a value after the object is created:
+
+.ES
+env = Environment(FOO = 'foo')
+env['BAR'] = 'bar'
+.EE
+
+As a convenience,
+construction variables may also be set or modified by the
+.I parse_flags
+keyword argument, which applies the
+.B ParseFlags
+method (described below) to the argument value
+after all other processing is completed.
+This is useful either if the exact content of the flags is unknown
+(for example, read from a control file)
+or if the flags are distributed to a number of construction variables.
+
+.ES
+env = Environment(parse_flags = '-Iinclude -DEBUG -lm')
+.EE
+
+This example adds 'include' to
+.BR CPPPATH ,
+\&'EBUG' to
+.BR CPPDEFINES ,
+and 'm' to
+.BR LIBS .
+
+By default, a new construction environment is
+initialized with a set of builder methods
+and construction variables that are appropriate
+for the current platform.
+An optional platform keyword argument may be
+used to specify that an environment should
+be initialized for a different platform:
+
+.ES
+env = Environment(platform = 'cygwin')
+env = Environment(platform = 'os2')
+env = Environment(platform = 'posix')
+env = Environment(platform = 'win32')
+.EE
+
+Specifying a platform initializes the appropriate
+construction variables in the environment
+to use and generate file names with prefixes
+and suffixes appropriate for the platform.
+
+Note that the
+.B win32
+platform adds the
+.B SystemDrive
+and
+.B SystemRoot
+variables from the user's external environment
+to the construction environment's
+.B ENV
+dictionary.
+This is so that any executed commands
+that use sockets to connect with other systems
+(such as fetching source files from
+external CVS repository specifications like
+.BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons )
+will work on Windows systems.
+
+The platform argument may be function or callable object,
+in which case the Environment() method
+will call the specified argument to update
+the new construction environment:
+
+.ES
+def my_platform(env):
+ env['VAR'] = 'xyzzy'
+
+env = Environment(platform = my_platform)
+.EE
+
+Additionally, a specific set of tools
+with which to initialize the environment
+may be specified as an optional keyword argument:
+
+.ES
+env = Environment(tools = ['msvc', 'lex'])
+.EE
+
+Non-built-in tools may be specified using the toolpath argument:
+
+.ES
+env = Environment(tools = ['default', 'foo'], toolpath = ['tools'])
+.EE
+
+This looks for a tool specification in tools/foo.py (as well as
+using the ordinary default tools for the platform). foo.py should
+have two functions: generate(env, **kw) and exists(env).
+The
+.B generate()
+function
+modifies the passed-in environment
+to set up variables so that the tool
+can be executed;
+it may use any keyword arguments
+that the user supplies (see below)
+to vary its initialization.
+The
+.B exists()
+function should return a true
+value if the tool is available.
+Tools in the toolpath are used before
+any of the built-in ones. For example, adding gcc.py to the toolpath
+would override the built-in gcc tool.
+Also note that the toolpath is
+stored in the environment for use
+by later calls to
+.BR Clone ()
+and
+.BR Tool ()
+methods:
+
+.ES
+base = Environment(toolpath=['custom_path'])
+derived = base.Clone(tools=['custom_tool'])
+derived.CustomBuilder()
+.EE
+
+The elements of the tools list may also
+be functions or callable objects,
+in which case the Environment() method
+will call the specified elements
+to update the new construction environment:
+
+.ES
+def my_tool(env):
+ env['XYZZY'] = 'xyzzy'
+
+env = Environment(tools = [my_tool])
+.EE
+
+The individual elements of the tools list
+may also themselves be two-element lists of the form
+.RI ( toolname ", " kw_dict ).
+SCons searches for the
+.I toolname
+specification file as described above, and
+passes
+.IR kw_dict ,
+which must be a dictionary, as keyword arguments to the tool's
+.B generate
+function.
+The
+.B generate
+function can use the arguments to modify the tool's behavior
+by setting up the environment in different ways
+or otherwise changing its initialization.
+
+.ES
+# in tools/my_tool.py:
+def generate(env, **kw):
+ # Sets MY_TOOL to the value of keyword argument 'arg1' or 1.
+ env['MY_TOOL'] = kw.get('arg1', '1')
+def exists(env):
+ return 1
+
+# in SConstruct:
+env = Environment(tools = ['default', ('my_tool', {'arg1': 'abc'})],
+ toolpath=['tools'])
+.EE
+
+The tool definition (i.e. my_tool()) can use the PLATFORM variable from
+the environment it receives to customize the tool for different platforms.
+
+If no tool list is specified, then SCons will auto-detect the installed
+tools using the PATH variable in the ENV construction variable and the
+platform name when the Environment is constructed. Changing the PATH
+variable after the Environment is constructed will not cause the tools to
+be redetected.
+
+SCons supports the following tool specifications out of the box:
+
+.ES
+386asm
+aixc++
+aixcc
+aixf77
+aixlink
+ar
+as
+bcc32
+c++
+cc
+cvf
+dmd
+dvipdf
+dvips
+f77
+f90
+f95
+fortran
+g++
+g77
+gas
+gcc
+gfortran
+gnulink
+gs
+hpc++
+hpcc
+hplink
+icc
+icl
+ifl
+ifort
+ilink
+ilink32
+intelc
+jar
+javac
+javah
+latex
+lex
+link
+linkloc
+m4
+masm
+midl
+mingw
+mslib
+mslink
+mssdk
+msvc
+msvs
+mwcc
+mwld
+nasm
+pdflatex
+pdftex
+qt
+rmic
+rpcgen
+sgiar
+sgic++
+sgicc
+sgilink
+sunar
+sunc++
+suncc
+sunf77
+sunf90
+sunf95
+sunlink
+swig
+tar
+tex
+textfile
+tlib
+yacc
+zip
+.EE
+
+Additionally, there is a "tool" named
+.B default
+which configures the
+environment with a default set of tools for the current platform.
+
+On posix and cygwin platforms
+the GNU tools (e.g. gcc) are preferred by SCons,
+on Windows the Microsoft tools (e.g. msvc)
+followed by MinGW are preferred by SCons,
+and in OS/2 the IBM tools (e.g. icc) are preferred by SCons.
+
+.SS Builder Methods
+
+Build rules are specified by calling a construction
+environment's builder methods.
+The arguments to the builder methods are
+.B target
+(a list of targets to be built,
+usually file names)
+and
+.B source
+(a list of sources to be built,
+usually file names).
+
+Because long lists of file names
+can lead to a lot of quoting,
+.B scons
+supplies a
+.B Split()
+global function
+and a same-named environment method
+that split a single string
+into a list, separated on
+strings of white-space characters.
+(These are similar to the
+string.split() method
+from the standard Python library,
+but work even if the input isn't a string.)
+
+Like all Python arguments,
+the target and source arguments to a builder method
+can be specified either with or without
+the "target" and "source" keywords.
+When the keywords are omitted,
+the target is first,
+followed by the source.
+The following are equivalent examples of calling the Program builder method:
+
+.ES
+env.Program('bar', ['bar.c', 'foo.c'])
+env.Program('bar', Split('bar.c foo.c'))
+env.Program('bar', env.Split('bar.c foo.c'))
+env.Program(source = ['bar.c', 'foo.c'], target = 'bar')
+env.Program(target = 'bar', Split('bar.c foo.c'))
+env.Program(target = 'bar', env.Split('bar.c foo.c'))
+env.Program('bar', source = string.split('bar.c foo.c'))
+.EE
+
+Target and source file names
+that are not absolute path names
+(that is, do not begin with
+.B /
+on POSIX systems
+or
+.B \\
+on Windows systems,
+with or without
+an optional drive letter)
+are interpreted relative to the directory containing the
+.B SConscript
+file being read.
+An initial
+.B #
+(hash mark)
+on a path name means that the rest of the file name
+is interpreted relative to
+the directory containing
+the top-level
+.B SConstruct
+file,
+even if the
+.B #
+is followed by a directory separator character
+(slash or backslash).
+
+Examples:
+
+.ES
+# The comments describing the targets that will be built
+# assume these calls are in a SConscript file in the
+# a subdirectory named "subdir".
+
+# Builds the program "subdir/foo" from "subdir/foo.c":
+env.Program('foo', 'foo.c')
+
+# Builds the program "/tmp/bar" from "subdir/bar.c":
+env.Program('/tmp/bar', 'bar.c')
+
+# An initial '#' or '#/' are equivalent; the following
+# calls build the programs "foo" and "bar" (in the
+# top-level SConstruct directory) from "subdir/foo.c" and
+# "subdir/bar.c", respectively:
+env.Program('#foo', 'foo.c')
+env.Program('#/bar', 'bar.c')
+
+# Builds the program "other/foo" (relative to the top-level
+# SConstruct directory) from "subdir/foo.c":
+env.Program('#other/foo', 'foo.c')
+.EE
+
+When the target shares the same base name
+as the source and only the suffix varies,
+and if the builder method has a suffix defined for the target file type,
+then the target argument may be omitted completely,
+and
+.B scons
+will deduce the target file name from
+the source file name.
+The following examples all build the
+executable program
+.B bar
+(on POSIX systems)
+or
+.B bar.exe
+(on Windows systems)
+from the bar.c source file:
+
+.ES
+env.Program(target = 'bar', source = 'bar.c')
+env.Program('bar', source = 'bar.c')
+env.Program(source = 'bar.c')
+env.Program('bar.c')
+.EE
+
+As a convenience, a
+.B srcdir
+keyword argument may be specified
+when calling a Builder.
+When specified,
+all source file strings that are not absolute paths
+will be interpreted relative to the specified
+.BR srcdir .
+The following example will build the
+.B build/prog
+(or
+.B build/prog.exe
+on Windows)
+program from the files
+.B src/f1.c
+and
+.BR src/f2.c :
+
+.ES
+env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src')
+.EE
+
+It is possible to override or add construction variables when calling a
+builder method by passing additional keyword arguments.
+These overridden or added
+variables will only be in effect when building the target, so they will not
+affect other parts of the build. For example, if you want to add additional
+libraries for just one program:
+
+.ES
+env.Program('hello', 'hello.c', LIBS=['gl', 'glut'])
+.EE
+
+or generate a shared library with a non-standard suffix:
+
+.ES
+env.SharedLibrary('word', 'word.cpp',
+ SHLIBSUFFIX='.ocx',
+ LIBSUFFIXES=['.ocx'])
+.EE
+
+(Note that both the $SHLIBSUFFIX and $LIBSUFFIXES variables must be set
+if you want SCons to search automatically
+for dependencies on the non-standard library names;
+see the descriptions of these variables, below, for more information.)
+
+It is also possible to use the
+.I parse_flags
+keyword argument in an override:
+
+.ES
+env = Program('hello', 'hello.c', parse_flags = '-Iinclude -DEBUG -lm')
+.EE
+
+This example adds 'include' to
+.BR CPPPATH ,
+\&'EBUG' to
+.BR CPPDEFINES ,
+and 'm' to
+.BR LIBS .
+
+Although the builder methods defined by
+.B scons
+are, in fact,
+methods of a construction environment object,
+they may also be called without an explicit environment:
+
+.ES
+Program('hello', 'hello.c')
+SharedLibrary('word', 'word.cpp')
+.EE
+
+In this case,
+the methods are called internally using a default construction
+environment that consists of the tools and values that
+.B scons
+has determined are appropriate for the local system.
+
+Builder methods that can be called without an explicit
+environment may be called from custom Python modules that you
+import into an SConscript file by adding the following
+to the Python module:
+
+.ES
+from SCons.Script import *
+.EE
+
+All builder methods return a list-like object
+containing Nodes that
+represent the target or targets that will be built.
+A
+.I Node
+is an internal SCons object
+which represents
+build targets or sources.
+
+The returned Node-list object
+can be passed to other builder methods as source(s)
+or passed to any SCons function or method
+where a filename would normally be accepted.
+For example, if it were necessary
+to add a specific
+.B -D
+flag when compiling one specific object file:
+
+.ES
+bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR')
+env.Program(source = ['foo.c', bar_obj_list, 'main.c'])
+.EE
+
+Using a Node in this way
+makes for a more portable build
+by avoiding having to specify
+a platform-specific object suffix
+when calling the Program() builder method.
+
+Note that Builder calls will automatically "flatten"
+the source and target file lists,
+so it's all right to have the bar_obj list
+return by the StaticObject() call
+in the middle of the source file list.
+If you need to manipulate a list of lists returned by Builders
+directly using Python,
+you can either build the list by hand:
+
+.ES
+foo = Object('foo.c')
+bar = Object('bar.c')
+objects = ['begin.o'] + foo + ['middle.o'] + bar + ['end.o']
+for object in objects:
+ print str(object)
+.EE
+
+Or you can use the
+.BR Flatten ()
+function supplied by scons
+to create a list containing just the Nodes,
+which may be more convenient:
+
+.ES
+foo = Object('foo.c')
+bar = Object('bar.c')
+objects = Flatten(['begin.o', foo, 'middle.o', bar, 'end.o'])
+for object in objects:
+ print str(object)
+.EE
+
+Note also that because Builder calls return
+a list-like object, not an actual Python list,
+you should
+.I not
+use the Python
+.B +=
+operator to append Builder results to a Python list.
+Because the list and the object are different types,
+Python will not update the original list in place,
+but will instead create a new Node-list object
+containing the concatenation of the list
+elements and the Builder results.
+This will cause problems for any other Python variables
+in your SCons configuration
+that still hold on to a reference to the original list.
+Instead, use the Python
+.B .extend()
+method to make sure the list is updated in-place.
+Example:
+
+.ES
+object_files = []
+
+# Do NOT use += as follows:
+#
+# object_files += Object('bar.c')
+#
+# It will not update the object_files list in place.
+#
+# Instead, use the .extend() method:
+object_files.extend(Object('bar.c'))
+
+.EE
+
+The path name for a Node's file may be used
+by passing the Node to the Python-builtin
+.B str()
+function:
+
+.ES
+bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR')
+print "The path to bar_obj is:", str(bar_obj_list[0])
+.EE
+
+Note again that because the Builder call returns a list,
+we have to access the first element in the list
+.B (bar_obj_list[0])
+to get at the Node that actually represents
+the object file.
+
+Builder calls support a
+.B chdir
+keyword argument that
+specifies that the Builder's action(s)
+should be executed
+after changing directory.
+If the
+.B chdir
+argument is
+a string or a directory Node,
+scons will change to the specified directory.
+If the
+.B chdir
+is not a string or Node
+and is non-zero,
+then scons will change to the
+target file's directory.
+
+.ES
+# scons will change to the "sub" subdirectory
+# before executing the "cp" command.
+env.Command('sub/dir/foo.out', 'sub/dir/foo.in',
+ "cp dir/foo.in dir/foo.out",
+ chdir='sub')
+
+# Because chdir is not a string, scons will change to the
+# target's directory ("sub/dir") before executing the
+# "cp" command.
+env.Command('sub/dir/foo.out', 'sub/dir/foo.in',
+ "cp foo.in foo.out",
+ chdir=1)
+.EE
+
+Note that scons will
+.I not
+automatically modify
+its expansion of
+construction variables like
+.B $TARGET
+and
+.B $SOURCE
+when using the chdir
+keyword argument--that is,
+the expanded file names
+will still be relative to
+the top-level SConstruct directory,
+and consequently incorrect
+relative to the chdir directory.
+If you use the chdir keyword argument,
+you will typically need to supply a different
+command line using
+expansions like
+.B ${TARGET.file}
+and
+.B ${SOURCE.file}
+to use just the filename portion of the
+targets and source.
+
+.B scons
+provides the following builder methods:
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+'\" BEGIN GENERATED BUILDER DESCRIPTIONS
+'\"
+'\" The descriptions below of the various SCons Builders are generated
+'\" from the .xml files that live next to the various Python modules in
+'\" the build enginer library. If you're reading this [gnt]roff file
+'\" with an eye towards patching this man page, you can still submit
+'\" a diff against this text, but it will have to be translated to a
+'\" diff against the underlying .xml file before the patch is actually
+'\" accepted. If you do that yourself, it will make it easier to
+'\" integrate the patch.
+'\"
+'\" BEGIN GENERATED BUILDER DESCRIPTIONS
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.so builders.man
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+'\" END GENERATED BUILDER DESCRIPTIONS
+'\"
+'\" The descriptions above of the various SCons Builders are generated
+'\" from the .xml files that live next to the various Python modules in
+'\" the build enginer library. If you're reading this [gnt]roff file
+'\" with an eye towards patching this man page, you can still submit
+'\" a diff against this text, but it will have to be translated to a
+'\" diff against the underlying .xml file before the patch is actually
+'\" accepted. If you do that yourself, it will make it easier to
+'\" integrate the patch.
+'\"
+'\" END GENERATED BUILDER DESCRIPTIONS
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+.P
+All
+targets of builder methods automatically depend on their sources.
+An explicit dependency can
+be specified using the
+.B Depends
+method of a construction environment (see below).
+
+In addition,
+.B scons
+automatically scans
+source files for various programming languages,
+so the dependencies do not need to be specified explicitly.
+By default, SCons can
+C source files,
+C++ source files,
+Fortran source files with
+.B .F
+(POSIX systems only),
+.B .fpp,
+or
+.B .FPP
+file extensions,
+and assembly language files with
+.B .S
+(POSIX systems only),
+.B .spp,
+or
+.B .SPP
+files extensions
+for C preprocessor dependencies.
+SCons also has default support
+for scanning D source files,
+You can also write your own Scanners
+to add support for additional source file types.
+These can be added to the default
+Scanner object used by the
+.BR Object (),
+.BR StaticObject (),
+and
+.BR SharedObject ()
+Builders by adding them
+to the
+.B SourceFileScanner
+object as follows:
+
+See the section "Scanner Objects,"
+below, for a more information about
+defining your own Scanner objects.
+
+.SS Methods and Functions to Do Things
+In addition to Builder methods,
+.B scons
+provides a number of other construction environment methods
+and global functions to
+manipulate the build configuration.
+
+Usually, a construction environment method
+and global function with the same name both exist
+so that you don't have to remember whether
+to a specific bit of functionality
+must be called with or without a construction environment.
+In the following list,
+if you call something as a global function
+it looks like:
+.ES
+.RI Function( arguments )
+.EE
+and if you call something through a construction
+environment it looks like:
+.ES
+.RI env.Function( arguments )
+.EE
+If you can call the functionality in both ways,
+then both forms are listed.
+
+Global functions may be called from custom Python modules that you
+import into an SConscript file by adding the following
+to the Python module:
+
+.ES
+from SCons.Script import *
+.EE
+
+Except where otherwise noted,
+the same-named
+construction environment method
+and global function
+provide the exact same functionality.
+The only difference is that,
+where appropriate,
+calling the functionality through a construction environment will
+substitute construction variables into
+any supplied strings.
+For example:
+
+.ES
+env = Environment(FOO = 'foo')
+Default('$FOO')
+env.Default('$FOO')
+.EE
+
+In the above example,
+the first call to the global
+.B Default()
+function will actually add a target named
+.B $FOO
+to the list of default targets,
+while the second call to the
+.B env.Default()
+construction environment method
+will expand the value
+and add a target named
+.B foo
+to the list of default targets.
+For more on construction variable expansion,
+see the next section on
+construction variables.
+
+Construction environment methods
+and global functions supported by
+.B scons
+include:
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Action( action ", [" cmd/str/fun ", [" var ", ...]] [" option = value ", ...])"
+.TP
+.IR env .Action( action ", [" cmd/str/fun ", [" var ", ...]] [" option = value ", ...])"
+Creates an Action object for
+the specified
+.IR action .
+See the section "Action Objects,"
+below, for a complete explanation of the arguments and behavior.
+
+Note that the
+.BR env.Action ()
+form of the invocation will expand
+construction variables in any argument strings,
+including the
+.I action
+argument, at the time it is called
+using the construction variables in the
+.I env
+construction environment through which
+.BR env.Action ()
+was called.
+The
+.BR Action ()
+form delays all variable expansion
+until the Action object is actually used.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI AddMethod( object, function ", [" name ])
+.TP
+.RI env.AddMethod( function ", [" name ])
+When called with the
+.BR AddMethod ()
+form,
+adds the specified
+.I function
+to the specified
+.I object
+as the specified method
+.IR name .
+When called with the
+.BR env.AddMethod ()
+form,
+adds the specified
+.I function
+to the construction environment
+.I env
+as the specified method
+.IR name .
+In both cases, if
+.I name
+is omitted or
+.BR None ,
+the name of the
+specified
+.I function
+itself is used for the method name.
+
+Examples:
+
+.ES
+# Note that the first argument to the function to
+# be attached as a method must be the object through
+# which the method will be called; the Python
+# convention is to call it 'self'.
+def my_method(self, arg):
+ print "my_method() got", arg
+
+# Use the global AddMethod() function to add a method
+# to the Environment class. This
+AddMethod(Environment, my_method)
+env = Environment()
+env.my_method('arg')
+
+# Add the function as a method, using the function
+# name for the method call.
+env = Environment()
+env.AddMethod(my_method, 'other_method_name')
+env.other_method_name('another arg')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI AddOption( arguments )
+This function adds a new command-line option to be recognized.
+The specified
+.I arguments
+are the same as supported by the standard Python
+.BR optparse.add_option ()
+method (with a few additional capabilities noted below);
+see the documentation for
+.B optparse
+for a thorough discussion of its option-processing capabities.
+(Note that although the
+.B optparse
+module was not a standard module until Python 2.3,
+.B scons
+contains a compatible version of the module
+that is used to provide identical functionality
+when run by earlier Python versions.)
+
+In addition to the arguments and values supported by the
+.B optparse.add_option ()
+method,
+the SCons
+.BR AddOption ()
+function allows you to set the
+.B nargs
+keyword value to
+.B '?'
+(a string with just the question mark)
+to indicate that the specified long option(s) take(s) an
+.I optional
+argument.
+When
+.B "nargs = '?'"
+is passed to the
+.BR AddOption ()
+function, the
+.B const
+keyword argument
+may be used to supply the "default"
+value that should be used when the
+option is specified on the command line
+without an explicit argument.
+
+If no
+.B default=
+keyword argument is supplied when calling
+.BR AddOption (),
+the option will have a default value of
+.BR None .
+
+Once a new command-line option has been added with
+.BR AddOption (),
+the option value may be accessed using
+.BR GetOption ()
+or
+.BR env.GetOption ().
+\" NOTE: in SCons 1.x or 2.0, user options will be settable, but not yet.
+\" Uncomment this when that works. See tigris issue 2105.
+\" The value may also be set, using
+\" .BR SetOption ()
+\" or
+\" .BR env.SetOption (),
+\" if conditions in a
+\" .B SConscript
+\" require overriding any default value.
+\" Note, however, that a
+\" value specified on the command line will
+\" .I always
+\" override a value set by any SConscript file.
+
+Any specified
+.B help=
+strings for the new option(s)
+will be displayed by the
+.B -H
+or
+.B -h
+options
+(the latter only if no other help text is
+specified in the SConscript files).
+The help text for the local options specified by
+.BR AddOption ()
+will appear below the SCons options themselves,
+under a separate
+.B "Local Options"
+heading.
+The options will appear in the help text
+in the order in which the
+.BR AddOption ()
+calls occur.
+
+Example:
+
+.ES
+AddOption('--prefix',
+ dest='prefix',
+ nargs=1, type='string',
+ action='store',
+ metavar='DIR',
+ help='installation prefix')
+env = Environment(PREFIX = GetOption('prefix'))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI AddPostAction( target ", " action )
+.TP
+.RI env.AddPostAction( target ", " action )
+Arranges for the specified
+.I action
+to be performed
+after the specified
+.I target
+has been built.
+The specified action(s) may be
+an Action object, or anything that
+can be converted into an Action object
+(see below).
+
+When multiple targets are supplied,
+the action may be called multiple times,
+once after each action that generates
+one or more targets in the list.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI AddPreAction( target ", " action )
+.TP
+.RI env.AddPreAction( target ", " action )
+Arranges for the specified
+.I action
+to be performed
+before the specified
+.I target
+is built.
+The specified action(s) may be
+an Action object, or anything that
+can be converted into an Action object
+(see below).
+
+When multiple targets are specified,
+the action(s) may be called multiple times,
+once before each action that generates
+one or more targets in the list.
+
+Note that if any of the targets are built in multiple steps,
+the action will be invoked just
+before the "final" action that specifically
+generates the specified target(s).
+For example, when building an executable program
+from a specified source
+.B .c
+file via an intermediate object file:
+
+.ES
+foo = Program('foo.c')
+AddPreAction(foo, 'pre_action')
+.EE
+
+The specified
+.B pre_action
+would be executed before
+.B scons
+calls the link command that actually
+generates the executable program binary
+.BR foo ,
+not before compiling the
+.B foo.c
+file into an object file.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Alias( alias ", [" targets ", [" action ]])
+.TP
+.RI env.Alias( alias ", [" targets ", [" action ]])
+Creates one or more phony targets that
+expand to one or more other targets.
+An optional
+.I action
+(command)
+or list of actions
+can be specified that will be executed
+whenever the any of the alias targets are out-of-date.
+Returns the Node object representing the alias,
+which exists outside of any file system.
+This Node object, or the alias name,
+may be used as a dependency of any other target,
+including another alias.
+.B Alias
+can be called multiple times for the same
+alias to add additional targets to the alias,
+or additional actions to the list for this alias.
+
+Examples:
+
+.ES
+Alias('install')
+Alias('install', '/usr/bin')
+Alias(['install', 'install-lib'], '/usr/local/lib')
+
+env.Alias('install', ['/usr/local/bin', '/usr/local/lib'])
+env.Alias('install', ['/usr/local/man'])
+
+env.Alias('update', ['file1', 'file2'], "update_database $SOURCES")
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI AllowSubstExceptions([ exception ", ...])"
+Specifies the exceptions that will be allowed
+when expanding construction variables.
+By default,
+any construction variable expansions that generate a
+.B NameError
+or
+.BR IndexError
+exception will expand to a
+.B ''
+(a null string) and not cause scons to fail.
+All exceptions not in the specified list
+will generate an error message
+and terminate processing.
+
+If
+.B AllowSubstExceptions
+is called multiple times,
+each call completely overwrites the previous list
+of allowed exceptions.
+
+Example:
+
+.ES
+# Requires that all construction variable names exist.
+# (You may wish to do this if you want to enforce strictly
+# that all construction variables must be defined before use.)
+AllowSubstExceptions()
+
+# Also allow a string containing a zero-division expansion
+# like '${1 / 0}' to evalute to ''.
+AllowSubstExceptions(IndexError, NameError, ZeroDivisionError)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI AlwaysBuild( target ", ...)"
+.TP
+.RI env.AlwaysBuild( target ", ...)"
+Marks each given
+.I target
+so that it is always assumed to be out of date,
+and will always be rebuilt if needed.
+Note, however, that
+.BR AlwaysBuild ()
+does not add its target(s) to the default target list,
+so the targets will only be built
+if they are specified on the command line,
+or are a dependent of a target specified on the command line--but
+they will
+.I always
+be built if so specified.
+Multiple targets can be passed in to a single call to
+.BR AlwaysBuild ().
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Append( key = val ", [...])"
+Appends the specified keyword arguments
+to the end of construction variables in the environment.
+If the Environment does not have
+the specified construction variable,
+it is simply added to the environment.
+If the values of the construction variable
+and the keyword argument are the same type,
+then the two values will be simply added together.
+Otherwise, the construction variable
+and the value of the keyword argument
+are both coerced to lists,
+and the lists are added together.
+(See also the Prepend method, below.)
+
+Example:
+
+.ES
+env.Append(CCFLAGS = ' -g', FOO = ['foo.yyy'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.AppendENVPath( name ", " newpath ", [" envname ", " sep ", " delete_existing ])
+This appends new path elements to the given path in the
+specified external environment
+.RB ( ENV
+by default).
+This will only add
+any particular path once (leaving the last one it encounters and
+ignoring the rest, to preserve path order),
+and to help assure this,
+will normalize all paths (using
+.B os.path.normpath
+and
+.BR os.path.normcase ).
+This can also handle the
+case where the given old path variable is a list instead of a
+string, in which case a list will be returned instead of a string.
+
+If
+.I delete_existing
+is 0, then adding a path that already exists
+will not move it to the end; it will stay where it is in the list.
+
+Example:
+
+.ES
+print 'before:',env['ENV']['INCLUDE']
+include_path = '/foo/bar:/foo'
+env.AppendENVPath('INCLUDE', include_path)
+print 'after:',env['ENV']['INCLUDE']
+
+yields:
+before: /foo:/biz
+after: /biz:/foo/bar:/foo
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.AppendUnique( key = val ", [...], delete_existing=0)"
+Appends the specified keyword arguments
+to the end of construction variables in the environment.
+If the Environment does not have
+the specified construction variable,
+it is simply added to the environment.
+If the construction variable being appended to is a list,
+then any value(s) that already exist in the
+construction variable will
+.I not
+be added again to the list.
+However, if delete_existing is 1,
+existing matching values are removed first, so
+existing values in the arg list move to the end of the list.
+
+Example:
+
+.ES
+env.AppendUnique(CCFLAGS = '-g', FOO = ['foo.yyy'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+env.BitKeeper()
+A factory function that
+returns a Builder object
+to be used to fetch source files
+using BitKeeper.
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+Example:
+
+.ES
+env.SourceCode('.', env.BitKeeper())
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI BuildDir( build_dir ", " src_dir ", [" duplicate ])
+.TP
+.RI env.BuildDir( build_dir ", " src_dir ", [" duplicate ])
+Deprecated synonyms for
+.BR VariantDir ()
+and
+.BR env.VariantDir ().
+The
+.I build_dir
+argument becomes the
+.I variant_dir
+argument of
+.BR VariantDir ()
+or
+.BR env.VariantDir ().
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Builder( action ", [" arguments ])
+.TP
+.RI env.Builder( action ", [" arguments ])
+Creates a Builder object for
+the specified
+.IR action .
+See the section "Builder Objects,"
+below, for a complete explanation of the arguments and behavior.
+
+Note that the
+.BR env.Builder ()
+form of the invocation will expand
+construction variables in any arguments strings,
+including the
+.I action
+argument,
+at the time it is called
+using the construction variables in the
+.B env
+construction environment through which
+.BR env.Builder ()
+was called.
+The
+.BR Builder ()
+form delays all variable expansion
+until after the Builder object is actually called.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI CacheDir( cache_dir )
+.TP
+.RI env.CacheDir( cache_dir )
+Specifies that
+.B scons
+will maintain a cache of derived files in
+.I cache_dir .
+The derived files in the cache will be shared
+among all the builds using the same
+.BR CacheDir ()
+call.
+Specifying a
+.I cache_dir
+of
+.B None
+disables derived file caching.
+
+Calling
+.BR env.CacheDir ()
+will only affect targets built
+through the specified construction environment.
+Calling
+.BR CacheDir ()
+sets a global default
+that will be used by all targets built
+through construction environments
+that do
+.I not
+have an
+.BR env.CacheDir ()
+specified.
+
+When a
+.BR CacheDir ()
+is being used and
+.B scons
+finds a derived file that needs to be rebuilt,
+it will first look in the cache to see if a
+derived file has already been built
+from identical input files and an identical build action
+(as incorporated into the MD5 build signature).
+If so,
+.B scons
+will retrieve the file from the cache.
+If the derived file is not present in the cache,
+.B scons
+will rebuild it and
+then place a copy of the built file in the cache
+(identified by its MD5 build signature),
+so that it may be retrieved by other
+builds that need to build the same derived file
+from identical inputs.
+
+Use of a specified
+.BR CacheDir()
+may be disabled for any invocation
+by using the
+.B --cache-disable
+option.
+
+If the
+.B --cache-force
+option is used,
+.B scons
+will place a copy of
+.I all
+derived files in the cache,
+even if they already existed
+and were not built by this invocation.
+This is useful to populate a cache
+the first time
+.BR CacheDir ()
+is added to a build,
+or after using the
+.B --cache-disable
+option.
+
+When using
+.BR CacheDir (),
+.B scons
+will report,
+"Retrieved `file' from cache,"
+unless the
+.B --cache-show
+option is being used.
+When the
+.B --cache-show
+option is used,
+.B scons
+will print the action that
+.I would
+have been used to build the file,
+without any indication that
+the file was actually retrieved from the cache.
+This is useful to generate build logs
+that are equivalent regardless of whether
+a given derived file has been built in-place
+or retrieved from the cache.
+
+The
+.BR NoCache ()
+method can be used to disable caching of specific files. This can be
+useful if inputs and/or outputs of some tool are impossible to
+predict or prohibitively large.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Clean( targets ", " files_or_dirs )
+.TP
+.RI env.Clean( targets ", " files_or_dirs )
+This specifies a list of files or directories which should be removed
+whenever the targets are specified with the
+.B -c
+command line option.
+The specified targets may be a list
+or an individual target.
+Multiple calls to
+.BR Clean ()
+are legal,
+and create new targets or add files and directories to the
+clean list for the specified targets.
+
+Multiple files or directories should be specified
+either as separate arguments to the
+.BR Clean ()
+method, or as a list.
+.BR Clean ()
+will also accept the return value of any of the construction environment
+Builder methods.
+Examples:
+
+The related
+.BR NoClean ()
+function overrides calling
+.BR Clean ()
+for the same target,
+and any targets passed to both functions will
+.I not
+be removed by the
+.B -c
+option.
+
+Examples:
+
+.ES
+Clean('foo', ['bar', 'baz'])
+Clean('dist', env.Program('hello', 'hello.c'))
+Clean(['foo', 'bar'], 'something_else_to_clean')
+.EE
+
+In this example,
+installing the project creates a subdirectory for the documentation.
+This statement causes the subdirectory to be removed
+if the project is deinstalled.
+.ES
+Clean(docdir, os.path.join(docdir, projectname))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Command( target ", " source ", " action ", [" key = val ", ...])"
+.TP
+.RI env.Command( target ", " source ", " action ", [" key = val ", ...])"
+Executes a specific action
+(or list of actions)
+to build a target file or files.
+This is more convenient
+than defining a separate Builder object
+for a single special-case build.
+
+As a special case, the
+.B source_scanner
+keyword argument can
+be used to specify
+a Scanner object
+that will be used to scan the sources.
+(The global
+.B DirScanner
+object can be used
+if any of the sources will be directories
+that must be scanned on-disk for
+changes to files that aren't
+already specified in other Builder of function calls.)
+
+Any other keyword arguments specified override any
+same-named existing construction variables.
+
+An action can be an external command,
+specified as a string,
+or a callable Python object;
+see "Action Objects," below,
+for more complete information.
+Also note that a string specifying an external command
+may be preceded by an
+.B @
+(at-sign)
+to suppress printing the command in question,
+or by a
+.B \-
+(hyphen)
+to ignore the exit status of the external command.
+
+Examples:
+
+.ES
+env.Command('foo.out', 'foo.in',
+ "$FOO_BUILD < $SOURCES > $TARGET")
+
+env.Command('bar.out', 'bar.in',
+ ["rm -f $TARGET",
+ "$BAR_BUILD < $SOURCES > $TARGET"],
+ ENV = {'PATH' : '/usr/local/bin/'})
+
+def rename(env, target, source):
+ import os
+ os.rename('.tmp', str(target[0]))
+
+env.Command('baz.out', 'baz.in',
+ ["$BAZ_BUILD < $SOURCES > .tmp",
+ rename ])
+.EE
+
+.IP
+Note that the
+.BR Command ()
+function will usually assume, by default,
+that the specified targets and/or sources are Files,
+if no other part of the configuration
+identifies what type of entry it is.
+If necessary, you can explicitly specify
+that targets or source nodes should
+be treated as directoriese
+by using the
+.BR Dir ()
+or
+.BR env.Dir ()
+functions.
+
+Examples:
+
+.ES
+env.Command('ddd.list', Dir('ddd'), 'ls -l $SOURCE > $TARGET')
+
+env['DISTDIR'] = 'destination/directory'
+env.Command(env.Dir('$DISTDIR')), None, make_distdir)
+.EE
+
+.IP
+(Also note that SCons will usually
+automatically create any directory necessary to hold a target file,
+so you normally don't need to create directories by hand.)
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Configure( env ", [" custom_tests ", " conf_dir ", " log_file ", " config_h ])
+.TP
+.RI env.Configure([ custom_tests ", " conf_dir ", " log_file ", " config_h ])
+Creates a Configure object for integrated
+functionality similar to GNU autoconf.
+See the section "Configure Contexts,"
+below, for a complete explanation of the arguments and behavior.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Clone([ key = val ", ...])"
+Return a separate copy of a construction environment.
+If there are any keyword arguments specified,
+they are added to the returned copy,
+overwriting any existing values
+for the keywords.
+
+Example:
+
+.ES
+env2 = env.Clone()
+env3 = env.Clone(CCFLAGS = '-g')
+.EE
+.IP
+Additionally, a list of tools and a toolpath may be specified, as in
+the Environment constructor:
+
+.ES
+def MyTool(env): env['FOO'] = 'bar'
+env4 = env.Clone(tools = ['msvc', MyTool])
+.EE
+
+The
+.I parse_flags
+keyword argument is also recognized:
+
+.ES
+# create an environment for compiling programs that use wxWidgets
+wx_env = env.Clone(parse_flags = '!wx-config --cflags --cxxflags')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Copy([ key = val ", ...])"
+A now-deprecated synonym for
+.BR env.Clone() .
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.CVS( repository ", " module )
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from the specified
+CVS
+.IR repository .
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+The optional specified
+.I module
+will be added to the beginning
+of all repository path names;
+this can be used, in essence,
+to strip initial directory names
+from the repository path names,
+so that you only have to
+replicate part of the repository
+directory hierarchy in your
+local build directory.
+
+Examples:
+
+.ES
+# Will fetch foo/bar/src.c
+# from /usr/local/CVSROOT/foo/bar/src.c.
+env.SourceCode('.', env.CVS('/usr/local/CVSROOT'))
+
+# Will fetch bar/src.c
+# from /usr/local/CVSROOT/foo/bar/src.c.
+env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo'))
+
+# Will fetch src.c
+# from /usr/local/CVSROOT/foo/bar/src.c.
+env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo/bar'))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Decider( function )
+.TP
+.RI env.Decider( function )
+Specifies that all up-to-date decisions for
+targets built through this construction environment
+will be handled by the specified
+.IR function .
+The
+.I function
+can be one of the following strings
+that specify the type of decision function
+to be performed:
+
+.RS 10
+.HP 6
+.B timestamp-newer
+Specifies that a target shall be considered out of date and rebuilt
+if the dependency's timestamp is newer than the target file's timestamp.
+This is the behavior of the classic Make utility,
+and
+.B make
+can be used a synonym for
+.BR timestamp-newer .
+
+.HP 6
+.B timestamp-match
+Specifies that a target shall be considered out of date and rebuilt
+if the dependency's timestamp is different than the
+timestamp recorded the last time the target was built.
+This provides behavior very similar to the classic Make utility
+(in particular, files are not opened up so that their
+contents can be checksummed)
+except that the target will also be rebuilt if a
+dependency file has been restored to a version with an
+.I earlier
+timestamp, such as can happen when restoring files from backup archives.
+
+.HP 6
+.B MD5
+Specifies that a target shall be considered out of date and rebuilt
+if the dependency's content has changed sine the last time
+the target was built,
+as determined be performing an MD5 checksum
+on the dependency's contents
+and comparing it to the checksum recorded the
+last time the target was built.
+.B content
+can be used as a synonym for
+.BR MD5 .
+
+.HP 6
+.B MD5-timestamp
+Specifies that a target shall be considered out of date and rebuilt
+if the dependency's content has changed sine the last time
+the target was built,
+except that dependencies with a timestamp that matches
+the last time the target was rebuilt will be
+assumed to be up-to-date and
+.I not
+rebuilt.
+This provides behavior very similar
+to the
+.B MD5
+behavior of always checksumming file contents,
+with an optimization of not checking
+the contents of files whose timestamps haven't changed.
+The drawback is that SCons will
+.I not
+detect if a file's content has changed
+but its timestamp is the same,
+as might happen in an automated script
+that runs a build,
+updates a file,
+and runs the build again,
+all within a single second.
+.RE
+
+.IP
+Examples:
+
+.ES
+# Use exact timestamp matches by default.
+Decider('timestamp-match')
+
+# Use MD5 content signatures for any targets built
+# with the attached construction environment.
+env.Decider('content')
+.EE
+
+.IP
+In addition to the above already-available functions,
+the
+.I function
+argument may be an actual Python function
+that takes the following three arguments:
+
+.RS 10
+.IP dependency
+The Node (file) which
+should cause the
+.I target
+to be rebuilt
+if it has "changed" since the last tme
+.I target was built.
+
+.IP target
+The Node (file) being built.
+In the normal case,
+this is what should get rebuilt
+if the
+.I dependency
+has "changed."
+
+.IP prev_ni
+Stored information about the state of the
+.I dependency
+the last time the
+.I target
+was built.
+This can be consulted to match various
+file characteristics
+such as the timestamp,
+size, or content signature.
+.RE
+
+.IP
+The
+.I function
+should return a
+.B True
+(non-zero)
+value if the
+.I dependency
+has "changed" since the last time
+the
+.I target
+was built
+(indicating that the target
+.I should
+be rebuilt),
+and
+.B False
+(zero)
+otherwise
+(indicating that the target should
+.I not
+be rebuilt).
+Note that the decision can be made
+using whatever criteria are appopriate.
+Ignoring some or all of the function arguments
+is perfectly normal.
+
+Example:
+
+.ES
+def my_decider(dependency, target, prev_ni):
+ return not os.path.exists(str(target))
+
+env.Decider(my_decider)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Default( targets )
+.TP
+.RI env.Default( targets )
+This specifies a list of default targets,
+which will be built by
+.B scons
+if no explicit targets are given on the command line.
+Multiple calls to
+.BR Default ()
+are legal,
+and add to the list of default targets.
+
+Multiple targets should be specified as
+separate arguments to the
+.BR Default ()
+method, or as a list.
+.BR Default ()
+will also accept the Node returned by any
+of a construction environment's
+builder methods.
+
+Examples:
+
+.ES
+Default('foo', 'bar', 'baz')
+env.Default(['a', 'b', 'c'])
+hello = env.Program('hello', 'hello.c')
+env.Default(hello)
+.EE
+.IP
+An argument to
+.BR Default ()
+of
+.B None
+will clear all default targets.
+Later calls to
+.BR Default ()
+will add to the (now empty) default-target list
+like normal.
+
+The current list of targets added using the
+.BR Default ()
+function or method is available in the
+.B DEFAULT_TARGETS
+list;
+see below.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI DefaultEnvironment([ args ])
+Creates and returns a default construction environment object.
+This construction environment is used internally by SCons
+in order to execute many of the global functions in this list,
+and to fetch source files transparently
+from source code management systems.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Depends( target ", " dependency )
+.TP
+.RI env.Depends( target ", " dependency )
+Specifies an explicit dependency;
+the
+.I target
+will be rebuilt
+whenever the
+.I dependency
+has changed.
+Both the specified
+.I target
+and
+.I dependency
+can be a string
+(usually the path name of a file or directory)
+or Node objects,
+or a list of strings or Node objects
+(such as returned by a Builder call).
+This should only be necessary
+for cases where the dependency
+is not caught by a Scanner
+for the file.
+
+Example:
+
+.ES
+env.Depends('foo', 'other-input-file-for-foo')
+
+mylib = env.Library('mylib.c')
+installed_lib = env.Install('lib', mylib)
+bar = env.Program('bar.c')
+
+# Arrange for the library to be copied into the installation
+# directory before trying to build the "bar" program.
+# (Note that this is for example only. A "real" library
+# dependency would normally be configured through the $LIBS
+# and $LIBPATH variables, not using an env.Depends() call.)
+
+env.Depends(bar, installed_lib)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Dictionary([ vars ])
+Returns a dictionary object
+containing copies of all of the
+construction variables in the environment.
+If there are any variable names specified,
+only the specified construction
+variables are returned in the dictionary.
+
+Example:
+
+.ES
+dict = env.Dictionary()
+cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Dir( name ", [" directory ])
+.TP
+.RI env.Dir( name ", [" directory ])
+This returns a Directory Node,
+an object that represents the specified directory
+.IR name .
+.I name
+can be a relative or absolute path.
+.I directory
+is an optional directory that will be used as the parent directory.
+If no
+.I directory
+is specified, the current script's directory is used as the parent.
+
+If
+.I name
+is a list, SCons returns a list of Dir nodes.
+Construction variables are expanded in
+.IR name .
+
+Directory Nodes can be used anywhere you
+would supply a string as a directory name
+to a Builder method or function.
+Directory Nodes have attributes and methods
+that are useful in many situations;
+see "File and Directory Nodes," below.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Dump([ key ])
+Returns a pretty printable representation of the environment.
+.IR key ,
+if not
+.IR None ,
+should be a string containing the name of the variable of interest.
+
+This SConstruct:
+.ES
+env=Environment()
+print env.Dump('CCCOM')
+.EE
+.IP
+will print:
+.ES
+\&'$CC -c -o $TARGET $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES'
+.EE
+
+.ES
+env=Environment()
+print env.Dump()
+.EE
+.IP
+will print:
+.ES
+{ 'AR': 'ar',
+ 'ARCOM': '$AR $ARFLAGS $TARGET $SOURCES\n$RANLIB $RANLIBFLAGS $TARGET',
+ 'ARFLAGS': ['r'],
+ 'AS': 'as',
+ 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES',
+ 'ASFLAGS': [],
+ ...
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI EnsurePythonVersion( major ", " minor )
+.TP
+.RI env.EnsurePythonVersion( major ", " minor )
+Ensure that the Python version is at least
+.IR major . minor .
+This function will
+print out an error message and exit SCons with a non-zero exit code if the
+actual Python version is not late enough.
+
+Example:
+
+.ES
+EnsurePythonVersion(2,2)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI EnsureSConsVersion( major ", " minor ", [" revision ])
+.TP
+.RI env.EnsureSConsVersion( major ", " minor ", [" revision ])
+Ensure that the SCons version is at least
+.IR major.minor ,
+or
+.IR major.minor.revision .
+if
+.I revision
+is specified.
+This function will
+print out an error message and exit SCons with a non-zero exit code if the
+actual SCons version is not late enough.
+
+Examples:
+
+.ES
+EnsureSConsVersion(0,14)
+
+EnsureSConsVersion(0,96,90)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Environment([ key = value ", ...])"
+.TP
+.RI env.Environment([ key = value ", ...])"
+Return a new construction environment
+initialized with the specified
+.IR key = value
+pairs.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Execute( action ", [" strfunction ", " varlist ])
+.TP
+.RI env.Execute( action ", [" strfunction ", " varlist ])
+Executes an Action object.
+The specified
+.IR action
+may be an Action object
+(see the section "Action Objects,"
+below, for a complete explanation of the arguments and behavior),
+or it may be a command-line string,
+list of commands,
+or executable Python function,
+each of which will be converted
+into an Action object
+and then executed.
+The exit value of the command
+or return value of the Python function
+will be returned.
+
+Note that
+.B scons
+will print an error message if the executed
+.I action
+fails--that is,
+exits with or returns a non-zero value.
+.B scons
+will
+.I not ,
+however,
+automatically terminate the build
+if the specified
+.I action
+fails.
+If you want the build to stop in response to a failed
+.BR Execute ()
+call,
+you must explicitly check for a non-zero return value:
+
+.ES
+Execute(Copy('file.out', 'file.in'))
+
+if Execute("mkdir sub/dir/ectory"):
+ # The mkdir failed, don't try to build.
+ Exit(1)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Exit([ value ])
+.TP
+.RI env.Exit([ value ])
+This tells
+.B scons
+to exit immediately
+with the specified
+.IR value .
+A default exit value of
+.B 0
+(zero)
+is used if no value is specified.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Export( vars )
+.TP
+.RI env.Export( vars )
+This tells
+.B scons
+to export a list of variables from the current
+SConscript file to all other SConscript files.
+The exported variables are kept in a global collection,
+so subsequent calls to
+.BR Export ()
+will over-write previous exports that have the same name.
+Multiple variable names can be passed to
+.BR Export ()
+as separate arguments or as a list.
+Keyword arguments can be used to provide names and their values.
+A dictionary can be used to map variables to a different name when exported.
+Both local variables and global variables can be exported.
+
+Examples:
+
+.ES
+env = Environment()
+# Make env available for all SConscript files to Import().
+Export("env")
+
+package = 'my_name'
+# Make env and package available for all SConscript files:.
+Export("env", "package")
+
+# Make env and package available for all SConscript files:
+Export(["env", "package"])
+
+# Make env available using the name debug:
+Export(debug = env)
+
+# Make env available using the name debug:
+Export({"debug":env})
+.EE
+
+.IP
+Note that the
+.BR SConscript ()
+function supports an
+.I exports
+argument that makes it easier to to export a variable or
+set of variables to a single SConscript file.
+See the description of the
+.BR SConscript ()
+function, below.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI File( name ", [" directory ])
+.TP
+.RI env.File( name ", [" directory ])
+This returns a
+File Node,
+an object that represents the specified file
+.IR name .
+.I name
+can be a relative or absolute path.
+.I directory
+is an optional directory that will be used as the parent directory.
+
+If
+.I name
+is a list, SCons returns a list of File nodes.
+Construction variables are expanded in
+.IR name .
+
+File Nodes can be used anywhere you
+would supply a string as a file name
+to a Builder method or function.
+File Nodes have attributes and methods
+that are useful in many situations;
+see "File and Directory Nodes," below.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI FindFile( file ", " dirs )
+.TP
+.RI env.FindFile( file ", " dirs )
+Search for
+.I file
+in the path specified by
+.IR dirs .
+.I dirs
+may be a list of directory names or a single directory name.
+In addition to searching for files that exist in the filesytem,
+this function also searches for derived files
+that have not yet been built.
+
+Example:
+
+.ES
+foo = env.FindFile('foo', ['dir1', 'dir2'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI FindInstalledFiles( )
+.TP
+.RI env.FindInstalledFiles( )
+Returns the list of targets set up by the
+.B Install()
+or
+.B InstallAs()
+builders.
+
+This function serves as a convenient method to select the contents of
+a binary package.
+
+Example:
+
+.ES
+Install( '/bin', [ 'executable_a', 'executable_b' ] )
+
+# will return the file node list
+# [ '/bin/executable_a', '/bin/executable_b' ]
+FindInstalledFiles()
+
+Install( '/lib', [ 'some_library' ] )
+
+# will return the file node list
+# [ '/bin/executable_a', '/bin/executable_b', '/lib/some_library' ]
+FindInstalledFiles()
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI FindSourceFiles( node = '"."' )
+.TP
+.RI env.FindSourceFiles( node = '"."' )
+
+Returns the list of nodes which serve as the source of the built files.
+It does so by inspecting the dependency tree starting at the optional
+argument
+.B node
+which defaults to the '"."'-node. It will then return all leaves of
+.B node.
+These are all children which have no further children.
+
+This function is a convenient method to select the contents of a Source
+Package.
+
+Example:
+
+.ES
+Program( 'src/main_a.c' )
+Program( 'src/main_b.c' )
+Program( 'main_c.c' )
+
+# returns ['main_c.c', 'src/main_a.c', 'SConstruct', 'src/main_b.c']
+FindSourceFiles()
+
+# returns ['src/main_b.c', 'src/main_a.c' ]
+FindSourceFiles( 'src' )
+.EE
+
+.IP
+As you can see build support files (SConstruct in the above example)
+will also be returned by this function.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI FindPathDirs( variable )
+Returns a function
+(actually a callable Python object)
+intended to be used as the
+.B path_function
+of a Scanner object.
+The returned object will look up the specified
+.I variable
+in a construction environment
+and treat the construction variable's value as a list of
+directory paths that should be searched
+(like
+.BR CPPPATH ,
+.BR LIBPATH ,
+etc.).
+
+Note that use of
+.BR FindPathDirs ()
+is generally preferable to
+writing your own
+.B path_function
+for the following reasons:
+1) The returned list will contain all appropriate directories
+found in source trees
+(when
+.BR VariantDir ()
+is used)
+or in code repositories
+(when
+.BR Repository ()
+or the
+.B \-Y
+option are used).
+2) scons will identify expansions of
+.I variable
+that evaluate to the same list of directories as,
+in fact, the same list,
+and avoid re-scanning the directories for files,
+when possible.
+
+Example:
+
+.ES
+def my_scan(node, env, path, arg):
+ # Code to scan file contents goes here...
+ return include_files
+
+scanner = Scanner(name = 'myscanner',
+ function = my_scan,
+ path_function = FindPathDirs('MYPATH'))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Flatten( sequence )
+.TP
+.RI env.Flatten( sequence )
+Takes a sequence (that is, a Python list or tuple)
+that may contain nested sequences
+and returns a flattened list containing
+all of the individual elements in any sequence.
+This can be helpful for collecting
+the lists returned by calls to Builders;
+other Builders will automatically
+flatten lists specified as input,
+but direct Python manipulation of
+these lists does not.
+
+Examples:
+
+.ES
+foo = Object('foo.c')
+bar = Object('bar.c')
+
+# Because `foo' and `bar' are lists returned by the Object() Builder,
+# `objects' will be a list containing nested lists:
+objects = ['f1.o', foo, 'f2.o', bar, 'f3.o']
+
+# Passing such a list to another Builder is all right because
+# the Builder will flatten the list automatically:
+Program(source = objects)
+
+# If you need to manipulate the list directly using Python, you need to
+# call Flatten() yourself, or otherwise handle nested lists:
+for object in Flatten(objects):
+ print str(object)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI GetBuildFailures()
+Returns a list of exceptions for the
+actions that failed while
+attempting to build targets.
+Each element in the returned list is a
+.B BuildError
+object
+with the following attributes
+that record various aspects
+of the build failure:
+
+.B .node
+The node that was being built
+when the build failure occurred.
+
+.B .status
+The numeric exit status
+returned by the command or Python function
+that failed when trying to build the
+specified Node.
+
+.B .errstr
+The SCons error string
+describing the build failure.
+(This is often a generic
+message like "Error 2"
+to indicate that an executed
+command exited with a status of 2.)
+
+.B .filename
+The name of the file or
+directory that actually caused the failure.
+This may be different from the
+.B .node
+attribute.
+For example,
+if an attempt to build a target named
+.B sub/dir/target
+fails because the
+.B sub/dir
+directory could not be created,
+then the
+.B .node
+attribute will be
+.B sub/dir/target
+but the
+.B .filename
+attribute will be
+.BR sub/dir .
+
+.B .executor
+The SCons Executor object
+for the target Node
+being built.
+This can be used to retrieve
+the construction environment used
+for the failed action.
+
+.B .action
+The actual SCons Action object that failed.
+This will be one specific action
+out of the possible list of
+actions that would have been
+executed to build the target.
+
+.B .command
+The actual expanded command that was executed and failed,
+after expansion of
+.BR $TARGET ,
+.BR $SOURCE ,
+and other construction variables.
+
+Note that the
+.BR GetBuildFailures ()
+function
+will always return an empty list
+until any build failure has occurred,
+which means that
+.BR GetBuildFailures ()
+will always return an empty list
+while the
+.B SConscript
+files are being read.
+Its primary intended use is
+for functions that will be
+executed before SCons exits
+by passing them to the
+standard Python
+.BR atexit.register ()
+function.
+Example:
+
+.ES
+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)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI GetBuildPath( file ", [" ... ])
+.TP
+.RI env.GetBuildPath( file ", [" ... ])
+Returns the
+.B scons
+path name (or names) for the specified
+.I file
+(or files).
+The specified
+.I file
+or files
+may be
+.B scons
+Nodes or strings representing path names.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI GetLaunchDir()
+.TP
+.RI env.GetLaunchDir()
+Returns the absolute path name of the directory from which
+.B scons
+was initially invoked.
+This can be useful when using the
+.BR \-u ,
+.BR \-U
+or
+.BR \-D
+options, which internally
+change to the directory in which the
+.B SConstruct
+file is found.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI GetOption( name )
+.TP
+.RI env.GetOption( name )
+This function provides a way to query the value of
+SCons options set on scons command line
+(or set using the
+.IR SetOption ()
+function).
+The options supported are:
+
+.RS 10
+.TP 6
+.B cache_debug
+which corresponds to --cache-debug;
+.TP 6
+.B cache_disable
+which corresponds to --cache-disable;
+.TP 6
+.B cache_force
+which corresponds to --cache-force;
+.TP 6
+.B cache_show
+which corresponds to --cache-show;
+.TP 6
+.B clean
+which corresponds to -c, --clean and --remove;
+.TP 6
+.B config
+which corresponds to --config;
+.TP 6
+.B directory
+which corresponds to -C and --directory;
+.TP 6
+.B diskcheck
+which corresponds to --diskcheck
+.TP 6
+.B duplicate
+which corresponds to --duplicate;
+.TP 6
+.B file
+which corresponds to -f, --file, --makefile and --sconstruct;
+.TP 6
+.B help
+which corresponds to -h and --help;
+.TP 6
+.B ignore_errors
+which corresponds to --ignore-errors;
+.TP 6
+.B implicit_cache
+which corresponds to --implicit-cache;
+.TP 6
+.B implicit_deps_changed
+which corresponds to --implicit-deps-changed;
+.TP 6
+.B implicit_deps_unchanged
+which corresponds to --implicit-deps-unchanged;
+.TP 6
+.B interactive
+which corresponds to --interact and --interactive;
+.TP 6
+.B keep_going
+which corresponds to -k and --keep-going;
+.TP 6
+.B max_drift
+which corresponds to --max-drift;
+.TP 6
+.B no_exec
+which corresponds to -n, --no-exec, --just-print, --dry-run and --recon;
+.TP 6
+.B no_site_dir
+which corresponds to --no-site-dir;
+.TP 6
+.B num_jobs
+which corresponds to -j and --jobs;
+.TP 6
+.B profile_file
+which corresponds to --profile;
+.TP 6
+.B question
+which corresponds to -q and --question;
+.TP 6
+.B random
+which corresponds to --random;
+.TP 6
+.B repository
+which corresponds to -Y, --repository and --srcdir;
+.TP 6
+.B silent
+which corresponds to -s, --silent and --quiet;
+.TP 6
+.B site_dir
+which corresponds to --site-dir;
+.TP 6
+.B stack_size
+which corresponds to --stack-size;
+.TP 6
+.B taskmastertrace_file
+which corresponds to --taskmastertrace; and
+.TP 6
+.B warn
+which corresponds to --warn and --warning.
+.RE
+
+.IP
+See the documentation for the
+corresponding command line object for information about each specific
+option.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Glob( pattern ", [" ondisk ", " source ", " strings ])
+.TP
+.RI env.Glob( pattern ", [" ondisk ", " source ", " strings ])
+Returns Nodes (or strings) that match the specified
+.IR pattern ,
+relative to the directory of the current
+.B SConscript
+file.
+The
+.BR env.Glob ()
+form performs string substition on
+.I pattern
+and returns whatever matches
+the resulting expanded pattern.
+
+The specified
+.I pattern
+uses Unix shell style metacharacters for matching:
+
+.ES
+ * matches everything
+ ? matches any single character
+ [seq] matches any character in seq
+ [!seq] matches any char not in seq
+.EE
+
+.IP
+If the first character of a filename is a dot,
+it must be matched explicitly.
+Character matches do
+.I not
+span directory separators.
+
+The
+.BR Glob ()
+knows about
+repositories
+(see the
+.BR Repository ()
+function)
+and source directories
+(see the
+.BR VariantDir ()
+function)
+and
+returns a Node (or string, if so configured)
+in the local (SConscript) directory
+if matching Node is found
+anywhere in a corresponding
+repository or source directory.
+
+The
+.B ondisk
+argument may be set to
+.B False
+(or any other non-true value)
+to disable the search for matches on disk,
+thereby only returning matches among
+already-configured File or Dir Nodes.
+The default behavior is to
+return corresponding Nodes
+for any on-disk matches found.
+
+The
+.B source
+argument may be set to
+.B True
+(or any equivalent value)
+to specify that,
+when the local directory is a
+.BR VariantDir (),
+the returned Nodes should be from the
+corresponding source directory,
+not the local directory.
+
+The
+.B strings
+argument may be set to
+.B True
+(or any equivalent value)
+to have the
+.BR Glob ()
+function return strings, not Nodes,
+that represent the matched files or directories.
+The returned strings will be relative to
+the local (SConscript) directory.
+(Note that This may make it easier to perform
+arbitrary manipulation of file names,
+but if the returned strings are
+passed to a different
+.B SConscript
+file,
+any Node translation will be relative
+to the other
+.B SConscript
+directory,
+not the original
+.B SConscript
+directory.)
+
+Examples:
+
+.ES
+Program('foo', Glob('*.c'))
+Zip('/tmp/everything', Glob('.??*') + Glob('*'))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+'\".TP
+'\".RI GlobalBuilders( flag )
+'\"When
+'\".B flag
+'\"is non-zero,
+'\"adds the names of the default builders
+'\"(Program, Library, etc.)
+'\"to the global name space
+'\"so they can be called without an explicit construction environment.
+'\"(This is the default.)
+'\"When
+'\".B
+'\"flag is zero,
+'\"the names of the default builders are removed
+'\"from the global name space
+'\"so that an explicit construction environment is required
+'\"to call all builders.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Help( text )
+.TP
+.RI env.Help( text )
+This specifies help text to be printed if the
+.B -h
+argument is given to
+.BR scons .
+If
+.BR Help
+is called multiple times, the text is appended together in the order
+that
+.BR Help
+is called.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Ignore( target ", " dependency )
+.TP
+.RI env.Ignore( target ", " dependency )
+The specified dependency file(s)
+will be ignored when deciding if
+the target file(s) need to be rebuilt.
+
+You can also use
+.BR Ignore()
+to remove a target from the default build.
+In order to do this you must specify the directory the target will
+be built in as the target, and the file you want to skip building
+as the dependency.
+
+Note that this will only remove the dependencies listed from
+the files built by default. It will still be built if that
+dependency is needed by another object being built.
+See the third and forth examples below.
+
+Examples:
+
+.ES
+env.Ignore('foo', 'foo.c')
+env.Ignore('bar', ['bar1.h', 'bar2.h'])
+env.Ignore('.','foobar.obj')
+env.Ignore('bar','bar/foobar.obj')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Import( vars )
+.TP
+.RI env.Import( vars )
+This tells
+.B scons
+to import a list of variables into the current SConscript file. This
+will import variables that were exported with
+.BR Export ()
+or in the
+.I exports
+argument to
+.BR SConscript ().
+Variables exported by
+.BR SConscript ()
+have precedence.
+Multiple variable names can be passed to
+.BR Import ()
+as separate arguments or as a list. The variable "*" can be used
+to import all variables.
+
+Examples:
+
+.ES
+Import("env")
+Import("env", "variable")
+Import(["env", "variable"])
+Import("*")
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Literal( string )
+.TP
+.RI env.Literal( string )
+The specified
+.I string
+will be preserved as-is
+and not have construction variables expanded.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Local( targets )
+.TP
+.RI env.Local( targets )
+The specified
+.I targets
+will have copies made in the local tree,
+even if an already up-to-date copy
+exists in a repository.
+Returns a list of the target Node or Nodes.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+\" .TP
+\" .RI env.MergeShellPaths( arg ", [" prepend ])
+\" Merges the elements of the specified
+\" .IR arg ,
+\" which must be a dictionary, to the construction
+\" environment's copy of the shell environment
+\" in env['ENV'].
+\" (This is the environment which is passed
+\" to subshells spawned by SCons.)
+\" Note that
+\" .I arg
+\" must be a single value,
+\" so multiple strings must
+\" be passed in as a list,
+\" not as separate arguments to
+\" .BR env.MergeShellPaths ().
+
+\" New values are prepended to the environment variable by default,
+\" unless prepend=0 is specified.
+\" Duplicate values are always eliminated,
+\" since this function calls
+\" .B AppendENVPath
+\" or
+\" .B PrependENVPath
+\" depending on the
+\" .I prepend
+\" argument. See those functions for more details.
+
+\" Examples:
+
+\" .ES
+\" # Prepend a path to the shell PATH.
+\" env.MergeShellPaths({'PATH':'/usr/local/bin'} )
+\" # Append two dirs to the shell INCLUDE.
+\" env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']}, prepend=0 )
+
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.MergeFlags( arg ", [" unique ])
+Merges the specified
+.I arg
+values to the construction environment's construction variables.
+If the
+.I arg
+argument is not a dictionary,
+it is converted to one by calling
+.B env.ParseFlags()
+on the argument
+before the values are merged.
+Note that
+.I arg
+must be a single value,
+so multiple strings must
+be passed in as a list,
+not as separate arguments to
+.BR env.MergeFlags ().
+
+By default,
+duplicate values are eliminated;
+you can, however, specify
+.B unique=0
+to allow duplicate
+values to be added.
+When eliminating duplicate values,
+any construction variables that end with
+the string
+.B PATH
+keep the left-most unique value.
+All other construction variables keep
+the right-most unique value.
+
+Examples:
+
+.ES
+# Add an optimization flag to $CCFLAGS.
+env.MergeFlags('-O3')
+
+# Combine the flags returned from running pkg-config with an optimization
+# flag and merge the result into the construction variables.
+env.MergeFlags(['!pkg-config gtk+-2.0 --cflags', '-O3'])
+
+# Combine an optimization flag with the flags returned from running pkg-config
+# twice and merge the result into the construction variables.
+env.MergeFlags(['-O3',
+ '!pkg-config gtk+-2.0 --cflags --libs',
+ '!pkg-config libpng12 --cflags --libs'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI NoCache( target ", ...)"
+.TP
+.RI env.NoCache( target ", ...)"
+Specifies a list of files which should
+.I not
+be cached whenever the
+.BR CacheDir ()
+method has been activated.
+The specified targets may be a list
+or an individual target.
+
+Multiple files should be specified
+either as separate arguments to the
+.BR NoCache ()
+method, or as a list.
+.BR NoCache ()
+will also accept the return value of any of the construction environment
+Builder methods.
+
+Calling
+.BR NoCache ()
+on directories and other non-File Node types has no effect because
+only File Nodes are cached.
+
+Examples:
+
+.ES
+NoCache('foo.elf')
+NoCache(env.Program('hello', 'hello.c'))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI NoClean( target ", ...)"
+.TP
+.RI env.NoClean( target ", ...)"
+Specifies a list of files or directories which should
+.I not
+be removed whenever the targets (or their dependencies)
+are specified with the
+.B -c
+command line option.
+The specified targets may be a list
+or an individual target.
+Multiple calls to
+.BR NoClean ()
+are legal,
+and prevent each specified target
+from being removed by calls to the
+.B -c
+option.
+
+Multiple files or directories should be specified
+either as separate arguments to the
+.BR NoClean ()
+method, or as a list.
+.BR NoClean ()
+will also accept the return value of any of the construction environment
+Builder methods.
+
+Calling
+.BR NoClean ()
+for a target overrides calling
+.BR Clean ()
+for the same target,
+and any targets passed to both functions will
+.I not
+be removed by the
+.B -c
+option.
+
+Examples:
+
+.ES
+NoClean('foo.elf')
+NoClean(env.Program('hello', 'hello.c'))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.ParseConfig( command ", [" function ", " unique ])
+Calls the specified
+.I function
+to modify the environment as specified by the output of
+.I command .
+The default
+.I function
+is
+.BR env.MergeFlags (),
+which expects the output of a typical
+.I *-config command
+(for example,
+.BR gtk-config )
+and adds the options
+to the appropriate construction variables.
+By default,
+duplicate values are not
+added to any construction variables;
+you can specify
+.B unique=0
+to allow duplicate
+values to be added.
+
+Interpreted options
+and the construction variables they affect
+are as specified for the
+.BR env.ParseFlags ()
+method (which this method calls).
+See that method's description, below,
+for a table of options and construction variables.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI ParseDepends( filename ", [" must_exist ", " only_one ])
+.TP
+.RI env.ParseDepends( filename ", [" must_exist ", " only_one ])
+Parses the contents of the specified
+.I filename
+as a list of dependencies in the style of
+.BR Make
+or
+.BR mkdep ,
+and explicitly establishes all of the listed dependencies.
+
+By default,
+it is not an error
+if the specified
+.I filename
+does not exist.
+The optional
+.I must_exist
+argument may be set to a non-zero
+value to have
+scons
+throw an exception and
+generate an error if the file does not exist,
+or is otherwise inaccessible.
+
+The optional
+.I only_one
+argument may be set to a non-zero
+value to have
+scons
+thrown an exception and
+generate an error
+if the file contains dependency
+information for more than one target.
+This can provide a small sanity check
+for files intended to be generated
+by, for example, the
+.B gcc -M
+flag,
+which should typically only
+write dependency information for
+one output file into a corresponding
+.B .d
+file.
+
+The
+.I filename
+and all of the files listed therein
+will be interpreted relative to
+the directory of the
+.I SConscript
+file which calls the
+.B ParseDepends
+function.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.ParseFlags( flags ", ...)"
+Parses one or more strings containing
+typical command-line flags for GCC tool chains
+and returns a dictionary with the flag values
+separated into the appropriate SCons construction variables.
+This is intended as a companion to the
+.BR env.MergeFlags ()
+method, but allows for the values in the returned dictionary
+to be modified, if necessary,
+before merging them into the construction environment.
+(Note that
+.BR env.MergeFlags ()
+will call this method if its argument is not a dictionary,
+so it is usually not necessary to call
+.BR env.ParseFlags ()
+directly unless you want to manipulate the values.)
+
+If the first character in any string is
+an exclamation mark (!),
+the rest of the string is executed as a command,
+and the output from the command is
+parsed as GCC tool chain command-line flags
+and added to the resulting dictionary.
+
+Flag values are translated accordig to the prefix found,
+and added to the following construction variables:
+
+.ES
+-arch CCFLAGS, LINKFLAGS
+-D CPPDEFINES
+-framework FRAMEWORKS
+-frameworkdir= FRAMEWORKPATH
+-include CCFLAGS
+-isysroot CCFLAGS, LINKFLAGS
+-I CPPPATH
+-l LIBS
+-L LIBPATH
+-mno-cygwin CCFLAGS, LINKFLAGS
+-mwindows LINKFLAGS
+-pthread CCFLAGS, LINKFLAGS
+-std= CFLAGS
+-Wa, ASFLAGS, CCFLAGS
+-Wl,-rpath= RPATH
+-Wl,-R, RPATH
+-Wl,-R RPATH
+-Wl, LINKFLAGS
+-Wp, CPPFLAGS
+- CCFLAGS
++ CCFLAGS, LINKFLAGS
+.EE
+
+.IP
+Any other strings not associated with options
+are assumed to be the names of libraries
+and added to the
+.B LIBS
+construction variable.
+
+Examples (all of which produce the same result):
+
+.ES
+dict = env.ParseFlags('-O2 -Dfoo -Dbar=1')
+dict = env.ParseFlags('-O2', '-Dfoo', '-Dbar=1')
+dict = env.ParseFlags(['-O2', '-Dfoo -Dbar=1'])
+dict = env.ParseFlags('-O2', '!echo -Dfoo -Dbar=1')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+env.Perforce()
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from the Perforce source code management system.
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+Example:
+
+.ES
+env.SourceCode('.', env.Perforce())
+.EE
+.IP
+Perforce uses a number of external
+environment variables for its operation.
+Consequently, this function adds the
+following variables from the user's external environment
+to the construction environment's
+ENV dictionary:
+P4CHARSET,
+P4CLIENT,
+P4LANGUAGE,
+P4PASSWD,
+P4PORT,
+P4USER,
+SystemRoot,
+USER,
+and
+USERNAME.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Platform( string )
+Returns a callable object
+that can be used to initialize
+a construction environment using the
+platform keyword of the Environment() method.
+
+Example:
+
+.ES
+env = Environment(platform = Platform('win32'))
+.EE
+.TP
+.RI env.Platform( string )
+Applies the callable object for the specified platform
+.I string
+to the environment through which the method was called.
+
+.ES
+env.Platform('posix')
+.EE
+.IP
+Note that the
+.B win32
+platform adds the
+.B SystemDrive
+and
+.B SystemRoot
+variables from the user's external environment
+to the construction environment's
+.B ENV
+dictionary.
+This is so that any executed commands
+that use sockets to connect with other systems
+(such as fetching source files from
+external CVS repository specifications like
+.BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons )
+will work on Windows systems.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Progress( callable ", [" interval ])
+.TP
+.RI Progress( string ", [" interval ", " file ", " overwrite ])
+.TP
+.RI Progress( list_of_strings ", [" interval ", " file ", " overwrite ])
+Allows SCons to show progress made during the build
+by displaying a string or calling a function while
+evaluating Nodes (e.g. files).
+
+If the first specified argument is a Python callable
+(a function or an object that has a
+.BR __call__ ()
+method),
+the function will be called
+once every
+.I interval
+times a Node is evaluated.
+The callable will be passed the evaluated Node
+as its only argument.
+(For future compatibility,
+it's a good idea to also add
+.B *args
+and
+.B **kw
+as arguments to your function or method.
+This will prevent the code from breaking
+if SCons ever changes the interface
+to call the function with additional arguments in the future.)
+
+An example of a simple custom progress function
+that prints a string containing the Node name
+every 10 Nodes:
+
+.ES
+def my_progress_function(node, *args, **kw):
+ print 'Evaluating node %s!' % node
+Progress(my_progress_function, interval=10)
+.EE
+.IP
+A more complicated example of a custom progress display object
+that prints a string containing a count
+every 100 evaluated Nodes.
+Note the use of
+.B \\\\r
+(a carriage return)
+at the end so that the string
+will overwrite itself on a display:
+
+.ES
+import sys
+class ProgressCounter:
+ count = 0
+ def __call__(self, node, *args, **kw):
+ self.count += 100
+ sys.stderr.write('Evaluated %s nodes\\r' % self.count)
+Progress(ProgressCounter(), interval=100)
+.EE
+.IP
+If the first argument
+.BR Progress ()
+is a string,
+the string will be displayed
+every
+.I interval
+evaluated Nodes.
+The default is to print the string on standard output;
+an alternate output stream
+may be specified with the
+.B file=
+argument.
+The following will print a series of dots
+on the error output,
+one dot for every 100 evaluated Nodes:
+
+.ES
+import sys
+Progress('.', interval=100, file=sys.stderr)
+.EE
+.IP
+If the string contains the verbatim substring
+.B $TARGET,
+it will be replaced with the Node.
+Note that, for performance reasons, this is
+.I not
+a regular SCons variable substition,
+so you can not use other variables
+or use curly braces.
+The following example will print the name of
+every evaluated Node,
+using a
+.B \\\\r
+(carriage return) to cause each line to overwritten by the next line,
+and the
+.B overwrite=
+keyword argument to make sure the previously-printed
+file name is overwritten with blank spaces:
+
+.ES
+import sys
+Progress('$TARGET\\r', overwrite=True)
+.EE
+.IP
+If the first argument to
+.BR Progress ()
+is a list of strings,
+then each string in the list will be displayed
+in rotating fashion every
+.I interval
+evaluated Nodes.
+This can be used to implement a "spinner"
+on the user's screen as follows:
+
+.ES
+Progress(['-\\r', '\\\\\\r', '|\\r', '/\\r'], interval=5)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Precious( target ", ...)"
+.TP
+.RI env.Precious( target ", ...)"
+Marks each given
+.I target
+as precious so it is not deleted before it is rebuilt. Normally
+.B scons
+deletes a target before building it.
+Multiple targets can be passed in to a single call to
+.BR Precious ().
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Prepend( key = val ", [...])"
+Appends the specified keyword arguments
+to the beginning of construction variables in the environment.
+If the Environment does not have
+the specified construction variable,
+it is simply added to the environment.
+If the values of the construction variable
+and the keyword argument are the same type,
+then the two values will be simply added together.
+Otherwise, the construction variable
+and the value of the keyword argument
+are both coerced to lists,
+and the lists are added together.
+(See also the Append method, above.)
+
+Example:
+
+.ES
+env.Prepend(CCFLAGS = '-g ', FOO = ['foo.yyy'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.PrependENVPath( name ", " newpath ", [" envname ", " sep ", " delete_existing ])
+This appends new path elements to the given path in the
+specified external environment
+.RB ( ENV
+by default).
+This will only add
+any particular path once (leaving the first one it encounters and
+ignoring the rest, to preserve path order),
+and to help assure this,
+will normalize all paths (using
+.B os.path.normpath
+and
+.BR os.path.normcase ).
+This can also handle the
+case where the given old path variable is a list instead of a
+string, in which case a list will be returned instead of a string.
+
+If
+.I delete_existing
+is 0, then adding a path that already exists
+will not move it to the beginning;
+it will stay where it is in the list.
+
+Example:
+
+.ES
+print 'before:',env['ENV']['INCLUDE']
+include_path = '/foo/bar:/foo'
+env.PrependENVPath('INCLUDE', include_path)
+print 'after:',env['ENV']['INCLUDE']
+.EE
+
+The above exmaple will print:
+
+.ES
+before: /biz:/foo
+after: /foo/bar:/foo:/biz
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.PrependUnique( key = val ", delete_existing=0, [...])"
+Appends the specified keyword arguments
+to the beginning of construction variables in the environment.
+If the Environment does not have
+the specified construction variable,
+it is simply added to the environment.
+If the construction variable being appended to is a list,
+then any value(s) that already exist in the
+construction variable will
+.I not
+be added again to the list.
+However, if delete_existing is 1,
+existing matching values are removed first, so
+existing values in the arg list move to the front of the list.
+
+Example:
+
+.ES
+env.PrependUnique(CCFLAGS = '-g', FOO = ['foo.yyy'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+env.RCS()
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from RCS.
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function:
+
+Examples:
+
+.ES
+env.SourceCode('.', env.RCS())
+.EE
+.IP
+Note that
+.B scons
+will fetch source files
+from RCS subdirectories automatically,
+so configuring RCS
+as demonstrated in the above example
+should only be necessary if
+you are fetching from
+RCS,v
+files in the same
+directory as the source files,
+or if you need to explicitly specify RCS
+for a specific subdirectory.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.Replace( key = val ", [...])"
+Replaces construction variables in the Environment
+with the specified keyword arguments.
+
+Example:
+
+.ES
+env.Replace(CCFLAGS = '-g', FOO = 'foo.xxx')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Repository( directory )
+.TP
+.RI env.Repository( directory )
+Specifies that
+.I directory
+is a repository to be searched for files.
+Multiple calls to
+.BR Repository ()
+are legal,
+and each one adds to the list of
+repositories that will be searched.
+
+To
+.BR scons ,
+a repository is a copy of the source tree,
+from the top-level directory on down,
+which may contain
+both source files and derived files
+that can be used to build targets in
+the local source tree.
+The canonical example would be an
+official source tree maintained by an integrator.
+If the repository contains derived files,
+then the derived files should have been built using
+.BR scons ,
+so that the repository contains the necessary
+signature information to allow
+.B scons
+to figure out when it is appropriate to
+use the repository copy of a derived file,
+instead of building one locally.
+
+Note that if an up-to-date derived file
+already exists in a repository,
+.B scons
+will
+.I not
+make a copy in the local directory tree.
+In order to guarantee that a local copy
+will be made,
+use the
+.B Local()
+method.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Requires( target ", " prerequisite )
+.TP
+.RI env.Requires( target ", " prerequisite )
+Specifies an order-only relationship
+between the specified target file(s)
+and the specified prerequisite file(s).
+The prerequisite file(s)
+will be (re)built, if necessary,
+.I before
+the target file(s),
+but the target file(s) do not actually
+depend on the prerequisites
+and will not be rebuilt simply because
+the prerequisite file(s) change.
+
+Example:
+
+.ES
+env.Requires('foo', 'file-that-must-be-built-before-foo')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Return([ vars "... , " stop= ])
+By default,
+this stops processing the current SConscript
+file and returns to the calling SConscript file
+the values of the variables named in the
+.I vars
+string arguments.
+Multiple strings contaning variable names may be passed to
+.BR Return ().
+Any strings that contain white space
+
+The optional
+.B stop=
+keyword argument may be set to a false value
+to continue processing the rest of the SConscript
+file after the
+.BR Return ()
+call.
+This was the default behavior prior to SCons 0.98.
+However, the values returned
+are still the values of the variables in the named
+.I vars
+at the point
+.BR Return ()
+is called.
+
+Examples:
+
+.ES
+# Returns without returning a value.
+Return()
+
+# Returns the value of the 'foo' Python variable.
+Return("foo")
+
+# Returns the values of the Python variables 'foo' and 'bar'.
+Return("foo", "bar")
+
+# Returns the values of Python variables 'val1' and 'val2'.
+Return('val1 val2')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Scanner( function ", [" argument ", " keys ", " path_function ", " node_class ", " node_factory ", " scan_check ", " recursive ])
+.TP
+.RI env.Scanner( function ", [" argument ", " keys ", " path_function ", " node_class ", " node_factory ", " scan_check ", " recursive ])
+Creates a Scanner object for
+the specified
+.IR function .
+See the section "Scanner Objects,"
+below, for a complete explanation of the arguments and behavior.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+env.SCCS()
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from SCCS.
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+Example:
+
+.ES
+env.SourceCode('.', env.SCCS())
+.EE
+.IP
+Note that
+.B scons
+will fetch source files
+from SCCS subdirectories automatically,
+so configuring SCCS
+as demonstrated in the above example
+should only be necessary if
+you are fetching from
+.I s.SCCS
+files in the same
+directory as the source files,
+or if you need to explicitly specify SCCS
+for a specific subdirectory.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SConscript( scripts ", [" exports ", " variant_dir ", " duplicate ])
+'\" .RI SConscript( scripts ", [" exports ", " variant_dir ", " src_dir ", " duplicate ])
+.TP
+.RI env.SConscript( scripts ", [" exports ", " variant_dir ", " duplicate ])
+'\" .RI env.SConscript( scripts ", [" exports ", " variant_dir ", " src_dir ", " duplicate ])
+.TP
+.RI SConscript(dirs= subdirs ", [name=" script ", " exports ", " variant_dir ", " duplicate ])
+'\" .RI SConscript(dirs= subdirs ", [name=" script ", " exports ", " variant_dir ", " src_dir ", " duplicate ])
+.TP
+.RI env.SConscript(dirs= subdirs ", [name=" script ", " exports ", " variant_dir ", " duplicate ])
+'\" .RI env.SConscript(dirs= subdirs ", [name=" script ", " exports ", " variant_dir ", " src_dir ", " duplicate ])
+This tells
+.B scons
+to execute
+one or more subsidiary SConscript (configuration) files.
+Any variables returned by a called script using
+.BR Return ()
+will be returned by the call to
+.BR SConscript ().
+There are two ways to call the
+.BR SConscript ()
+function.
+
+The first way you can call
+.BR SConscript ()
+is to explicitly specify one or more
+.I scripts
+as the first argument.
+A single script may be specified as a string;
+multiple scripts must be specified as a list
+(either explicitly or as created by
+a function like
+.BR Split ()).
+Examples:
+.ES
+SConscript('SConscript') # run SConscript in the current directory
+SConscript('src/SConscript') # run SConscript in the src directory
+SConscript(['src/SConscript', 'doc/SConscript'])
+config = SConscript('MyConfig.py')
+.EE
+
+The second way you can call
+.BR SConscript ()
+is to specify a list of (sub)directory names
+as a
+.RI dirs= subdirs
+keyword argument.
+In this case,
+.B scons
+will, by default,
+execute a subsidiary configuration file named
+.B SConscript
+in each of the specified directories.
+You may specify a name other than
+.B SConscript
+by supplying an optional
+.RI name= script
+keyword argument.
+The first three examples below have the same effect
+as the first three examples above:
+.ES
+SConscript(dirs='.') # run SConscript in the current directory
+SConscript(dirs='src') # run SConscript in the src directory
+SConscript(dirs=['src', 'doc'])
+SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
+.EE
+
+The optional
+.I exports
+argument provides a list of variable names or a dictionary of
+named values to export to the
+.IR script(s) .
+These variables are locally exported only to the specified
+.IR script(s) ,
+and do not affect the global pool of variables used by the
+.BR Export ()
+function.
+'\"If multiple dirs are provided, each script gets a fresh export.
+The subsidiary
+.I script(s)
+must use the
+.BR Import ()
+function to import the variables.
+Examples:
+.ES
+foo = SConscript('sub/SConscript', exports='env')
+SConscript('dir/SConscript', exports=['env', 'variable'])
+SConscript(dirs='subdir', exports='env variable')
+SConscript(dirs=['one', 'two', 'three'], exports='shared_info')
+.EE
+
+If the optional
+.I variant_dir
+argument is present, it causes an effect equivalent to the
+.BR VariantDir ()
+method described below.
+(If
+.I variant_dir
+is not present, the
+'\" .IR src_dir and
+.I duplicate
+'\" arguments are ignored.)
+argument is ignored.)
+The
+.I variant_dir
+'\" and
+'\" .I src_dir
+'\" arguments are interpreted relative to the directory of the calling
+argument is interpreted relative to the directory of the calling
+.BR SConscript file.
+See the description of the
+.BR VariantDir ()
+function below for additional details and restrictions.
+
+If
+'\" .IR variant_dir " is present, but"
+'\" .IR src_dir " is not,"
+.IR variant_dir " is present,"
+the source directory is relative to the called
+.BR SConscript " file."
+.ES
+SConscript('src/SConscript', variant_dir = 'build')
+.EE
+is equivalent to
+.ES
+VariantDir('build', 'src')
+SConscript('build/SConscript')
+.EE
+This later paradigm is often used when the sources are
+in the same directory as the
+.BR SConstruct file:
+.ES
+SConscript('SConscript', variant_dir = 'build')
+.EE
+is equivalent to
+.ES
+VariantDir('build', '.')
+SConscript('build/SConscript')
+.EE
+
+'\" If
+'\" .IR variant_dir " and"
+'\" .IR src_dir " are both present,"
+'\" xxxxx everything is in a state of confusion.
+'\" .ES
+'\" SConscript(dirs = 'src', variant_dir = 'build', src_dir = '.')
+'\" runs src/SConscript in build/src, but
+'\" SConscript(dirs = 'lib', variant_dir = 'build', src_dir = 'src')
+'\" runs lib/SConscript (in lib!). However,
+'\" SConscript(dirs = 'src', variant_dir = 'build', src_dir = 'src')
+'\" runs src/SConscript in build. Moreover,
+'\" SConscript(dirs = 'src/lib', variant_dir = 'build', src_dir = 'src')
+'\" runs src/lib/SConscript in build/lib. Moreover,
+'\" SConscript(dirs = 'build/src/lib', variant_dir = 'build', src_dir = 'src')
+'\" can't find build/src/lib/SConscript, even though it ought to exist.
+'\" .EE
+'\" is equivalent to
+'\" .ES
+'\" ????????????????
+'\" .EE
+'\" and what about this alternative?
+'\"TODO??? SConscript('build/SConscript', src_dir='src')
+
+Here are some composite examples:
+
+.ES
+# collect the configuration information and use it to build src and doc
+shared_info = SConscript('MyConfig.py')
+SConscript('src/SConscript', exports='shared_info')
+SConscript('doc/SConscript', exports='shared_info')
+.EE
+
+.ES
+# build debugging and production versions. SConscript
+# can use Dir('.').path to determine variant.
+SConscript('SConscript', variant_dir='debug', duplicate=0)
+SConscript('SConscript', variant_dir='prod', duplicate=0)
+.EE
+
+.ES
+# build debugging and production versions. SConscript
+# is passed flags to use.
+opts = { 'CPPDEFINES' : ['DEBUG'], 'CCFLAGS' : '-pgdb' }
+SConscript('SConscript', variant_dir='debug', duplicate=0, exports=opts)
+opts = { 'CPPDEFINES' : ['NODEBUG'], 'CCFLAGS' : '-O' }
+SConscript('SConscript', variant_dir='prod', duplicate=0, exports=opts)
+.EE
+
+.ES
+# build common documentation and compile for different architectures
+SConscript('doc/SConscript', variant_dir='build/doc', duplicate=0)
+SConscript('src/SConscript', variant_dir='build/x86', duplicate=0)
+SConscript('src/SConscript', variant_dir='build/ppc', duplicate=0)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SConscriptChdir( value )
+.TP
+.RI env.SConscriptChdir( value )
+By default,
+.B scons
+changes its working directory
+to the directory in which each
+subsidiary SConscript file lives.
+This behavior may be disabled
+by specifying either:
+
+.ES
+SConscriptChdir(0)
+env.SConscriptChdir(0)
+.EE
+.IP
+in which case
+.B scons
+will stay in the top-level directory
+while reading all SConscript files.
+(This may be necessary when building from repositories,
+when all the directories in which SConscript files may be found
+don't necessarily exist locally.)
+You may enable and disable
+this ability by calling
+SConscriptChdir()
+multiple times.
+
+Example:
+
+.ES
+env = Environment()
+SConscriptChdir(0)
+SConscript('foo/SConscript') # will not chdir to foo
+env.SConscriptChdir(1)
+SConscript('bar/SConscript') # will chdir to bar
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SConsignFile([ file , dbm_module ])
+.TP
+.RI env.SConsignFile([ file , dbm_module ])
+This tells
+.B scons
+to store all file signatures
+in the specified database
+.IR file .
+If the
+.I file
+name is omitted,
+.B .sconsign
+is used by default.
+(The actual file name(s) stored on disk
+may have an appropriated suffix appended
+by the
+.IR dbm_module .)
+If
+.I file
+is not an absolute path name,
+the file is placed in the same directory as the top-level
+.B SConstruct
+file.
+
+If
+.I file
+is
+.BR None ,
+then
+.B scons
+will store file signatures
+in a separate
+.B .sconsign
+file in each directory,
+not in one global database file.
+(This was the default behavior
+prior to SCons 0.96.91 and 0.97.)
+
+The optional
+.I dbm_module
+argument can be used to specify
+which Python database module
+The default is to use a custom
+.B SCons.dblite
+module that uses pickled
+Python data structures,
+and which works on all Python versions from 1.5.2 on.
+
+Examples:
+
+.ES
+# Explicitly stores signatures in ".sconsign.dblite"
+# in the top-level SConstruct directory (the
+# default behavior).
+SConsignFile()
+
+# Stores signatures in the file "etc/scons-signatures"
+# relative to the top-level SConstruct directory.
+SConsignFile("etc/scons-signatures")
+
+# Stores signatures in the specified absolute file name.
+SConsignFile("/home/me/SCons/signatures")
+
+# Stores signatures in a separate .sconsign file
+# in each directory.
+SConsignFile(None)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.SetDefault(key = val ", [...])"
+Sets construction variables to default values specified with the keyword
+arguments if (and only if) the variables are not already set.
+The following statements are equivalent:
+
+.ES
+env.SetDefault(FOO = 'foo')
+
+if not env.has_key('FOO'): env['FOO'] = 'foo'
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SetOption( name ", " value )
+.TP
+.RI env.SetOption( name ", " value )
+This function provides a way to set a select subset of the scons command
+line options from a SConscript file. The options supported are:
+
+.RS 10
+.TP 6
+.B clean
+which corresponds to -c, --clean and --remove;
+.TP 6
+.B duplicate
+which corresponds to --duplicate;
+.TP 6
+.B help
+which corresponds to -h and --help;
+.TP 6
+.B implicit_cache
+which corresponds to --implicit-cache;
+.TP 6
+.B max_drift
+which corresponds to --max-drift;
+.TP 6
+.B no_exec
+which corresponds to -n, --no-exec, --just-print, --dry-run and --recon;
+.TP 6
+.B num_jobs
+which corresponds to -j and --jobs;
+.TP 6
+.B random
+which corresponds to --random; and
+.TP 6
+.B stack_size
+which corresponds to --stack-size.
+.RE
+
+.IP
+See the documentation for the
+corresponding command line object for information about each specific
+option.
+
+Example:
+
+.ES
+SetOption('max_drift', 1)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SideEffect( side_effect ", " target )
+.TP
+.RI env.SideEffect( side_effect ", " target )
+Declares
+.I side_effect
+as a side effect of building
+.IR target .
+Both
+.I side_effect
+and
+.I target
+can be a list, a file name, or a node.
+A side effect is a target file that is created or updated
+as a side effect of building other targets.
+For example, a Windows PDB
+file is created as a side effect of building the .obj
+files for a static library,
+and various log files are created updated
+as side effects of various TeX commands.
+If a target is a side effect of multiple build commands,
+.B scons
+will ensure that only one set of commands
+is executed at a time.
+Consequently, you only need to use this method
+for side-effect targets that are built as a result of
+multiple build commands.
+
+Because multiple build commands may update
+the same side effect file,
+by default the
+.I side_effect
+target is
+.I not
+automatically removed
+when the
+.I target
+is removed by the
+.B -c
+option.
+(Note, however, that the
+.I side_effect
+might be removed as part of
+cleaning the directory in which it lives.)
+If you want to make sure the
+.I side_effect
+is cleaned whenever a specific
+.I target
+is cleaned,
+you must specify this explicitly
+with the
+.BR Clean ()
+or
+.BR env.Clean ()
+function.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SourceCode( entries ", " builder )
+.TP
+.RI env.SourceCode( entries ", " builder )
+Arrange for non-existent source files to
+be fetched from a source code management system
+using the specified
+.IR builder .
+The specified
+.I entries
+may be a Node, string or list of both,
+and may represent either individual
+source files or directories in which
+source files can be found.
+
+For any non-existent source files,
+.B scons
+will search up the directory tree
+and use the first
+.B SourceCode
+builder it finds.
+The specified
+.I builder
+may be
+.BR None ,
+in which case
+.B scons
+will not use a builder to fetch
+source files for the specified
+.IR entries ,
+even if a
+.B SourceCode
+builder has been specified
+for a directory higher up the tree.
+
+.B scons
+will, by default,
+fetch files from SCCS or RCS subdirectories
+without explicit configuration.
+This takes some extra processing time
+to search for the necessary
+source code management files on disk.
+You can avoid these extra searches
+and speed up your build a little
+by disabling these searches as follows:
+
+.ES
+env.SourceCode('.', None)
+.EE
+
+.IP
+Note that if the specified
+.I builder
+is one you create by hand,
+it must have an associated
+construction environment to use
+when fetching a source file.
+
+.B scons
+provides a set of canned factory
+functions that return appropriate
+Builders for various popular
+source code management systems.
+Canonical examples of invocation include:
+
+.ES
+env.SourceCode('.', env.BitKeeper('/usr/local/BKsources'))
+env.SourceCode('src', env.CVS('/usr/local/CVSROOT'))
+env.SourceCode('/', env.RCS())
+env.SourceCode(['f1.c', 'f2.c'], env.SCCS())
+env.SourceCode('no_source.c', None)
+.EE
+'\"env.SourceCode('.', env.Subversion('file:///usr/local/Subversion'))
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI env.subst( input ", [" raw ", " target ", " source ", " conv ])
+Performs construction variable interpolation
+on the specified string or sequence argument
+.IR input .
+
+By default,
+leading or trailing white space will
+be removed from the result.
+and all sequences of white space
+will be compressed to a single space character.
+Additionally, any
+.B $(
+and
+.B $)
+character sequences will be stripped from the returned string,
+The optional
+.I raw
+argument may be set to
+.B 1
+if you want to preserve white space and
+.BR $( - $)
+sequences.
+The
+.I raw
+argument may be set to
+.B 2
+if you want to strip
+all characters between
+any
+.B $(
+and
+.B $)
+pairs
+(as is done for signature calculation).
+
+If the input is a sequence
+(list or tuple),
+the individual elements of
+the sequence will be expanded,
+and the results will be returned as a list.
+
+The optional
+.I target
+and
+.I source
+keyword arguments
+must be set to lists of
+target and source nodes, respectively,
+if you want the
+.BR $TARGET ,
+.BR $TARGETS ,
+.BR $SOURCE
+and
+.BR $SOURCES
+to be available for expansion.
+This is usually necessary if you are
+calling
+.BR env.subst ()
+from within a Python function used
+as an SCons action.
+
+Returned string values or sequence elements
+are converted to their string representation by default.
+The optional
+.I conv
+argument
+may specify a conversion function
+that will be used in place of
+the default.
+For example, if you want Python objects
+(including SCons Nodes)
+to be returned as Python objects,
+you can use the Python
+.B lambda
+idiom to pass in an unnamed function
+that simply returns its unconverted argument.
+
+Example:
+
+.ES
+print env.subst("The C compiler is: $CC")
+
+def compile(target, source, env):
+ sourceDir = env.subst("${SOURCE.srcdir}",
+ target=target,
+ source=source)
+
+source_nodes = env.subst('$EXPAND_TO_NODELIST',
+ conv=lambda x: x)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+'\".TP
+'\".RI Subversion( repository ", " module )
+'\"A factory function that
+'\"returns a Builder object
+'\"to be used to fetch source files
+'\"from the specified Subversion
+'\".IR repository .
+'\"The returned Builder
+'\"is intended to be passed to the
+'\".B SourceCode
+'\"function.
+'\"
+'\"The optional specified
+'\".I module
+'\"will be added to the beginning
+'\"of all repository path names;
+'\"this can be used, in essence,
+'\"to strip initial directory names
+'\"from the repository path names,
+'\"so that you only have to
+'\"replicate part of the repository
+'\"directory hierarchy in your
+'\"local build directory.
+'\"
+'\"Example:
+'\"
+'\".ES
+'\"# Will fetch foo/bar/src.c
+'\"# from /usr/local/Subversion/foo/bar/src.c.
+'\"env.SourceCode('.', env.Subversion('file:///usr/local/Subversion'))
+'\"
+'\"# Will fetch bar/src.c
+'\"# from /usr/local/Subversion/foo/bar/src.c.
+'\"env.SourceCode('.', env.Subversion('file:///usr/local/Subversion', 'foo'))
+'\"
+'\"# Will fetch src.c
+'\"# from /usr/local/Subversion/foo/bar/src.c.
+'\"env.SourceCode('.', env.Subversion('file:///usr/local/Subversion', 'foo/bar'))
+'\".EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI SourceSignatures( type )
+.TP
+.RI env.SourceSignatures( type )
+Note: Although it is not yet officially deprecated,
+use of this function is discouraged.
+See the
+.BR Decider ()
+function for a more flexible and straightforward way
+to configure SCons' decision-making.
+
+The
+.BR SourceSignatures ()
+function tells
+.B scons
+how to decide if a source file
+(a file that is not built from any other files)
+has changed since the last time it
+was used to build a particular target file.
+Legal values are
+.B "MD5"
+or
+.BR "timestamp" .
+
+If the environment method is used,
+the specified type of source signature
+is only used when deciding whether targets
+built with that environment are up-to-date or must be rebuilt.
+If the global function is used,
+the specified type of source signature becomes the default
+used for all decisions
+about whether targets are up-to-date.
+
+.B "MD5"
+means
+.B scons
+decides that a source file has changed
+if the MD5 checksum of its contents has changed since
+the last time it was used to rebuild a particular target file.
+
+.B "timestamp"
+means
+.B scons
+decides that a source file has changed
+if its timestamp (modification time) has changed since
+the last time it was used to rebuild a particular target file.
+(Note that although this is similar to the behavior of Make,
+by default it will also rebuild if the dependency is
+.I older
+than the last time it was used to rebuild the target file.)
+
+There is no different between the two behaviors
+for Python
+.BR Value ()
+node objects.
+
+.B "MD5"
+signatures take longer to compute,
+but are more accurate than
+.B "timestamp"
+signatures.
+The default value is
+.BR "MD5" .
+
+Note that the default
+.BR TargetSignatures ()
+setting (see below)
+is to use this
+.BR SourceSignatures ()
+setting for any target files that are used
+to build other target files.
+Consequently, changing the value of
+.BR SourceSignatures ()
+will, by default,
+affect the up-to-date decision for all files in the build
+(or all files built with a specific construction environment
+when
+.BR env.SourceSignatures ()
+is used).
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Split( arg )
+.TP
+.RI env.Split( arg )
+Returns a list of file names or other objects.
+If arg is a string,
+it will be split on strings of white-space characters
+within the string,
+making it easier to write long lists of file names.
+If arg is already a list,
+the list will be returned untouched.
+If arg is any other type of object,
+it will be returned as a list
+containing just the object.
+
+Example:
+
+.ES
+files = Split("f1.c f2.c f3.c")
+files = env.Split("f4.c f5.c f6.c")
+files = Split("""
+ f7.c
+ f8.c
+ f9.c
+""")
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Tag( node ", " tags )
+Annotates file or directory Nodes with
+information about how the
+.BR Package ()
+Builder should package those files or directories.
+All tags are optional.
+
+Examples:
+
+.ES
+# makes sure the built library will be installed with 0644 file
+# access mode
+Tag( Library( 'lib.c' ), UNIX_ATTR="0644" )
+
+# marks file2.txt to be a documentation file
+Tag( 'file2.txt', DOC )
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI TargetSignatures( type )
+.TP
+.RI env.TargetSignatures( type )
+Note: Although it is not yet officially deprecated,
+use of this function is discouraged.
+See the
+.BR Decider ()
+function for a more flexible and straightforward way
+to configure SCons' decision-making.
+
+The
+.BR TargetSignatures ()
+function tells
+.B scons
+how to decide if a target file
+(a file that
+.I is
+built from any other files)
+has changed since the last time it
+was used to build some other target file.
+Legal values are
+.BR "build" ;
+.BR "content"
+(or its synonym
+.BR "MD5" );
+.BR "timestamp" ;
+or
+.BR "source" .
+
+If the environment method is used,
+the specified type of target signature is only used
+for targets built with that environment.
+If the global function is used,
+the specified type of signature becomes the default
+used for all target files that
+don't have an explicit target signature type
+specified for their environments.
+
+.B "content"
+(or its synonym
+.BR "MD5" )
+means
+.B scons
+decides that a target file has changed
+if the MD5 checksum of its contents has changed since
+the last time it was used to rebuild some other target file.
+This means
+.B scons
+will open up
+MD5 sum the contents
+of target files after they're built,
+and may decide that it does not need to rebuild
+"downstream" target files if a file was
+rebuilt with exactly the same contents as the last time.
+
+.B "timestamp"
+means
+.B scons
+decides that a target file has changed
+if its timestamp (modification time) has changed since
+the last time it was used to rebuild some other target file.
+(Note that although this is similar to the behavior of Make,
+by default it will also rebuild if the dependency is
+.I older
+than the last time it was used to rebuild the target file.)
+
+.B "source"
+means
+.B scons
+decides that a target file has changed
+as specified by the corresponding
+.BR SourceSignatures ()
+setting
+.BR "" ( "MD5"
+or
+.BR "timestamp" ).
+This means that
+.B scons
+will treat all input files to a target the same way,
+regardless of whether they are source files
+or have been built from other files.
+
+.B "build"
+means
+.B scons
+decides that a target file has changed
+if it has been rebuilt in this invocation
+or if its content or timestamp have changed
+as specified by the corresponding
+.BR SourceSignatures ()
+setting.
+This "propagates" the status of a rebuilt file
+so that other "downstream" target files
+will always be rebuilt,
+even if the contents or the timestamp
+have not changed.
+
+.B "build"
+signatures are fastest because
+.B "content"
+(or
+.BR "MD5" )
+signatures take longer to compute,
+but are more accurate than
+.B "timestamp"
+signatures,
+and can prevent unnecessary "downstream" rebuilds
+when a target file is rebuilt to the exact same contents
+as the previous build.
+The
+.B "source"
+setting provides the most consistent behavior
+when other target files may be rebuilt from
+both source and target input files.
+The default value is
+.BR "source" .
+
+Because the default setting is
+.BR "source" ,
+using
+.BR SourceSignatures ()
+is generally preferable to
+.BR TargetSignatures () ,
+so that the up-to-date decision
+will be consistent for all files
+(or all files built with a specific construction environment).
+Use of
+.BR TargetSignatures ()
+provides specific control for how built target files
+affect their "downstream" dependencies.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Tool( string [, toolpath ", " **kw ])
+Returns a callable object
+that can be used to initialize
+a construction environment using the
+tools keyword of the Environment() method.
+The object may be called with a construction
+environment as an argument,
+in which case the object will
+add the necessary variables
+to the construction environment
+and the name of the tool will be added to the
+.B $TOOLS
+construction variable.
+
+Additional keyword arguments are passed to the tool's
+.B generate()
+method.
+
+Examples:
+
+.ES
+env = Environment(tools = [ Tool('msvc') ])
+
+env = Environment()
+t = Tool('msvc')
+t(env) # adds 'msvc' to the TOOLS variable
+u = Tool('opengl', toolpath = ['tools'])
+u(env) # adds 'opengl' to the TOOLS variable
+.EE
+.TP
+.RI env.Tool( string [, toolpath ", " **kw ])
+Applies the callable object for the specified tool
+.I string
+to the environment through which the method was called.
+
+Additional keyword arguments are passed to the tool's
+.B generate()
+method.
+
+.ES
+env.Tool('gcc')
+env.Tool('opengl', toolpath = ['build/tools'])
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI Value( value ", [" built_value ])
+.TP
+.RI env.Value( value ", [" built_value ])
+Returns a Node object representing the specified Python value. Value
+Nodes can be used as dependencies of targets. If the result of
+calling
+.BR str( value )
+changes between SCons runs, any targets depending on
+.BR Value( value )
+will be rebuilt.
+(This is true even when using timestamps to decide if
+files are up-to-date.)
+When using timestamp source signatures, Value Nodes'
+timestamps are equal to the system time when the Node is created.
+
+The returned Value Node object has a
+.BR write ()
+method that can be used to "build" a Value Node
+by setting a new value.
+The optional
+.I built_value
+argument can be specified
+when the Value Node is created
+to indicate the Node should already be considered
+"built."
+There is a corresponding
+.BR read ()
+method that will return the built value of the Node.
+
+Examples:
+
+.ES
+env = Environment()
+
+def create(target, source, env):
+ # A function that will write a 'prefix=$SOURCE'
+ # string into the file name specified as the
+ # $TARGET.
+ f = open(str(target[0]), 'wb')
+ f.write('prefix=' + source[0].get_contents())
+
+# Fetch the prefix= argument, if any, from the command
+# line, and use /usr/local as the default.
+prefix = ARGUMENTS.get('prefix', '/usr/local')
+
+# Attach a .Config() builder for the above function action
+# to the construction environment.
+env['BUILDERS']['Config'] = Builder(action = create)
+env.Config(target = 'package-config', source = Value(prefix))
+
+def build_value(target, source, env):
+ # A function that "builds" a Python Value by updating
+ # the the Python value with the contents of the file
+ # specified as the source of the Builder call ($SOURCE).
+ target[0].write(source[0].get_contents())
+
+output = env.Value('before')
+input = env.Value('after')
+
+# Attach a .UpdateValue() builder for the above function
+# action to the construction environment.
+env['BUILDERS']['UpdateValue'] = Builder(action = build_value)
+env.UpdateValue(target = Value(output), source = Value(input))
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI VariantDir( variant_dir ", " src_dir ", [" duplicate ])
+.TP
+.RI env.VariantDir( variant_dir ", " src_dir ", [" duplicate ])
+Use the
+.BR VariantDir ()
+function to create a copy of your sources in another location:
+if a name under
+.IR variant_dir
+is not found but exists under
+.IR src_dir ,
+the file or directory is copied to
+.IR variant_dir .
+Target files can be built in a different directory
+than the original sources by simply refering to the sources (and targets)
+within the variant tree.
+
+.BR VariantDir ()
+can be called multiple times with the same
+.I src_dir
+to set up multiple builds with different options
+.RI ( variants ).
+The
+.I src_dir
+location must be in or underneath the SConstruct file's directory, and
+.I variant_dir
+may not be underneath
+.IR src_dir .
+'\"TODO: Can the above restrictions be clarified or relaxed?
+'\"TODO: The latter restriction is clearly not completely right;
+'\"TODO: src_dir = '.' works fine with a build dir under it.
+
+The default behavior is for
+.B scons
+to physically duplicate the source files in the variant tree.
+Thus, a build performed in the variant tree is guaranteed to be identical
+to a build performed in the source tree even if
+intermediate source files are generated during the build,
+or preprocessors or other scanners search for included files
+relative to the source file,
+or individual compilers or other invoked tools are hard-coded
+to put derived files in the same directory as source files.
+
+If possible on the platform,
+the duplication is performed by linking rather than copying;
+see also the
+.IR --duplicate
+command-line option.
+Moreover, only the files needed for the build are duplicated;
+files and directories that are not used are not present in
+.IR variant_dir .
+
+Duplicating the source tree may be disabled by setting the
+.I duplicate
+argument to 0 (zero).
+This will cause
+.B scons
+to invoke Builders using the path names of source files in
+.I src_dir
+and the path names of derived files within
+.IR variant_dir .
+This is always more efficient than
+.IR duplicate =1,
+and is usually safe for most builds
+(but see above for cases that may cause problems).
+
+Note that
+.BR VariantDir ()
+works most naturally with a subsidiary SConscript file.
+However, you would then call the subsidiary SConscript file
+not in the source directory, but in the
+.I variant_dir ,
+regardless of the value of
+.IR duplicate .
+This is how you tell
+.B scons
+which variant of a source tree to build:
+
+.ES
+# run src/SConscript in two variant directories
+VariantDir('build/variant1', 'src')
+SConscript('build/variant1/SConscript')
+VariantDir('build/variant2', 'src')
+SConscript('build/variant2/SConscript')
+.EE
+
+.IP
+See also the
+.BR SConscript ()
+function, described above,
+for another way to specify a variant directory
+in conjunction with calling a subsidiary SConscript file.
+
+Examples:
+
+.ES
+# use names in the build directory, not the source directory
+VariantDir('build', 'src', duplicate=0)
+Program('build/prog', 'build/source.c')
+.EE
+
+.ES
+# this builds both the source and docs in a separate subtree
+VariantDir('build', '.', duplicate=0)
+SConscript(dirs=['build/src','build/doc'])
+.EE
+
+.ES
+# same as previous example, but only uses SConscript
+SConscript(dirs='src', variant_dir='build/src', duplicate=0)
+SConscript(dirs='doc', variant_dir='build/doc', duplicate=0)
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI WhereIs( program ", [" path ", " pathext ", " reject ])
+.TP
+.RI env.WhereIs( program ", [" path ", " pathext ", " reject ])
+
+Searches for the specified executable
+.I program,
+returning the full path name to the program
+if it is found,
+and returning None if not.
+Searches the specified
+.I path,
+the value of the calling environment's PATH
+(env['ENV']['PATH']),
+or the user's current external PATH
+(os.environ['PATH'])
+by default.
+On Windows systems, searches for executable
+programs with any of the file extensions
+listed in the specified
+.I pathext,
+the calling environment's PATHEXT
+(env['ENV']['PATHEXT'])
+or the user's current PATHEXT
+(os.environ['PATHEXT'])
+by default.
+Will not select any
+path name or names
+in the specified
+.I reject
+list, if any.
+
+.SS SConscript Variables
+In addition to the global functions and methods,
+.B scons
+supports a number of Python variables
+that can be used in SConscript files
+to affect how you want the build to be performed.
+These variables may be accessed from custom Python modules that you
+import into an SConscript file by adding the following
+to the Python module:
+
+.ES
+from SCons.Script import *
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+ARGLIST
+A list
+.IR keyword = value
+arguments specified on the command line.
+Each element in the list is a tuple
+containing the
+.RI ( keyword , value )
+of the argument.
+The separate
+.I keyword
+and
+.I value
+elements of the tuple
+can be accessed by
+subscripting for element
+.B [0]
+and
+.B [1]
+of the tuple, respectively.
+
+Example:
+
+.ES
+print "first keyword, value =", ARGLIST[0][0], ARGLIST[0][1]
+print "second keyword, value =", ARGLIST[1][0], ARGLIST[1][1]
+third_tuple = ARGLIST[2]
+print "third keyword, value =", third_tuple[0], third_tuple[1]
+for key, value in ARGLIST:
+ # process key and value
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+ARGUMENTS
+A dictionary of all the
+.IR keyword = value
+arguments specified on the command line.
+The dictionary is not in order,
+and if a given keyword has
+more than one value assigned to it
+on the command line,
+the last (right-most) value is
+the one in the
+.B ARGUMENTS
+dictionary.
+
+Example:
+
+.ES
+if ARGUMENTS.get('debug', 0):
+ env = Environment(CCFLAGS = '-g')
+else:
+ env = Environment()
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+BUILD_TARGETS
+A list of the targets which
+.B scons
+will actually try to build,
+regardless of whether they were specified on
+the command line or via the
+.BR Default ()
+function or method.
+The elements of this list may be strings
+.I or
+nodes, so you should run the list through the Python
+.B str
+function to make sure any Node path names
+are converted to strings.
+
+Because this list may be taken from the
+list of targets specified using the
+.BR Default ()
+function or method,
+the contents of the list may change
+on each successive call to
+.BR Default ().
+See the
+.B DEFAULT_TARGETS
+list, below,
+for additional information.
+
+Example:
+
+.ES
+if 'foo' in BUILD_TARGETS:
+ print "Don't forget to test the `foo' program!"
+if 'special/program' in BUILD_TARGETS:
+ SConscript('special')
+.EE
+.IP
+Note that the
+.B BUILD_TARGETS
+list only contains targets expected listed
+on the command line or via calls to the
+.BR Default ()
+function or method.
+It does
+.I not
+contain all dependent targets that will be built as
+a result of making the sure the explicitly-specified
+targets are up to date.
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+COMMAND_LINE_TARGETS
+A list of the targets explicitly specified on
+the command line.
+If there are no targets specified on the command line,
+the list is empty.
+This can be used, for example,
+to take specific actions only
+when a certain target or targets
+is explicitly being built.
+
+Example:
+
+.ES
+if 'foo' in COMMAND_LINE_TARGETS:
+ print "Don't forget to test the `foo' program!"
+if 'special/program' in COMMAND_LINE_TARGETS:
+ SConscript('special')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+DEFAULT_TARGETS
+A list of the target
+.I nodes
+that have been specified using the
+.BR Default ()
+function or method.
+The elements of the list are nodes,
+so you need to run them through the Python
+.B str
+function to get at the path name for each Node.
+
+Example:
+
+.ES
+print str(DEFAULT_TARGETS[0])
+if 'foo' in map(str, DEFAULT_TARGETS):
+ print "Don't forget to test the `foo' program!"
+.EE
+.IP
+The contents of the
+.B DEFAULT_TARGETS
+list change on on each successive call to the
+.BR Default ()
+function:
+
+.ES
+print map(str, DEFAULT_TARGETS) # originally []
+Default('foo')
+print map(str, DEFAULT_TARGETS) # now a node ['foo']
+Default('bar')
+print map(str, DEFAULT_TARGETS) # now a node ['foo', 'bar']
+Default(None)
+print map(str, DEFAULT_TARGETS) # back to []
+.EE
+.IP
+Consequently, be sure to use
+.B DEFAULT_TARGETS
+only after you've made all of your
+.BR Default ()
+calls,
+or else simply be careful of the order
+of these statements in your SConscript files
+so that you don't look for a specific
+default target before it's actually been added to the list.
+
+.SS Construction Variables
+.\" XXX From Gary Ruben, 23 April 2002:
+.\" I think it would be good to have an example with each construction
+.\" variable description in the documentation.
+.\" eg.
+.\" CC The C compiler
+.\" Example: env["CC"] = "c68x"
+.\" Default: env["CC"] = "cc"
+.\"
+.\" CCCOM The command line ...
+.\" Example:
+.\" To generate the compiler line c68x -ps -qq -mr -o $TARGET $SOURCES
+.\" env["CC"] = "c68x"
+.\" env["CFLAGS"] = "-ps -qq -mr"
+.\" env["CCCOM"] = "$CC $CFLAGS -o $TARGET $SOURCES
+.\" Default:
+.\" (I dunno what this is ;-)
+A construction environment has an associated dictionary of
+.I construction variables
+that are used by built-in or user-supplied build rules.
+Construction variables must follow the same rules for
+Python identifiers:
+the initial character must be an underscore or letter,
+followed by any number of underscores, letters, or digits.
+
+A number of useful construction variables are automatically defined by
+scons for each supported platform, and additional construction variables
+can be defined by the user. The following is a list of the automatically
+defined construction variables:
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+'\" BEGIN GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS
+'\"
+'\" The descriptions below of the various SCons construction variables
+'\" are generated from the .xml files that live next to the various
+'\" Python modules in the build enginer library. If you're reading
+'\" this [gnt]roff file with an eye towards patching this man page,
+'\" you can still submit a diff against this text, but it will have to
+'\" be translated to a diff against the underlying .xml file before the
+'\" patch is actually accepted. If you do that yourself, it will make
+'\" it easier to integrate the patch.
+'\"
+'\" BEGIN GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.so variables.man
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+'\" END GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS
+'\"
+'\" The descriptions above of the various SCons construction variables
+'\" are generated from the .xml files that live next to the various
+'\" Python modules in the build enginer library. If you're reading
+'\" this [gnt]roff file with an eye towards patching this man page,
+'\" you can still submit a diff against this text, but it will have to
+'\" be translated to a diff against the underlying .xml file before the
+'\" patch is actually accepted. If you do that yourself, it will make
+'\" it easier to integrate the patch.
+'\"
+'\" END GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+.LP
+Construction variables can be retrieved and set using the
+.B Dictionary
+method of the construction environment:
+
+.ES
+dict = env.Dictionary()
+dict["CC"] = "cc"
+.EE
+
+or using the [] operator:
+
+.ES
+env["CC"] = "cc"
+.EE
+
+Construction variables can also be passed to the construction environment
+constructor:
+
+.ES
+env = Environment(CC="cc")
+.EE
+
+or when copying a construction environment using the
+.B Clone
+method:
+
+.ES
+env2 = env.Clone(CC="cl.exe")
+.EE
+
+.SS Configure Contexts
+
+.B scons
+supports
+.I configure contexts,
+an integrated mechanism similar to the
+various AC_CHECK macros in GNU autoconf
+for testing for the existence of C header
+files, libraries, etc.
+In contrast to autoconf,
+.B scons
+does not maintain an explicit cache of the tested values,
+but uses its normal dependency tracking to keep the checked values
+up to date. However, users may override this behaviour with the
+.B --config
+command line option.
+
+The following methods can be used to perform checks:
+
+.TP
+.RI Configure( env ", [" custom_tests ", " conf_dir ", " log_file ", " config_h ", " clean ", " help])
+.TP
+.RI env.Configure([ custom_tests ", " conf_dir ", " log_file ", " config_h ", " clean ", " help])
+This creates a configure context, which can be used to perform checks.
+.I env
+specifies the environment for building the tests.
+This environment may be modified when performing checks.
+.I custom_tests
+is a dictionary containing custom tests.
+See also the section about custom tests below.
+By default, no custom tests are added to the configure context.
+.I conf_dir
+specifies a directory where the test cases are built.
+Note that this directory is not used for building
+normal targets.
+The default value is the directory
+#/.sconf_temp.
+.I log_file
+specifies a file which collects the output from commands
+that are executed to check for the existence of header files, libraries, etc.
+The default is the file #/config.log.
+If you are using the
+.BR VariantDir ()
+method,
+you may want to specify a subdirectory under your variant directory.
+.I config_h
+specifies a C header file where the results of tests
+will be written, e.g. #define HAVE_STDIO_H, #define HAVE_LIBM, etc.
+The default is to not write a
+.B config.h
+file.
+You can specify the same
+.B config.h
+file in multiple calls to Configure,
+in which case
+.B scons
+will concatenate all results in the specified file.
+Note that SCons
+uses its normal dependency checking
+to decide if it's necessary to rebuild
+the specified
+.I config_h
+file.
+This means that the file is not necessarily re-built each
+time scons is run,
+but is only rebuilt if its contents will have changed
+and some target that depends on the
+.I config_h
+file is being built.
+
+The optional
+.B clean
+and
+.B help
+arguments can be used to suppress execution of the configuration
+tests when the
+.B -c/--clean
+or
+.B -H/-h/--help
+options are used, respectively.
+The default behavior is always to execute
+configure context tests,
+since the results of the tests may
+affect the list of targets to be cleaned
+or the help text.
+If the configure tests do not affect these,
+then you may add the
+.B clean=False
+or
+.B help=False
+arguments
+(or both)
+to avoid unnecessary test execution.
+
+.EE
+A created
+.B Configure
+instance has the following associated methods:
+
+.TP
+.RI SConf.Finish( context )
+.TP
+.IR sconf .Finish()
+This method should be called after configuration is done.
+It returns the environment as modified
+by the configuration checks performed.
+After this method is called, no further checks can be performed
+with this configuration context.
+However, you can create a new
+.RI Configure
+context to perform additional checks.
+Only one context should be active at a time.
+
+The following Checks are predefined.
+(This list will likely grow larger as time
+goes by and developers contribute new useful tests.)
+
+.TP
+.RI SConf.CheckHeader( context ", " header ", [" include_quotes ", " language ])
+.TP
+.IR sconf .CheckHeader( header ", [" include_quotes ", " language ])
+Checks if
+.I header
+is usable in the specified language.
+.I header
+may be a list,
+in which case the last item in the list
+is the header file to be checked,
+and the previous list items are
+header files whose
+.B #include
+lines should precede the
+header line being checked for.
+The optional argument
+.I include_quotes
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote.
+By default, both characters are " (double quote).
+The optional argument
+.I language
+should be either
+.B C
+or
+.B C++
+and selects the compiler to be used for the check.
+Returns 1 on success and 0 on failure.
+
+.TP
+.RI SConf.CheckCHeader( context ", " header ", [" include_quotes ])
+.TP
+.IR sconf .CheckCHeader( header ", [" include_quotes ])
+This is a wrapper around
+.B SConf.CheckHeader
+which checks if
+.I header
+is usable in the C language.
+.I header
+may be a list,
+in which case the last item in the list
+is the header file to be checked,
+and the previous list items are
+header files whose
+.B #include
+lines should precede the
+header line being checked for.
+The optional argument
+.I include_quotes
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote (both default
+to \N'34').
+Returns 1 on success and 0 on failure.
+
+.TP
+.RI SConf.CheckCXXHeader( context ", " header ", [" include_quotes ])
+.TP
+.IR sconf .CheckCXXHeader( header ", [" include_quotes ])
+This is a wrapper around
+.B SConf.CheckHeader
+which checks if
+.I header
+is usable in the C++ language.
+.I header
+may be a list,
+in which case the last item in the list
+is the header file to be checked,
+and the previous list items are
+header files whose
+.B #include
+lines should precede the
+header line being checked for.
+The optional argument
+.I include_quotes
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote (both default
+to \N'34').
+Returns 1 on success and 0 on failure.
+
+.TP
+.RI SConf.CheckFunc( context, ", " function_name ", [" header ", " language ])
+.TP
+.IR sconf .CheckFunc( function_name ", [" header ", " language ])
+Checks if the specified
+C or C++ function is available.
+.I function_name
+is the name of the function to check for.
+The optional
+.I header
+argument is a string
+that will be
+placed at the top
+of the test file
+that will be compiled
+to check if the function exists;
+the default is:
+.ES
+#ifdef __cplusplus
+extern "C"
+#endif
+char function_name();
+.EE
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+
+.TP
+.RI SConf.CheckLib( context ", [" library ", " symbol ", " header ", " language ", " autoadd=1 ])
+.TP
+.IR sconf .CheckLib([ library ", " symbol ", " header ", " language ", " autoadd=1 ])
+Checks if
+.I library
+provides
+.IR symbol .
+If the value of
+.I autoadd
+is 1 and the library provides the specified
+.IR symbol ,
+appends the library to the LIBS construction environment variable.
+.I library
+may also be None (the default),
+in which case
+.I symbol
+is checked with the current LIBS variable,
+or a list of library names,
+in which case each library in the list
+will be checked for
+.IR symbol .
+If
+.I symbol
+is not set or is
+.BR None ,
+then
+.BR SConf.CheckLib ()
+just checks if
+you can link against the specified
+.IR library .
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+The default value for
+.I autoadd
+is 1.
+This method returns 1 on success and 0 on error.
+
+.TP
+.RI SConf.CheckLibWithHeader( context ", " library ", " header ", " language ", [" call ", " autoadd ])
+.TP
+.IR sconf .CheckLibWithHeader( library ", " header ", " language ", [" call ", " autoadd ])
+
+In contrast to the
+.RI SConf.CheckLib
+call, this call provides a more sophisticated way to check against libraries.
+Again,
+.I library
+specifies the library or a list of libraries to check.
+.I header
+specifies a header to check for.
+.I header
+may be a list,
+in which case the last item in the list
+is the header file to be checked,
+and the previous list items are
+header files whose
+.B #include
+lines should precede the
+header line being checked for.
+.I language
+may be one of 'C','c','CXX','cxx','C++' and 'c++'.
+.I call
+can be any valid expression (with a trailing ';').
+If
+.I call
+is not set,
+the default simply checks that you
+can link against the specified
+.IR library .
+.I autoadd
+specifies whether to add the library to the environment (only if the check
+succeeds). This method returns 1 on success and 0 on error.
+
+.TP
+.RI SConf.CheckType( context ", " type_name ", [" includes ", " language ])
+.TP
+.IR sconf .CheckType( type_name ", [" includes ", " language ])
+Checks for the existence of a type defined by
+.BR typedef .
+.I type_name
+specifies the typedef name to check for.
+.I includes
+is a string containing one or more
+.B #include
+lines that will be inserted into the program
+that will be run to test for the existence of the type.
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+Example:
+.ES
+sconf.CheckType('foo_type', '#include "my_types.h"', 'C++')
+.EE
+
+.TP
+.RI Configure.CheckCC( self )
+Checks whether the C compiler (as defined by the CC construction variable) works
+by trying to compile a small source file.
+
+By default, SCons only detects if there is a program with the correct name, not
+if it is a functioning compiler.
+
+This uses the exact same command than the one used by the object builder for C
+source file, so it can be used to detect if a particular compiler flag works or
+not.
+
+.TP
+.RI Configure.CheckCXX( self )
+Checks whether the C++ compiler (as defined by the CXX construction variable)
+works by trying to compile a small source file. By default, SCons only detects
+if there is a program with the correct name, not if it is a functioning compiler.
+
+This uses the exact same command than the one used by the object builder for
+CXX source files, so it can be used to detect if a particular compiler flag
+works or not.
+
+.TP
+.RI Configure.CheckSHCC( self )
+Checks whether the C compiler (as defined by the SHCC construction variable) works
+by trying to compile a small source file. By default, SCons only detects if
+there is a program with the correct name, not if it is a functioning compiler.
+
+This uses the exact same command than the one used by the object builder for C
+source file, so it can be used to detect if a particular compiler flag works or
+not. This does not check whether the object code can be used to build a shared
+library, only that the compilation (not link) succeeds.
+
+.TP
+.RI Configure.CheckSHCXX( self )
+Checks whether the C++ compiler (as defined by the SHCXX construction variable)
+works by trying to compile a small source file. By default, SCons only detects
+if there is a program with the correct name, not if it is a functioning compiler.
+
+This uses the exact same command than the one used by the object builder for
+CXX source files, so it can be used to detect if a particular compiler flag
+works or not. This does not check whether the object code can be used to build
+a shared library, only that the compilation (not link) succeeds.
+
+.EE
+Example of a typical Configure usage:
+
+.ES
+env = Environment()
+conf = Configure( env )
+if not conf.CheckCHeader( 'math.h' ):
+ print 'We really need math.h!'
+ Exit(1)
+if conf.CheckLibWithHeader( 'qt', 'qapp.h', 'c++',
+ 'QApplication qapp(0,0);' ):
+ # do stuff for qt - usage, e.g.
+ conf.env.Append( CPPFLAGS = '-DWITH_QT' )
+env = conf.Finish()
+.EE
+
+.TP
+.RI SConf.CheckTypeSize( context ", " type_name ", [" header ", " language ", " expect ])
+.TP
+.IR sconf .CheckTypeSize( type_name ", [" header ", " language ", " expect ])
+Checks for the size of a type defined by
+.BR typedef .
+.I type_name
+specifies the typedef name to check for.
+The optional
+.I header
+argument is a string
+that will be
+placed at the top
+of the test file
+that will be compiled
+to check if the function exists;
+the default is empty.
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+The optional
+.I expect
+argument should be an integer.
+If this argument is used,
+the function will only check whether the type
+given in type_name has the expected size (in bytes).
+For example,
+.B "CheckTypeSize('short', expect = 2)"
+will return success only if short is two bytes.
+
+.ES
+.EE
+
+.TP
+.RI SConf.CheckDeclaration( context ", " symbol ", [" includes ", " language ])
+.TP
+.IR sconf .CheckDeclaration( symbol ", [" includes ", " language ])
+Checks if the specified
+.I symbol
+is declared.
+.I includes
+is a string containing one or more
+.B #include
+lines that will be inserted into the program
+that will be run to test for the existence of the type.
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+
+.TP
+.RI SConf.Define( context ", " symbol ", [" value ", " comment ])
+.TP
+.IR sconf .Define( symbol ", [" value ", " comment ])
+This function does not check for anything, but defines a
+preprocessor symbol that will be added to the configuration header file.
+It is the equivalent of AC_DEFINE,
+and defines the symbol
+.I name
+with the optional
+.B value
+and the optional comment
+.BR comment .
+
+.IP
+Examples:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL
+conf.Define('A_SYMBOL')
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL 1
+conf.Define('A_SYMBOL', 1)
+.EE
+
+.IP
+Be careful about quoting string values, though:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL YA
+conf.Define('A_SYMBOL', "YA")
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL "YA"
+conf.Define('A_SYMBOL', '"YA"')
+.EE
+
+.IP
+For comment:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following lines in the config header file:
+# /* Set to 1 if you have a symbol */
+# #define A_SYMBOL 1
+conf.Define('A_SYMBOL', 1, 'Set to 1 if you have a symbol')
+.EE
+
+.EE
+You can define your own custom checks.
+in addition to the predefined checks.
+These are passed in a dictionary to the Configure function.
+This dictionary maps the names of the checks
+to user defined Python callables
+(either Python functions or class instances implementing the
+.I __call__
+method).
+The first argument of the call is always a
+.I CheckContext
+instance followed by the arguments,
+which must be supplied by the user of the check.
+These CheckContext instances define the following methods:
+
+.TP
+.RI CheckContext.Message( self ", " text )
+
+Usually called before the check is started.
+.I text
+will be displayed to the user, e.g. 'Checking for library X...'
+
+.TP
+.RI CheckContext.Result( self, ", " res )
+
+Usually called after the check is done.
+.I res
+can be either an integer or a string. In the former case, 'yes' (res != 0)
+or 'no' (res == 0) is displayed to the user, in the latter case the
+given string is displayed.
+
+.TP
+.RI CheckContext.TryCompile( self ", " text ", " extension )
+Checks if a file with the specified
+.I extension
+(e.g. '.c') containing
+.I text
+can be compiled using the environment's
+.B Object
+builder. Returns 1 on success and 0 on failure.
+
+.TP
+.RI CheckContext.TryLink( self ", " text ", " extension )
+Checks, if a file with the specified
+.I extension
+(e.g. '.c') containing
+.I text
+can be compiled using the environment's
+.B Program
+builder. Returns 1 on success and 0 on failure.
+
+.TP
+.RI CheckContext.TryRun( self ", " text ", " extension )
+Checks, if a file with the specified
+.I extension
+(e.g. '.c') containing
+.I text
+can be compiled using the environment's
+.B Program
+builder. On success, the program is run. If the program
+executes successfully
+(that is, its return status is 0),
+a tuple
+.I (1, outputStr)
+is returned, where
+.I outputStr
+is the standard output of the
+program.
+If the program fails execution
+(its return status is non-zero),
+then (0, '') is returned.
+
+.TP
+.RI CheckContext.TryAction( self ", " action ", [" text ", " extension ])
+Checks if the specified
+.I action
+with an optional source file (contents
+.I text
+, extension
+.I extension
+= ''
+) can be executed.
+.I action
+may be anything which can be converted to a
+.B scons
+.RI Action.
+On success,
+.I (1, outputStr)
+is returned, where
+.I outputStr
+is the content of the target file.
+On failure
+.I (0, '')
+is returned.
+
+.TP
+.RI CheckContext.TryBuild( self ", " builder ", [" text ", " extension ])
+Low level implementation for testing specific builds;
+the methods above are based on this method.
+Given the Builder instance
+.I builder
+and the optional
+.I text
+of a source file with optional
+.IR extension ,
+this method returns 1 on success and 0 on failure. In addition,
+.I self.lastTarget
+is set to the build target node, if the build was successful.
+
+.EE
+Example for implementing and using custom tests:
+
+.ES
+def CheckQt(context, qtdir):
+ context.Message( 'Checking for qt ...' )
+ lastLIBS = context.env['LIBS']
+ lastLIBPATH = context.env['LIBPATH']
+ lastCPPPATH= context.env['CPPPATH']
+ context.env.Append(LIBS = 'qt', LIBPATH = qtdir + '/lib', CPPPATH = qtdir + '/include' )
+ ret = context.TryLink("""
+#include <qapp.h>
+int main(int argc, char **argv) {
+ QApplication qapp(argc, argv);
+ return 0;
+}
+""")
+ if not ret:
+ context.env.Replace(LIBS = lastLIBS, LIBPATH=lastLIBPATH, CPPPATH=lastCPPPATH)
+ context.Result( ret )
+ return ret
+
+env = Environment()
+conf = Configure( env, custom_tests = { 'CheckQt' : CheckQt } )
+if not conf.CheckQt('/usr/lib/qt'):
+ print 'We really need qt!'
+ Exit(1)
+env = conf.Finish()
+.EE
+
+.SS Command-Line Construction Variables
+
+Often when building software,
+some variables must be specified at build time.
+For example, libraries needed for the build may be in non-standard
+locations, or site-specific compiler options may need to be passed to the
+compiler.
+.B scons
+provides a
+.B Variables
+object to support overriding construction variables
+on the command line:
+.ES
+$ scons VARIABLE=foo
+.EE
+The variable values can also be specified in a text-based SConscript file.
+To create a Variables object, call the Variables() function:
+
+.TP
+.RI Variables([ files "], [" args ])
+This creates a Variables object that will read construction variables from
+the file or list of filenames specified in
+.IR files .
+If no files are specified,
+or the
+.I files
+argument is
+.BR None ,
+then no files will be read.
+The optional argument
+.I args
+is a dictionary of
+values that will override anything read from the specified files;
+it is primarily intended to be passed the
+.B ARGUMENTS
+dictionary that holds variables
+specified on the command line.
+Example:
+
+.ES
+vars = Variables('custom.py')
+vars = Variables('overrides.py', ARGUMENTS)
+vars = Variables(None, {FOO:'expansion', BAR:7})
+.EE
+
+Variables objects have the following methods:
+
+.TP
+.RI Add( key ", [" help ", " default ", " validator ", " converter ])
+This adds a customizable construction variable to the Variables object.
+.I key
+is the name of the variable.
+.I help
+is the help text for the variable.
+.I default
+is the default value of the variable;
+if the default value is
+.B None
+and there is no explicit value specified,
+the construction variable will
+.I not
+be added to the construction environment.
+.I validator
+is called to validate the value of the variable, and should take three
+arguments: key, value, and environment.
+The recommended way to handle an invalid value is
+to raise an exception (see example below).
+.I converter
+is called to convert the value before putting it in the environment, and
+should take either a value, or the value and environment, as parameters.
+The
+.I converter
+must return a value,
+which will be converted into a string
+before being validated by the
+.I validator
+(if any)
+and then added to the environment.
+
+Examples:
+
+.ES
+vars.Add('CC', 'The C compiler')
+
+def validate_color(key, val, env):
+ if not val in ['red', 'blue', 'yellow']:
+ raise "Invalid color value '%s'" % val
+vars.Add('COLOR', validator=valid_color)
+.EE
+
+.TP
+.RI AddVariables( list )
+A wrapper script that adds
+multiple customizable construction variables
+to a Variables object.
+.I list
+is a list of tuple or list objects
+that contain the arguments
+for an individual call to the
+.B Add
+method.
+
+.ES
+opt.AddVariables(
+ ('debug', '', 0),
+ ('CC', 'The C compiler'),
+ ('VALIDATE', 'An option for testing validation',
+ 'notset', validator, None),
+ )
+.EE
+
+.TP
+.RI Update( env ", [" args ])
+This updates a construction environment
+.I env
+with the customized construction variables.
+Any specified variables that are
+.I not
+configured for the Variables object
+will be saved and may be
+retrieved with the
+.BR UnknownVariables ()
+method, below.
+
+Normally this method is not called directly,
+but is called indirectly by passing the Variables object to
+the Environment() function:
+
+.ES
+env = Environment(variables=vars)
+.EE
+
+.IP
+The text file(s) that were specified
+when the Variables object was created
+are executed as Python scripts,
+and the values of (global) Python variables set in the file
+are added to the construction environment.
+
+Example:
+
+.ES
+CC = 'my_cc'
+.EE
+
+.TP
+.RI UnknownVariables( )
+Returns a dictionary containing any
+variables that were specified
+either in the files or the dictionary
+with which the Variables object was initialized,
+but for which the Variables object was
+not configured.
+
+.ES
+env = Environment(variables=vars)
+for key, value in vars.UnknownVariables():
+ print "unknown variable: %s=%s" % (key, value)
+.EE
+
+.TP
+.RI Save( filename ", " env )
+This saves the currently set variables into a script file named
+.I filename
+that can be used on the next invocation to automatically load the current
+settings. This method combined with the Variables method can be used to
+support caching of variables between runs.
+
+.ES
+env = Environment()
+vars = Variables(['variables.cache', 'custom.py'])
+vars.Add(...)
+vars.Update(env)
+vars.Save('variables.cache', env)
+.EE
+
+.TP
+.RI GenerateHelpText( env ", [" sort ])
+This generates help text documenting the customizable construction
+variables suitable to passing in to the Help() function.
+.I env
+is the construction environment that will be used to get the actual values
+of customizable variables. Calling with
+an optional
+.I sort
+function
+will cause the output to be sorted
+by the specified argument.
+The specific
+.I sort
+function
+should take two arguments
+and return
+-1, 0 or 1
+(like the standard Python
+.I cmp
+function).
+
+.ES
+Help(vars.GenerateHelpText(env))
+Help(vars.GenerateHelpText(env, sort=cmp))
+.EE
+
+.TP
+.RI FormatVariableHelpText( env ", " opt ", " help ", " default ", " actual )
+This method returns a formatted string
+containing the printable help text
+for one option.
+It is normally not called directly,
+but is called by the
+.IR GenerateHelpText ()
+method to create the returned help text.
+It may be overridden with your own
+function that takes the arguments specified above
+and returns a string of help text formatted to your liking.
+Note that the
+.IR GenerateHelpText ()
+will not put any blank lines or extra
+characters in between the entries,
+so you must add those characters to the returned
+string if you want the entries separated.
+
+.ES
+def my_format(env, opt, help, default, actual):
+ fmt = "\n%s: default=%s actual=%s (%s)\n"
+ return fmt % (opt, default. actual, help)
+vars.FormatVariableHelpText = my_format
+.EE
+
+To make it more convenient to work with customizable Variables,
+.B scons
+provides a number of functions
+that make it easy to set up
+various types of Variables:
+
+.TP
+.RI BoolVariable( key ", " help ", " default )
+Return a tuple of arguments
+to set up a Boolean option.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will interpret the values
+.BR y ,
+.BR yes ,
+.BR t ,
+.BR true ,
+.BR 1 ,
+.B on
+and
+.B all
+as true,
+and the values
+.BR n ,
+.BR no ,
+.BR f ,
+.BR false ,
+.BR 0 ,
+.B off
+and
+.B none
+as false.
+
+.TP
+.RI EnumVariable( key ", " help ", " default ", " allowed_values ", [" map ", " ignorecase ])
+Return a tuple of arguments
+to set up an option
+whose value may be one
+of a specified list of legal enumerated values.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will only support those
+values in the
+.I allowed_values
+list.
+The optional
+.I map
+argument is a dictionary
+that can be used to convert
+input values into specific legal values
+in the
+.I allowed_values
+list.
+If the value of
+.I ignore_case
+is
+.B 0
+(the default),
+then the values are case-sensitive.
+If the value of
+.I ignore_case
+is
+.BR 1 ,
+then values will be matched
+case-insensitive.
+If the value of
+.I ignore_case
+is
+.BR 1 ,
+then values will be matched
+case-insensitive,
+and all input values will be
+converted to lower case.
+
+.TP
+.RI ListVariable( key ", " help ", " default ", " names ", [", map ])
+Return a tuple of arguments
+to set up an option
+whose value may be one or more
+of a specified list of legal enumerated values.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will only support the values
+.BR all ,
+.BR none ,
+or the values in the
+.I names
+list.
+More than one value may be specified,
+with all values separated by commas.
+The default may be a string of
+comma-separated default values,
+or a list of the default values.
+The optional
+.I map
+argument is a dictionary
+that can be used to convert
+input values into specific legal values
+in the
+.I names
+list.
+
+.TP
+.RI PackageVariable( key ", " help ", " default )
+Return a tuple of arguments
+to set up an option
+whose value is a path name
+of a package that may be
+enabled, disabled or
+given an explicit path name.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will support the values
+.BR yes ,
+.BR true ,
+.BR on ,
+.BR enable
+or
+.BR search ,
+in which case the specified
+.I default
+will be used,
+or the option may be set to an
+arbitrary string
+(typically the path name to a package
+that is being enabled).
+The option will also support the values
+.BR no ,
+.BR false ,
+.BR off
+or
+.BR disable
+to disable use of the specified option.
+
+.TP
+.RI PathVariable( key ", " help ", " default ", [" validator ])
+Return a tuple of arguments
+to set up an option
+whose value is expected to be a path name.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+An additional
+.I validator
+may be specified
+that will be called to
+verify that the specified path
+is acceptable.
+SCons supplies the
+following ready-made validators:
+.BR PathVariable.PathExists
+(the default),
+which verifies that the specified path exists;
+.BR PathVariable.PathIsFile ,
+which verifies that the specified path is an existing file;
+.BR PathVariable.PathIsDir ,
+which verifies that the specified path is an existing directory;
+.BR PathVariable.PathIsDirCreate ,
+which verifies that the specified path is a directory
+and will create the specified directory if the path does not exist;
+and
+.BR PathVariable.PathAccept ,
+which simply accepts the specific path name argument without validation,
+and which is suitable if you want your users
+to be able to specify a directory path that will be
+created as part of the build process, for example.
+You may supply your own
+.I validator
+function,
+which must take three arguments
+.RI ( key ,
+the name of the variable to be set;
+.IR val ,
+the specified value being checked;
+and
+.IR env ,
+the construction environment)
+and should raise an exception
+if the specified value is not acceptable.
+
+.RE
+These functions make it
+convenient to create a number
+of variables with consistent behavior
+in a single call to the
+.B AddVariables
+method:
+
+.ES
+vars.AddVariables(
+ 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),
+ PathVariable('foopath', 'where the foo library is installed', foopath,
+ PathVariable.PathIsDir),
+
+)
+.EE
+
+.SS File and Directory Nodes
+
+The
+.IR File ()
+and
+.IR Dir ()
+functions return
+.I File
+and
+.I Dir
+Nodes, respectively.
+python objects, respectively.
+Those objects have several user-visible attributes
+and methods that are often useful:
+
+.IP path
+The build path
+of the given
+file or directory.
+This path is relative to the top-level directory
+(where the
+.B SConstruct
+file is found).
+The build path is the same as the source path if
+.I variant_dir
+is not being used.
+
+.IP abspath
+The absolute build path of the given file or directory.
+
+.IP srcnode()
+The
+.IR srcnode ()
+method
+returns another
+.I File
+or
+.I Dir
+object representing the
+.I source
+path of the given
+.I File
+or
+.IR Dir .
+The
+
+.ES
+# Get the current build dir's path, relative to top.
+Dir('.').path
+# Current dir's absolute path
+Dir('.').abspath
+# Next line is always '.', because it is the top dir's path relative to itself.
+Dir('#.').path
+File('foo.c').srcnode().path # source path of the given source file.
+
+# Builders also return File objects:
+foo = env.Program('foo.c')
+print "foo will be built in %s"%foo.path
+.EE
+
+A
+.I Dir
+Node or
+.I File
+Node can also be used to create
+file and subdirectory Nodes relative to the generating Node.
+A
+.I Dir
+Node will place the new Nodes within the directory it represents.
+A
+.I File
+node will place the new Nodes within its parent directory
+(that is, "beside" the file in question).
+If
+.I d
+is a
+.I Dir
+(directory) Node and
+.I f
+is a
+.I File
+(file) Node,
+then these methods are available:
+
+.TP
+.IR d .Dir( name )
+Returns a directory Node for a subdirectory of
+.I d
+named
+.IR name .
+
+.TP
+.IR d .File( name )
+Returns a file Node for a file within
+.I d
+named
+.IR name .
+
+.TP
+.IR d .Entry( name )
+Returns an unresolved Node within
+.I d
+named
+.IR name .
+
+.TP
+.IR f .Dir( name )
+Returns a directory named
+.I name
+within the parent directory of
+.IR f .
+
+.TP
+.IR f .File( name )
+Returns a file named
+.I name
+within the parent directory of
+.IR f .
+
+.TP
+.IR f .Entry( name )
+Returns an unresolved Node named
+.I name
+within the parent directory of
+.IR f .
+
+.RE
+For example:
+
+.ES
+# Get a Node for a file within a directory
+incl = Dir('include')
+f = incl.File('header.h')
+
+# Get a Node for a subdirectory within a directory
+dist = Dir('project-3.2.1)
+src = dist.Dir('src')
+
+# Get a Node for a file in the same directory
+cfile = File('sample.c')
+hfile = cfile.File('sample.h')
+
+# Combined example
+docs = Dir('docs')
+html = docs.Dir('html')
+index = html.File('index.html')
+css = index.File('app.css')
+.EE
+
+.SH EXTENDING SCONS
+.SS Builder Objects
+.B scons
+can be extended to build different types of targets
+by adding new Builder objects
+to a construction environment.
+.IR "In general" ,
+you should only need to add a new Builder object
+when you want to build a new type of file or other external target.
+If you just want to invoke a different compiler or other tool
+to build a Program, Object, Library, or any other
+type of output file for which
+.B scons
+already has an existing Builder,
+it is generally much easier to
+use those existing Builders
+in a construction environment
+that sets the appropriate construction variables
+(CC, LINK, etc.).
+
+Builder objects are created
+using the
+.B Builder
+function.
+The
+.B Builder
+function accepts the following arguments:
+
+.IP action
+The command line string used to build the target from the source.
+.B action
+can also be:
+a list of strings representing the command
+to be executed and its arguments
+(suitable for enclosing white space in an argument),
+a dictionary
+mapping source file name suffixes to
+any combination of command line strings
+(if the builder should accept multiple source file extensions),
+a Python function;
+an Action object
+(see the next section);
+or a list of any of the above.
+
+An action function
+takes three arguments:
+.I source
+- a list of source nodes,
+.I target
+- a list of target nodes,
+.I env
+- the construction environment.
+
+.IP prefix
+The prefix that will be prepended to the target file name.
+This may be specified as a:
+
+.RS 10
+.HP 6
+*
+.IR string ,
+
+.HP 6
+*
+.I callable object
+- a function or other callable that takes
+two arguments (a construction environment and a list of sources)
+and returns a prefix,
+
+.HP 6
+*
+.I dictionary
+- specifies a mapping from a specific source suffix (of the first
+source specified) to a corresponding target prefix. Both the source
+suffix and target prefix specifications may use environment variable
+substitution, and the target prefix (the 'value' entries in the
+dictionary) may also be a callable object. The default target prefix
+may be indicated by a dictionary entry with a key value of None.
+.RE
+.P
+
+.ES
+b = Builder("build_it < $SOURCE > $TARGET"
+ prefix = "file-")
+
+def gen_prefix(env, sources):
+ return "file-" + env['PLATFORM'] + '-'
+b = Builder("build_it < $SOURCE > $TARGET",
+ prefix = gen_prefix)
+
+b = Builder("build_it < $SOURCE > $TARGET",
+ suffix = { None: "file-",
+ "$SRC_SFX_A": gen_prefix })
+.EE
+
+.IP suffix
+The suffix that will be appended to the target file name.
+This may be specified in the same manner as the prefix above.
+If the suffix is a string, then
+.B scons
+will append a '.' to the beginning of the suffix if it's not already
+there. The string returned by callable object (or obtained from the
+dictionary) is untouched and must append its own '.' to the beginning
+if one is desired.
+
+.ES
+b = Builder("build_it < $SOURCE > $TARGET"
+ suffix = "-file")
+
+def gen_suffix(env, sources):
+ return "." + env['PLATFORM'] + "-file"
+b = Builder("build_it < $SOURCE > $TARGET",
+ suffix = gen_suffix)
+
+b = Builder("build_it < $SOURCE > $TARGET",
+ suffix = { None: ".sfx1",
+ "$SRC_SFX_A": gen_suffix })
+.EE
+
+.IP ensure_suffix
+When set to any true value, causes
+.B scons
+to add the target suffix specified by the
+.I suffix
+keyword to any target strings
+that have a different suffix.
+(The default behavior is to leave untouched
+any target file name that looks like it already has any suffix.)
+
+.ES
+b1 = Builder("build_it < $SOURCE > $TARGET"
+ suffix = ".out")
+b2 = Builder("build_it < $SOURCE > $TARGET"
+ suffix = ".out",
+ ensure_suffix)
+env = Environment()
+env['BUILDERS']['B1'] = b1
+env['BUILDERS']['B2'] = b2
+
+# Builds "foo.txt" because ensure_suffix is not set.
+env.B1('foo.txt', 'foo.in')
+
+# Builds "bar.txt.out" because ensure_suffix is set.
+env.B2('bar.txt', 'bar.in')
+.EE
+
+.IP src_suffix
+The expected source file name suffix. This may be a string or a list
+of strings.
+
+.IP target_scanner
+A Scanner object that
+will be invoked to find
+implicit dependencies for this target file.
+This keyword argument should be used
+for Scanner objects that find
+implicit dependencies
+based only on the target file
+and the construction environment,
+.I not
+for implicit
+(See the section "Scanner Objects," below,
+for information about creating Scanner objects.)
+
+.IP source_scanner
+A Scanner object that
+will be invoked to
+find implicit dependences in
+any source files
+used to build this target file.
+This is where you would
+specify a scanner to
+find things like
+.B #include
+lines in source files.
+The pre-built
+.B DirScanner
+Scanner object may be used to
+indicate that this Builder
+should scan directory trees
+for on-disk changes to files
+that
+.B scons
+does not know about from other Builder or function calls.
+(See the section "Scanner Objects," below,
+for information about creating your own Scanner objects.)
+
+.IP target_factory
+A factory function that the Builder will use
+to turn any targets specified as strings into SCons Nodes.
+By default,
+SCons assumes that all targets are files.
+Other useful target_factory
+values include
+.BR Dir ,
+for when a Builder creates a directory target,
+and
+.BR Entry ,
+for when a Builder can create either a file
+or directory target.
+
+Example:
+
+.ES
+MakeDirectoryBuilder = Builder(action=my_mkdir, target_factory=Dir)
+env = Environment()
+env.Append(BUILDERS = {'MakeDirectory':MakeDirectoryBuilder})
+env.MakeDirectory('new_directory', [])
+.EE
+
+.IP
+Note that the call to the MakeDirectory Builder
+needs to specify an empty source list
+to make the string represent the builder's target;
+without that, it would assume the argument is the source,
+and would try to deduce the target name from it,
+which in the absence of an automatically-added prefix or suffix
+would lead to a matching target and source name
+and a circular dependency.
+
+.IP source_factory
+A factory function that the Builder will use
+to turn any sources specified as strings into SCons Nodes.
+By default,
+SCons assumes that all source are files.
+Other useful source_factory
+values include
+.BR Dir ,
+for when a Builder uses a directory as a source,
+and
+.BR Entry ,
+for when a Builder can use files
+or directories (or both) as sources.
+
+Example:
+
+.ES
+CollectBuilder = Builder(action=my_mkdir, source_factory=Entry)
+env = Environment()
+env.Append(BUILDERS = {'Collect':CollectBuilder})
+env.Collect('archive', ['directory_name', 'file_name'])
+.EE
+
+.IP emitter
+A function or list of functions to manipulate the target and source
+lists before dependencies are established
+and the target(s) are actually built.
+.B emitter
+can also be a string containing a construction variable to expand
+to an emitter function or list of functions,
+or a dictionary mapping source file suffixes
+to emitter functions.
+(Only the suffix of the first source file
+is used to select the actual emitter function
+from an emitter dictionary.)
+
+An emitter function
+takes three arguments:
+.I source
+- a list of source nodes,
+.I target
+- a list of target nodes,
+.I env
+- the construction environment.
+An emitter must return a tuple containing two lists,
+the list of targets to be built by this builder,
+and the list of sources for this builder.
+
+Example:
+
+.ES
+def e(target, source, env):
+ return (target + ['foo.foo'], source + ['foo.src'])
+
+# Simple association of an emitter function with a Builder.
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = e)
+
+def e2(target, source, env):
+ return (target + ['bar.foo'], source + ['bar.src'])
+
+# Simple association of a list of emitter functions with a Builder.
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = [e, e2])
+
+# Calling an emitter function through a construction variable.
+env = Environment(MY_EMITTER = e)
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = '$MY_EMITTER')
+
+# Calling a list of emitter functions through a construction variable.
+env = Environment(EMITTER_LIST = [e, e2])
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = '$EMITTER_LIST')
+
+# Associating multiple emitters with different file
+# suffixes using a dictionary.
+def e_suf1(target, source, env):
+ return (target + ['another_target_file'], source)
+def e_suf2(target, source, env):
+ return (target, source + ['another_source_file'])
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = {'.suf1' : e_suf1,
+ '.suf2' : e_suf2})
+.EE
+
+.IP multi
+Specifies whether this builder is allowed to be called multiple times for
+the same target file(s). The default is 0, which means the builder
+can not be called multiple times for the same target file(s). Calling a
+builder multiple times for the same target simply adds additional source
+files to the target; it is not allowed to change the environment associated
+with the target, specify addition environment overrides, or associate a different
+builder with the target.
+
+.IP env
+A construction environment that can be used
+to fetch source code using this Builder.
+(Note that this environment is
+.I not
+used for normal builds of normal target files,
+which use the environment that was
+used to call the Builder for the target file.)
+
+.IP generator
+A function that returns a list of actions that will be executed to build
+the target(s) from the source(s).
+The returned action(s) may be
+an Action object, or anything that
+can be converted into an Action object
+(see the next section).
+
+The generator function
+takes four arguments:
+.I source
+- a list of source nodes,
+.I target
+- a list of target nodes,
+.I env
+- the construction environment,
+.I for_signature
+- a Boolean value that specifies
+whether the generator is being called
+for generating a build signature
+(as opposed to actually executing the command).
+Example:
+
+.ES
+def g(source, target, env, for_signature):
+ return [["gcc", "-c", "-o"] + target + source]
+
+b = Builder(generator=g)
+.EE
+
+.IP
+The
+.I generator
+and
+.I action
+arguments must not both be used for the same Builder.
+
+.IP src_builder
+Specifies a builder to use when a source file name suffix does not match
+any of the suffixes of the builder. Using this argument produces a
+multi-stage builder.
+
+.IP single_source
+Specifies that this builder expects exactly one source file per call. Giving
+more than one source files without target files results in implicitely calling
+the builder multiple times (once for each source given). Giving multiple
+source files together with target files results in a UserError exception.
+
+.RE
+.IP
+The
+.I generator
+and
+.I action
+arguments must not both be used for the same Builder.
+
+.IP source_ext_match
+When the specified
+.I action
+argument is a dictionary,
+the default behavior when a builder is passed
+multiple source files is to make sure that the
+extensions of all the source files match.
+If it is legal for this builder to be
+called with a list of source files with different extensions,
+this check can be suppressed by setting
+.B source_ext_match
+to
+.B None
+or some other non-true value.
+When
+.B source_ext_match
+is disable,
+.B scons
+will use the suffix of the first specified
+source file to select the appropriate action from the
+.I action
+dictionary.
+
+In the following example,
+the setting of
+.B source_ext_match
+prevents
+.B scons
+from exiting with an error
+due to the mismatched suffixes of
+.B foo.in
+and
+.BR foo.extra .
+
+.ES
+b = Builder(action={'.in' : 'build $SOURCES > $TARGET'},
+ source_ext_match = None)
+
+env = Environment(BUILDERS = {'MyBuild':b})
+env.MyBuild('foo.out', ['foo.in', 'foo.extra'])
+.EE
+
+.IP env
+A construction environment that can be used
+to fetch source code using this Builder.
+(Note that this environment is
+.I not
+used for normal builds of normal target files,
+which use the environment that was
+used to call the Builder for the target file.)
+
+.ES
+b = Builder(action="build < $SOURCE > $TARGET")
+env = Environment(BUILDERS = {'MyBuild' : b})
+env.MyBuild('foo.out', 'foo.in', my_arg = 'xyzzy')
+.EE
+
+.IP chdir
+A directory from which scons
+will execute the
+action(s) specified
+for this Builder.
+If the
+.B chdir
+argument is
+a string or a directory Node,
+scons will change to the specified directory.
+If the
+.B chdir
+is not a string or Node
+and is non-zero,
+then scons will change to the
+target file's directory.
+
+Note that scons will
+.I not
+automatically modify
+its expansion of
+construction variables like
+.B $TARGET
+and
+.B $SOURCE
+when using the chdir
+keyword argument--that is,
+the expanded file names
+will still be relative to
+the top-level SConstruct directory,
+and consequently incorrect
+relative to the chdir directory.
+Builders created using chdir keyword argument,
+will need to use construction variable
+expansions like
+.B ${TARGET.file}
+and
+.B ${SOURCE.file}
+to use just the filename portion of the
+targets and source.
+
+.ES
+b = Builder(action="build < ${SOURCE.file} > ${TARGET.file}",
+ chdir=1)
+env = Environment(BUILDERS = {'MyBuild' : b})
+env.MyBuild('sub/dir/foo.out', 'sub/dir/foo.in')
+.EE
+
+.B WARNING:
+Python only keeps one current directory
+location for all of the threads.
+This means that use of the
+.B chdir
+argument
+will
+.I not
+work with the SCons
+.B -j
+option,
+because individual worker threads spawned
+by SCons interfere with each other
+when they start changing directory.
+
+.RE
+Any additional keyword arguments supplied
+when a Builder object is created
+(that is, when the Builder() function is called)
+will be set in the executing construction
+environment when the Builder object is called.
+The canonical example here would be
+to set a construction variable to
+the repository of a source code system.
+
+Any additional keyword arguments supplied
+when a Builder
+.I object
+is called
+will only be associated with the target
+created by that particular Builder call
+(and any other files built as a
+result of the call).
+
+These extra keyword arguments are passed to the
+following functions:
+command generator functions,
+function Actions,
+and emitter functions.
+
+.SS Action Objects
+
+The
+.BR Builder ()
+function will turn its
+.B action
+keyword argument into an appropriate
+internal Action object.
+You can also explicity create Action objects
+using the
+.BR Action ()
+global function,
+which can then be passed to the
+.BR Builder ()
+function.
+This can be used to configure
+an Action object more flexibly,
+or it may simply be more efficient
+than letting each separate Builder object
+create a separate Action
+when multiple
+Builder objects need to do the same thing.
+
+The
+.BR Action ()
+global function
+returns an appropriate object for the action
+represented by the type of the first argument:
+
+.IP Action
+If the first argument is already an Action object,
+the object is simply returned.
+
+.IP String
+If the first argument is a string,
+a command-line Action is returned.
+Note that the command-line string
+may be preceded by an
+.B @
+(at-sign)
+to suppress printing of the specified command line,
+or by a
+.B \-
+(hyphen)
+to ignore the exit status from the specified command:
+
+.ES
+Action('$CC -c -o $TARGET $SOURCES')
+
+# Doesn't print the line being executed.
+Action('@build $TARGET $SOURCES')
+
+# Ignores return value
+Action('-build $TARGET $SOURCES')
+.EE
+.\" XXX From Gary Ruben, 23 April 2002:
+.\" What would be useful is a discussion of how you execute command
+.\" shell commands ie. what is the process used to spawn the shell, pass
+.\" environment variables to it etc., whether there is one shell per
+.\" environment or one per command etc. It might help to look at the Gnu
+.\" make documentation to see what they think is important to discuss about
+.\" a build system. I'm sure you can do a better job of organising the
+.\" documentation than they have :-)
+
+.IP List
+If the first argument is a list,
+then a list of Action objects is returned.
+An Action object is created as necessary
+for each element in the list.
+If an element
+.I within
+the list is itself a list,
+the internal list is the
+command and arguments to be executed via
+the command line.
+This allows white space to be enclosed
+in an argument by defining
+a command in a list within a list:
+
+.ES
+Action([['cc', '-c', '-DWHITE SPACE', '-o', '$TARGET', '$SOURCES']])
+.EE
+
+.IP Function
+If the first argument is a Python function,
+a function Action is returned.
+The Python function must take three keyword arguments,
+.B target
+(a Node object representing the target file),
+.B source
+(a Node object representing the source file)
+and
+.B env
+(the construction environment
+used for building the target file).
+The
+.B target
+and
+.B source
+arguments may be lists of Node objects if there is
+more than one target file or source file.
+The actual target and source file name(s) may
+be retrieved from their Node objects
+via the built-in Python str() function:
+
+.ES
+target_file_name = str(target)
+source_file_names = map(lambda x: str(x), source)
+.EE
+.IP
+The function should return
+.B 0
+or
+.B None
+to indicate a successful build of the target file(s).
+The function may raise an exception
+or return a non-zero exit status
+to indicate an unsuccessful build.
+
+.ES
+def build_it(target = None, source = None, env = None):
+ # build the target from the source
+ return 0
+
+a = Action(build_it)
+.EE
+
+If the action argument is not one of the above,
+None is returned.
+.PP
+
+The second argument is optional and is used to define the output
+which is printed when the Action is actually performed.
+In the absence of this parameter,
+or if it's an empty string,
+a default output depending on the type of the action is used.
+For example, a command-line action will print the executed command.
+The argument must be either a Python function or a string.
+
+In the first case,
+it's a function that returns a string to be printed
+to describe the action being executed.
+The function may also be specified by the
+.IR strfunction =
+keyword argument.
+Like a function to build a file,
+this function must take three keyword arguments:
+.B target
+(a Node object representing the target file),
+.B source
+(a Node object representing the source file)
+and
+.BR env
+(a construction environment).
+The
+.B target
+and
+.B source
+arguments may be lists of Node objects if there is
+more than one target file or source file.
+
+In the second case, you provide the string itself.
+The string may also be specified by the
+.IR cmdstr =
+keyword argument.
+The string typically contains variables, notably
+$TARGET(S) and $SOURCE(S), or consists of just a single
+variable, which is optionally defined somewhere else.
+SCons itself heavily uses the latter variant.
+
+Examples:
+
+.ES
+def build_it(target, source, env):
+ # build the target from the source
+ return 0
+
+def string_it(target, source, env):
+ return "building '%s' from '%s'" % (target[0], source[0])
+
+# Use a positional argument.
+f = Action(build_it, string_it)
+s = Action(build_it, "building '$TARGET' from '$SOURCE'")
+
+# Alternatively, use a keyword argument.
+f = Action(build_it, strfunction=string_it)
+s = Action(build_it, cmdstr="building '$TARGET' from '$SOURCE'")
+
+# You can provide a configurable variable.
+l = Action(build_it, '$STRINGIT')
+.EE
+
+The third and succeeding arguments, if present,
+may either be a construction variable or a list of construction variables
+whose values will be included in the signature of the Action
+when deciding whether a target should be rebuilt because the action changed.
+The variables may also be specified by a
+.IR varlist =
+keyword parameter;
+if both are present, they are combined.
+This is necessary whenever you want a target to be rebuilt
+when a specific construction variable changes.
+This is not often needed for a string action,
+as the expanded variables will normally be part of the command line,
+but may be needed if a Python function action uses
+the value of a construction variable when generating the command line.
+
+.ES
+def build_it(target, source, env):
+ # build the target from the 'XXX' construction variable
+ open(target[0], 'w').write(env['XXX'])
+ return 0
+
+# Use positional arguments.
+a = Action(build_it, '$STRINGIT', ['XXX'])
+
+# Alternatively, use a keyword argument.
+a = Action(build_it, varlist=['XXX'])
+.EE
+
+The
+.BR Action ()
+global function
+can be passed the following
+optional keyword arguments
+to modify the Action object's behavior:
+
+.IP
+.B chdir
+The
+.B chdir
+keyword argument specifies that
+scons will execute the action
+after changing to the specified directory.
+If the
+.B chdir
+argument is
+a string or a directory Node,
+scons will change to the specified directory.
+If the
+.B chdir
+argument
+is not a string or Node
+and is non-zero,
+then scons will change to the
+target file's directory.
+
+Note that scons will
+.I not
+automatically modify
+its expansion of
+construction variables like
+.B $TARGET
+and
+.B $SOURCE
+when using the chdir
+keyword argument--that is,
+the expanded file names
+will still be relative to
+the top-level SConstruct directory,
+and consequently incorrect
+relative to the chdir directory.
+Builders created using chdir keyword argument,
+will need to use construction variable
+expansions like
+.B ${TARGET.file}
+and
+.B ${SOURCE.file}
+to use just the filename portion of the
+targets and source.
+
+.ES
+a = Action("build < ${SOURCE.file} > ${TARGET.file}",
+ chdir=1)
+.EE
+
+.IP
+.B exitstatfunc
+The
+.BR Action ()
+global function
+also takes an
+.B exitstatfunc
+keyword argument
+which specifies a function
+that is passed the exit status
+(or return value)
+from the specified action
+and can return an arbitrary
+or modified value.
+This can be used, for example,
+to specify that an Action object's
+return value should be ignored
+under special conditions
+and SCons should, therefore,
+consider that the action always suceeds:
+
+.ES
+def always_succeed(s):
+ # Always return 0, which indicates success.
+ return 0
+a = Action("build < ${SOURCE.file} > ${TARGET.file}",
+ exitstatfunc=always_succeed)
+.EE
+
+.IP
+.B batch_key
+The
+.B batch_key
+keyword argument can be used
+to specify that the Action can create multiple target files
+by processing multiple independent source files simultaneously.
+(The canonical example is "batch compilation"
+of multiple object files
+by passing multiple source files
+to a single invocation of a compiler
+such as Microsoft's Visual C / C++ compiler.)
+If the
+.B batch_key
+argument is any non-False, non-callable Python value,
+the configured Action object will cause
+.B scons
+to collect all targets built with the Action object
+and configured with the same construction environment
+into single invocations of the Action object's
+command line or function.
+Command lines will typically want to use the
+.BR CHANGED_SOURCES
+construction variable
+(and possibly
+.BR CHANGED_TARGETS
+as well)
+to only pass to the command line those sources that
+have actually changed since their targets were built.
+
+Example:
+
+.ES
+a = Action('build $CHANGED_SOURCES', batch_key=True)
+.EE
+
+The
+.B batch_key
+argument may also be
+a callable function
+that returns a key that
+will be used to identify different
+"batches" of target files to be collected
+for batch building.
+A
+.B batch_key
+function must take the following arguments:
+
+.IP action
+The action object.
+
+.IP env
+The construction environment
+configured for the target.
+
+.IP target
+The list of targets for a particular configured action.
+
+.IP source
+The list of source for a particular configured action.
+
+The returned key should typically
+be a tuple of values derived from the arguments,
+using any appropriate logic to decide
+how multiple invocations should be batched.
+For example, a
+.B batch_key
+function may decide to return
+the value of a specific construction
+variable from the
+.B env
+argument
+which will cause
+.B scons
+to batch-build targets
+with matching values of that variable,
+or perhaps return the
+.BR id ()
+of the entire construction environment,
+in which case
+.B scons
+will batch-build
+all targets configured with the same construction environment.
+Returning
+.B None
+indicates that
+the particular target should
+.I not
+be part of any batched build,
+but instead will be built
+by a separate invocation of action's
+command or function.
+Example:
+
+.ES
+def batch_key(action, env, target, source):
+ tdir = target[0].dir
+ if tdir.name == 'special':
+ # Don't batch-build any target
+ # in the special/ subdirectory.
+ return None
+ return (id(action), id(env), tdir)
+a = Action('build $CHANGED_SOURCES', batch_key=batch_key)
+.EE
+
+.SS Miscellaneous Action Functions
+
+.B scons
+supplies a number of functions
+that arrange for various common
+file and directory manipulations
+to be performed.
+These are similar in concept to "tasks" in the
+Ant build tool,
+although the implementation is slightly different.
+These functions do not actually
+perform the specified action
+at the time the function is called,
+but instead return an Action object
+that can be executed at the
+appropriate time.
+(In Object-Oriented terminology,
+these are actually
+Action
+.I Factory
+functions
+that return Action objects.)
+
+In practice,
+there are two natural ways
+that these
+Action Functions
+are intended to be used.
+
+First,
+if you need
+to perform the action
+at the time the SConscript
+file is being read,
+you can use the
+.B Execute
+global function to do so:
+.ES
+Execute(Touch('file'))
+.EE
+
+Second,
+you can use these functions
+to supply Actions in a list
+for use by the
+.B Command
+method.
+This can allow you to
+perform more complicated
+sequences of file manipulation
+without relying
+on platform-specific
+external commands:
+that
+.ES
+env = Environment(TMPBUILD = '/tmp/builddir')
+env.Command('foo.out', 'foo.in',
+ [Mkdir('$TMPBUILD'),
+ Copy('$TMPBUILD', '${SOURCE.dir}'),
+ "cd $TMPBUILD && make",
+ Delete('$TMPBUILD')])
+.EE
+
+.TP
+.RI Chmod( dest ", " mode )
+Returns an Action object that
+changes the permissions on the specified
+.I dest
+file or directory to the specified
+.IR mode .
+Examples:
+
+.ES
+Execute(Chmod('file', 0755))
+
+env.Command('foo.out', 'foo.in',
+ [Copy('$TARGET', '$SOURCE'),
+ Chmod('$TARGET', 0755)])
+.EE
+
+.TP
+.RI Copy( dest ", " src )
+Returns an Action object
+that will copy the
+.I src
+source file or directory to the
+.I dest
+destination file or directory.
+Examples:
+
+.ES
+Execute(Copy('foo.output', 'foo.input'))
+
+env.Command('bar.out', 'bar.in',
+ Copy('$TARGET', '$SOURCE'))
+.EE
+
+.TP
+.RI Delete( entry ", [" must_exist ])
+Returns an Action that
+deletes the specified
+.IR entry ,
+which may be a file or a directory tree.
+If a directory is specified,
+the entire directory tree
+will be removed.
+If the
+.I must_exist
+flag is set,
+then a Python error will be thrown
+if the specified entry does not exist;
+the default is
+.BR must_exist=0 ,
+that is, the Action will silently do nothing
+if the entry does not exist.
+Examples:
+
+.ES
+Execute(Delete('/tmp/buildroot'))
+
+env.Command('foo.out', 'foo.in',
+ [Delete('${TARGET.dir}'),
+ MyBuildAction])
+
+Execute(Delete('file_that_must_exist', must_exist=1))
+.EE
+
+.TP
+.RI Mkdir( dir )
+Returns an Action
+that creates the specified
+directory
+.I dir .
+Examples:
+
+.ES
+Execute(Mkdir('/tmp/outputdir'))
+
+env.Command('foo.out', 'foo.in',
+ [Mkdir('/tmp/builddir'),
+ Copy('/tmp/builddir/foo.in', '$SOURCE'),
+ "cd /tmp/builddir && make",
+ Copy('$TARGET', '/tmp/builddir/foo.out')])
+.EE
+
+.TP
+.RI Move( dest ", " src )
+Returns an Action
+that moves the specified
+.I src
+file or directory to
+the specified
+.I dest
+file or directory.
+Examples:
+
+.ES
+Execute(Move('file.destination', 'file.source'))
+
+env.Command('output_file', 'input_file',
+ [MyBuildAction,
+ Move('$TARGET', 'file_created_by_MyBuildAction')])
+.EE
+
+.TP
+.RI Touch( file )
+Returns an Action
+that updates the modification time
+on the specified
+.IR file .
+Examples:
+
+.ES
+Execute(Touch('file_to_be_touched'))
+
+env.Command('marker', 'input_file',
+ [MyBuildAction,
+ Touch('$TARGET')])
+.EE
+
+.SS Variable Substitution
+
+Before executing a command,
+.B scons
+performs construction variable interpolation on the strings that make up
+the command line of builders.
+Variables are introduced by a
+.B $
+prefix.
+Besides construction variables, scons provides the following
+variables for each command execution:
+
+.IP CHANGED_SOURCES
+The file names of all sources of the build command
+that have changed since the target was last built.
+
+.IP CHANGED_TARGETS
+The file names of all targets that would be built
+from sources that have changed since the target was last built.
+
+.IP SOURCE
+The file name of the source of the build command,
+or the file name of the first source
+if multiple sources are being built.
+
+.IP SOURCES
+The file names of the sources of the build command.
+
+.IP TARGET
+The file name of the target being built,
+or the file name of the first target
+if multiple targets are being built.
+
+.IP TARGETS
+The file names of all targets being built.
+
+.IP UNCHANGED_SOURCES
+The file names of all sources of the build command
+that have
+.I not
+changed since the target was last built.
+
+.IP UNCHANGED_TARGETS
+The file names of all targets that would be built
+from sources that have
+.I not
+changed since the target was last built.
+
+(Note that the above variables are reserved
+and may not be set in a construction environment.)
+
+.LP
+For example, given the construction variable CC='cc', targets=['foo'], and
+sources=['foo.c', 'bar.c']:
+
+.ES
+action='$CC -c -o $TARGET $SOURCES'
+.EE
+
+would produce the command line:
+
+.ES
+cc -c -o foo foo.c bar.c
+.EE
+
+Variable names may be surrounded by curly braces ({})
+to separate the name from the trailing characters.
+Within the curly braces, a variable name may have
+a Python slice subscript appended to select one
+or more items from a list.
+In the previous example, the string:
+
+.ES
+${SOURCES[1]}
+.EE
+
+would produce:
+
+.ES
+bar.c
+.EE
+
+Additionally, a variable name may
+have the following special
+modifiers appended within the enclosing curly braces
+to modify the interpolated string:
+
+.IP base
+The base path of the file name,
+including the directory path
+but excluding any suffix.
+
+.IP dir
+The name of the directory in which the file exists.
+
+.IP file
+The file name,
+minus any directory portion.
+
+.IP filebase
+Just the basename of the file,
+minus any suffix
+and minus the directory.
+
+.IP suffix
+Just the file suffix.
+
+.IP abspath
+The absolute path name of the file.
+
+.IP posix
+The POSIX form of the path,
+with directories separated by
+.B /
+(forward slashes)
+not backslashes.
+This is sometimes necessary on Windows systems
+when a path references a file on other (POSIX) systems.
+
+.IP srcpath
+The directory and file name to the source file linked to this file through
+.BR VariantDir ().
+If this file isn't linked,
+it just returns the directory and filename unchanged.
+
+.IP srcdir
+The directory containing the source file linked to this file through
+.BR VariantDir ().
+If this file isn't linked,
+it just returns the directory part of the filename.
+
+.IP rsrcpath
+The directory and file name to the source file linked to this file through
+.BR VariantDir ().
+If the file does not exist locally but exists in a Repository,
+the path in the Repository is returned.
+If this file isn't linked, it just returns the
+directory and filename unchanged.
+
+.IP rsrcdir
+The Repository directory containing the source file linked to this file through
+.BR VariantDir ().
+If this file isn't linked,
+it just returns the directory part of the filename.
+
+.LP
+For example, the specified target will
+expand as follows for the corresponding modifiers:
+
+.ES
+$TARGET => sub/dir/file.x
+${TARGET.base} => sub/dir/file
+${TARGET.dir} => sub/dir
+${TARGET.file} => file.x
+${TARGET.filebase} => file
+${TARGET.suffix} => .x
+${TARGET.abspath} => /top/dir/sub/dir/file.x
+
+SConscript('src/SConscript', variant_dir='sub/dir')
+$SOURCE => sub/dir/file.x
+${SOURCE.srcpath} => src/file.x
+${SOURCE.srcdir} => src
+
+Repository('/usr/repository')
+$SOURCE => sub/dir/file.x
+${SOURCE.rsrcpath} => /usr/repository/src/file.x
+${SOURCE.rsrcdir} => /usr/repository/src
+.EE
+
+Note that curly braces braces may also be used
+to enclose arbitrary Python code to be evaluated.
+(In fact, this is how the above modifiers are substituted,
+they are simply attributes of the Python objects
+that represent TARGET, SOURCES, etc.)
+See the section "Python Code Substitution," below,
+for more thorough examples of
+how this can be used.
+
+Lastly, a variable name
+may be a callable Python function
+associated with a
+construction variable in the environment.
+The function should
+take four arguments:
+.I target
+- a list of target nodes,
+.I source
+- a list of source nodes,
+.I env
+- the construction environment,
+.I for_signature
+- a Boolean value that specifies
+whether the function is being called
+for generating a build signature.
+SCons will insert whatever
+the called function returns
+into the expanded string:
+
+.ES
+def foo(target, source, env, for_signature):
+ return "bar"
+
+# Will expand $BAR to "bar baz"
+env=Environment(FOO=foo, BAR="$FOO baz")
+.EE
+
+You can use this feature to pass arguments to a
+Python function by creating a callable class
+that stores one or more arguments in an object,
+and then uses them when the
+.B __call__()
+method is called.
+Note that in this case,
+the entire variable expansion must
+be enclosed by curly braces
+so that the arguments will
+be associated with the
+instantiation of the class:
+
+.ES
+class foo:
+ def __init__(self, arg):
+ self.arg = arg
+
+ def __call__(self, target, source, env, for_signature):
+ return self.arg + " bar"
+
+# Will expand $BAR to "my argument bar baz"
+env=Environment(FOO=foo, BAR="${FOO('my argument')} baz")
+.EE
+
+.LP
+The special pseudo-variables
+.B "$("
+and
+.B "$)"
+may be used to surround parts of a command line
+that may change
+.I without
+causing a rebuild--that is,
+which are not included in the signature
+of target files built with this command.
+All text between
+.B "$("
+and
+.B "$)"
+will be removed from the command line
+before it is added to file signatures,
+and the
+.B "$("
+and
+.B "$)"
+will be removed before the command is executed.
+For example, the command line:
+
+.ES
+echo Last build occurred $( $TODAY $). > $TARGET
+.EE
+
+.LP
+would execute the command:
+
+.ES
+echo Last build occurred $TODAY. > $TARGET
+.EE
+
+.LP
+but the command signature added to any target files would be:
+
+.ES
+echo Last build occurred . > $TARGET
+.EE
+
+.SS Python Code Substitution
+
+Any python code within
+.BR "${" - "}"
+pairs gets evaluated by python 'eval', with the python globals set to
+the current environment's set of construction variables.
+So in the following case:
+.ES
+env['COND'] = 0
+env.Command('foo.out', 'foo.in',
+ '''echo ${COND==1 and 'FOO' or 'BAR'} > $TARGET''')
+.EE
+the command executed will be either
+.ES
+echo FOO > foo.out
+.EE
+or
+.ES
+echo BAR > foo.out
+.EE
+according to the current value of env['COND'] when the command is
+executed. The evaluation occurs when the target is being
+built, not when the SConscript is being read. So if env['COND'] is changed
+later in the SConscript, the final value will be used.
+
+Here's a more interesting example. Note that all of COND, FOO, and
+BAR are environment variables, and their values are substituted into
+the final command. FOO is a list, so its elements are interpolated
+separated by spaces.
+
+.ES
+env=Environment()
+env['COND'] = 0
+env['FOO'] = ['foo1', 'foo2']
+env['BAR'] = 'barbar'
+env.Command('foo.out', 'foo.in',
+ 'echo ${COND==1 and FOO or BAR} > $TARGET')
+
+# Will execute this:
+# echo foo1 foo2 > foo.out
+.EE
+
+SCons uses the following rules when converting construction variables into
+command lines:
+
+.IP String
+When the value is a string it is interpreted as a space delimited list of
+command line arguments.
+
+.IP List
+When the value is a list it is interpreted as a list of command line
+arguments. Each element of the list is converted to a string.
+
+.IP Other
+Anything that is not a list or string is converted to a string and
+interpreted as a single command line argument.
+
+.IP Newline
+Newline characters (\\n) delimit lines. The newline parsing is done after
+all other parsing, so it is not possible for arguments (e.g. file names) to
+contain embedded newline characters. This limitation will likely go away in
+a future version of SCons.
+
+.SS Scanner Objects
+
+You can use the
+.B Scanner
+function to define
+objects to scan
+new file types for implicit dependencies.
+Scanner accepts the following arguments:
+
+.IP function
+This can be either:
+1) a Python function that will process
+the Node (file)
+and return a list of strings (file names)
+representing the implicit
+dependencies found in the contents;
+or:
+2) a dictionary that maps keys
+(typically the file suffix, but see below for more discussion)
+to other Scanners that should be called.
+
+If the argument is actually a Python function,
+the function must take three or four arguments:
+
+ def scanner_function(node, env, path):
+
+ def scanner_function(node, env, path, arg=None):
+
+The
+.B node
+argument is the internal
+SCons node representing the file.
+Use
+.B str(node)
+to fetch the name of the file, and
+.B node.get_contents()
+to fetch contents of the file.
+Note that the file is
+.I not
+guaranteed to exist before the scanner is called,
+so the scanner function should check that
+if there's any chance that the scanned file
+might not exist
+(for example, if it's built from other files).
+
+The
+.B env
+argument is the construction environment for the scan.
+Fetch values from it using the
+.B env.Dictionary()
+method.
+
+The
+.B path
+argument is a tuple (or list)
+of directories that can be searched
+for files.
+This will usually be the tuple returned by the
+.B path_function
+argument (see below).
+
+The
+.B arg
+argument is the argument supplied
+when the scanner was created, if any.
+
+.IP name
+The name of the Scanner.
+This is mainly used
+to identify the Scanner internally.
+
+.IP argument
+An optional argument that, if specified,
+will be passed to the scanner function
+(described above)
+and the path function
+(specified below).
+
+.IP skeys
+An optional list that can be used to
+determine which scanner should be used for
+a given Node.
+In the usual case of scanning for file names,
+this argument will be a list of suffixes
+for the different file types that this
+Scanner knows how to scan.
+If the argument is a string,
+then it will be expanded
+into a list by the current environment.
+
+.IP path_function
+A Python function that takes four or five arguments:
+a construction environment,
+a Node for the directory containing
+the SConscript file in which
+the first target was defined,
+a list of target nodes,
+a list of source nodes,
+and an optional argument supplied
+when the scanner was created.
+The
+.B path_function
+returns a tuple of directories
+that can be searched for files to be returned
+by this Scanner object.
+(Note that the
+.BR FindPathDirs ()
+function can be used to return a ready-made
+.B path_function
+for a given construction variable name,
+instead of having to write your own function from scratch.)
+
+.IP node_class
+The class of Node that should be returned
+by this Scanner object.
+Any strings or other objects returned
+by the scanner function
+that are not of this class
+will be run through the
+.B node_factory
+function.
+
+.IP node_factory
+A Python function that will take a string
+or other object
+and turn it into the appropriate class of Node
+to be returned by this Scanner object.
+
+.IP scan_check
+An optional Python function that takes two arguments,
+a Node (file) and a construction environment,
+and returns whether the
+Node should, in fact,
+be scanned for dependencies.
+This check can be used to eliminate unnecessary
+calls to the scanner function when,
+for example, the underlying file
+represented by a Node does not yet exist.
+
+.IP recursive
+An optional flag that
+specifies whether this scanner should be re-invoked
+on the dependency files returned by the scanner.
+When this flag is not set,
+the Node subsystem will
+only invoke the scanner on the file being scanned,
+and not (for example) also on the files
+specified by the #include lines
+in the file being scanned.
+.I recursive
+may be a callable function,
+in which case it will be called with a list of
+Nodes found and
+should return a list of Nodes
+that should be scanned recursively;
+this can be used to select a specific subset of
+Nodes for additional scanning.
+
+Note that
+.B scons
+has a global
+.B SourceFileScanner
+object that is used by
+the
+.BR Object (),
+.BR SharedObject (),
+and
+.BR StaticObject ()
+builders to decide
+which scanner should be used
+for different file extensions.
+You can using the
+.BR SourceFileScanner.add_scanner ()
+method to add your own Scanner object
+to the
+.B scons
+infrastructure
+that builds target programs or
+libraries from a list of
+source files of different types:
+
+.ES
+def xyz_scan(node, env, path):
+ contents = node.get_text_contents()
+ # Scan the contents and return the included files.
+
+XYZScanner = Scanner(xyz_scan)
+
+SourceFileScanner.add_scanner('.xyx', XYZScanner)
+
+env.Program('my_prog', ['file1.c', 'file2.f', 'file3.xyz'])
+.EE
+
+.SH SYSTEM-SPECIFIC BEHAVIOR
+SCons and its configuration files are very portable,
+due largely to its implementation in Python.
+There are, however, a few portability
+issues waiting to trap the unwary.
+.SS .C file suffix
+SCons handles the upper-case
+.B .C
+file suffix differently,
+depending on the capabilities of
+the underlying system.
+On a case-sensitive system
+such as Linux or UNIX,
+SCons treats a file with a
+.B .C
+suffix as a C++ source file.
+On a case-insensitive system
+such as Windows,
+SCons treats a file with a
+.B .C
+suffix as a C source file.
+.SS .F file suffix
+SCons handles the upper-case
+.B .F
+file suffix differently,
+depending on the capabilities of
+the underlying system.
+On a case-sensitive system
+such as Linux or UNIX,
+SCons treats a file with a
+.B .F
+suffix as a Fortran source file
+that is to be first run through
+the standard C preprocessor.
+On a case-insensitive system
+such as Windows,
+SCons treats a file with a
+.B .F
+suffix as a Fortran source file that should
+.I not
+be run through the C preprocessor.
+.SS Windows: Cygwin Tools and Cygwin Python vs. Windows Pythons
+Cygwin supplies a set of tools and utilities
+that let users work on a
+Windows system using a more POSIX-like environment.
+The Cygwin tools, including Cygwin Python,
+do this, in part,
+by sharing an ability to interpret UNIX-like path names.
+For example, the Cygwin tools
+will internally translate a Cygwin path name
+like /cygdrive/c/mydir
+to an equivalent Windows pathname
+of C:/mydir (equivalent to C:\\mydir).
+
+Versions of Python
+that are built for native Windows execution,
+such as the python.org and ActiveState versions,
+do not have the Cygwin path name semantics.
+This means that using a native Windows version of Python
+to build compiled programs using Cygwin tools
+(such as gcc, bison, and flex)
+may yield unpredictable results.
+"Mixing and matching" in this way
+can be made to work,
+but it requires careful attention to the use of path names
+in your SConscript files.
+
+In practice, users can sidestep
+the issue by adopting the following rules:
+When using gcc,
+use the Cygwin-supplied Python interpreter
+to run SCons;
+when using Microsoft Visual C/C++
+(or some other Windows compiler)
+use the python.org or ActiveState version of Python
+to run SCons.
+.SS Windows: scons.bat file
+On Windows systems,
+SCons is executed via a wrapper
+.B scons.bat
+file.
+This has (at least) two ramifications:
+
+First, Windows command-line users
+that want to use variable assignment
+on the command line
+may have to put double quotes
+around the assignments:
+
+.ES
+scons "FOO=BAR" "BAZ=BLEH"
+.EE
+
+Second, the Cygwin shell does not
+recognize this file as being the same
+as an
+.B scons
+command issued at the command-line prompt.
+You can work around this either by
+executing
+.B scons.bat
+from the Cygwin command line,
+or by creating a wrapper shell
+script named
+.B scons .
+
+.SS MinGW
+
+The MinGW bin directory must be in your PATH environment variable or the
+PATH variable under the ENV construction variable for SCons
+to detect and use the MinGW tools. When running under the native Windows
+Python interpreter, SCons will prefer the MinGW tools over the Cygwin
+tools, if they are both installed, regardless of the order of the bin
+directories in the PATH variable. If you have both MSVC and MinGW
+installed and you want to use MinGW instead of MSVC,
+then you must explictly tell SCons to use MinGW by passing
+
+.ES
+tools=['mingw']
+.EE
+
+to the Environment() function, because SCons will prefer the MSVC tools
+over the MinGW tools.
+
+.SH EXAMPLES
+
+To help you get started using SCons,
+this section contains a brief overview of some common tasks.
+
+.SS Basic Compilation From a Single Source File
+
+.ES
+env = Environment()
+env.Program(target = 'foo', source = 'foo.c')
+.EE
+
+Note: Build the file by specifying
+the target as an argument
+("scons foo" or "scons foo.exe").
+or by specifying a dot ("scons .").
+
+.SS Basic Compilation From Multiple Source Files
+
+.ES
+env = Environment()
+env.Program(target = 'foo', source = Split('f1.c f2.c f3.c'))
+.EE
+
+.SS Setting a Compilation Flag
+
+.ES
+env = Environment(CCFLAGS = '-g')
+env.Program(target = 'foo', source = 'foo.c')
+.EE
+
+.SS Search The Local Directory For .h Files
+
+Note: You do
+.I not
+need to set CCFLAGS to specify -I options by hand.
+SCons will construct the right -I options from CPPPATH.
+
+.ES
+env = Environment(CPPPATH = ['.'])
+env.Program(target = 'foo', source = 'foo.c')
+.EE
+
+.SS Search Multiple Directories For .h Files
+
+.ES
+env = Environment(CPPPATH = ['include1', 'include2'])
+env.Program(target = 'foo', source = 'foo.c')
+.EE
+
+.SS Building a Static Library
+
+.ES
+env = Environment()
+env.StaticLibrary(target = 'foo', source = Split('l1.c l2.c'))
+env.StaticLibrary(target = 'bar', source = ['l3.c', 'l4.c'])
+.EE
+
+.SS Building a Shared Library
+
+.ES
+env = Environment()
+env.SharedLibrary(target = 'foo', source = ['l5.c', 'l6.c'])
+env.SharedLibrary(target = 'bar', source = Split('l7.c l8.c'))
+.EE
+
+.SS Linking a Local Library Into a Program
+
+.ES
+env = Environment(LIBS = 'mylib', LIBPATH = ['.'])
+env.Library(target = 'mylib', source = Split('l1.c l2.c'))
+env.Program(target = 'prog', source = ['p1.c', 'p2.c'])
+.EE
+
+.SS Defining Your Own Builder Object
+
+Notice that when you invoke the Builder,
+you can leave off the target file suffix,
+and SCons will add it automatically.
+
+.ES
+bld = Builder(action = 'pdftex < $SOURCES > $TARGET'
+ suffix = '.pdf',
+ src_suffix = '.tex')
+env = Environment(BUILDERS = {'PDFBuilder' : bld})
+env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex')
+
+# The following creates "bar.pdf" from "bar.tex"
+env.PDFBuilder(target = 'bar', source = 'bar')
+.EE
+
+Note also that the above initialization
+overwrites the default Builder objects,
+so the Environment created above
+can not be used call Builders like env.Program(),
+env.Object(), env.StaticLibrary(), etc.
+
+.SS Adding Your Own Builder Object to an Environment
+
+.ES
+bld = Builder(action = 'pdftex < $SOURCES > $TARGET'
+ suffix = '.pdf',
+ src_suffix = '.tex')
+env = Environment()
+env.Append(BUILDERS = {'PDFBuilder' : bld})
+env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex')
+env.Program(target = 'bar', source = 'bar.c')
+.EE
+
+You also can use other Pythonic techniques to add
+to the BUILDERS construction variable, such as:
+
+.ES
+env = Environment()
+env['BUILDERS]['PDFBuilder'] = bld
+.EE
+
+.SS Defining Your Own Scanner Object
+
+The following example shows an extremely simple scanner (the
+.BR kfile_scan ()
+function)
+that doesn't use a search path at all
+and simply returns the
+file names present on any
+.B include
+lines in the scanned file.
+This would implicitly assume that all included
+files live in the top-level directory:
+
+.ES
+import re
+
+'\" Note: the \\ in the following are for the benefit of nroff/troff,
+'\" not inappropriate doubled escape characters within the r'' raw string.
+include_re = re.compile(r'^include\\s+(\\S+)$', re.M)
+
+def kfile_scan(node, env, path, arg):
+ contents = node.get_text_contents()
+ includes = include_re.findall(contents)
+ return includes
+
+kscan = Scanner(name = 'kfile',
+ function = kfile_scan,
+ argument = None,
+ skeys = ['.k'])
+scanners = Environment().Dictionary('SCANNERS')
+env = Environment(SCANNERS = scanners + [kscan])
+
+env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET')
+
+bar_in = File('bar.in')
+env.Command('bar', bar_in, 'kprocess $SOURCES > $TARGET')
+bar_in.target_scanner = kscan
+.EE
+
+Here is a similar but more complete example that searches
+a path of directories
+(specified as the
+.B MYPATH
+construction variable)
+for files that actually exist:
+
+.ES
+include_re = re.compile(r'^include\\s+(\\S+)$', re.M)
+
+def my_scan(node, env, path, arg):
+ contents = node.get_text_contents()
+ includes = include_re.findall(contents)
+ if includes == []:
+ return []
+ results = []
+ for inc in includes:
+ for dir in path:
+ file = dir + os.sep + inc
+ if os.path.exists(file):
+ results.append(file)
+ break
+ return results
+
+scanner = Scanner(name = 'myscanner',
+ function = my_scan,
+ argument = None,
+ skeys = ['.x'],
+ path_function = FindPathDirs('MYPATH'),
+ )
+scanners = Environment().Dictionary('SCANNERS')
+env = Environment(SCANNERS = scanners + [scanner])
+.EE
+
+The
+.BR FindPathDirs ()
+function used in the previous example returns a function
+(actually a callable Python object)
+that will return a list of directories
+specified in the
+.B $MYPATH
+construction variable.
+If you need to customize how the search path is derived,
+you would provide your own
+.B path_function
+argument when creating the Scanner object,
+as follows:
+
+.ES
+# MYPATH is a list of directories to search for files in
+def pf(env, dir, target, source, arg):
+ top_dir = Dir('#').abspath
+ results = []
+ if env.has_key('MYPATH'):
+ for p in env['MYPATH']:
+ results.append(top_dir + os.sep + p)
+ return results
+
+scanner = Scanner(name = 'myscanner',
+ function = my_scan,
+ argument = None,
+ skeys = ['.x'],
+ path_function = pf,
+ )
+.EE
+
+.SS Creating a Hierarchical Build
+
+Notice that the file names specified in a subdirectory's
+SConscript
+file are relative to that subdirectory.
+
+.ES
+SConstruct:
+
+ env = Environment()
+ env.Program(target = 'foo', source = 'foo.c')
+
+ SConscript('sub/SConscript')
+
+sub/SConscript:
+
+ env = Environment()
+ # Builds sub/foo from sub/foo.c
+ env.Program(target = 'foo', source = 'foo.c')
+
+ SConscript('dir/SConscript')
+
+sub/dir/SConscript:
+
+ env = Environment()
+ # Builds sub/dir/foo from sub/dir/foo.c
+ env.Program(target = 'foo', source = 'foo.c')
+.EE
+
+.SS Sharing Variables Between SConscript Files
+
+You must explicitly Export() and Import() variables that
+you want to share between SConscript files.
+
+.ES
+SConstruct:
+
+ env = Environment()
+ env.Program(target = 'foo', source = 'foo.c')
+
+ Export("env")
+ SConscript('subdirectory/SConscript')
+
+subdirectory/SConscript:
+
+ Import("env")
+ env.Program(target = 'foo', source = 'foo.c')
+.EE
+
+.SS Building Multiple Variants From the Same Source
+
+Use the variant_dir keyword argument to
+the SConscript function to establish
+one or more separate variant build directory trees
+for a given source directory:
+
+.ES
+SConstruct:
+
+ cppdefines = ['FOO']
+ Export("cppdefines")
+ SConscript('src/SConscript', variant_dir='foo')
+
+ cppdefines = ['BAR']
+ Export("cppdefines")
+ SConscript('src/SConscript', variant_dir='bar')
+
+src/SConscript:
+
+ Import("cppdefines")
+ env = Environment(CPPDEFINES = cppdefines)
+ env.Program(target = 'src', source = 'src.c')
+.EE
+
+Note the use of the Export() method
+to set the "cppdefines" variable to a different
+value each time we call the SConscript function.
+
+.SS Hierarchical Build of Two Libraries Linked With a Program
+
+.ES
+SConstruct:
+
+ env = Environment(LIBPATH = ['#libA', '#libB'])
+ Export('env')
+ SConscript('libA/SConscript')
+ SConscript('libB/SConscript')
+ SConscript('Main/SConscript')
+
+libA/SConscript:
+
+ Import('env')
+ env.Library('a', Split('a1.c a2.c a3.c'))
+
+libB/SConscript:
+
+ Import('env')
+ env.Library('b', Split('b1.c b2.c b3.c'))
+
+Main/SConscript:
+
+ Import('env')
+ e = env.Copy(LIBS = ['a', 'b'])
+ e.Program('foo', Split('m1.c m2.c m3.c'))
+.EE
+
+The '#' in the LIBPATH directories specify that they're relative to the
+top-level directory, so they don't turn into "Main/libA" when they're
+used in Main/SConscript.
+
+Specifying only 'a' and 'b' for the library names
+allows SCons to append the appropriate library
+prefix and suffix for the current platform
+(for example, 'liba.a' on POSIX systems,
+\&'a.lib' on Windows).
+
+.SS Customizing construction variables from the command line.
+
+The following would allow the C compiler to be specified on the command
+line or in the file custom.py.
+
+.ES
+vars = Variables('custom.py')
+vars.Add('CC', 'The C compiler.')
+env = Environment(variables=vars)
+Help(vars.GenerateHelpText(env))
+.EE
+
+The user could specify the C compiler on the command line:
+
+.ES
+scons "CC=my_cc"
+.EE
+
+or in the custom.py file:
+
+.ES
+CC = 'my_cc'
+.EE
+
+or get documentation on the options:
+
+.ES
+$ scons -h
+
+CC: The C compiler.
+ default: None
+ actual: cc
+
+.EE
+
+.SS Using Microsoft Visual C++ precompiled headers
+
+Since windows.h includes everything and the kitchen sink, it can take quite
+some time to compile it over and over again for a bunch of object files, so
+Microsoft provides a mechanism to compile a set of headers once and then
+include the previously compiled headers in any object file. This
+technology is called precompiled headers. The general recipe is to create a
+file named "StdAfx.cpp" that includes a single header named "StdAfx.h", and
+then include every header you want to precompile in "StdAfx.h", and finally
+include "StdAfx.h" as the first header in all the source files you are
+compiling to object files. For example:
+
+StdAfx.h:
+.ES
+#include <windows.h>
+#include <my_big_header.h>
+.EE
+
+StdAfx.cpp:
+.ES
+#include <StdAfx.h>
+.EE
+
+Foo.cpp:
+.ES
+#include <StdAfx.h>
+
+/* do some stuff */
+.EE
+
+Bar.cpp:
+.ES
+#include <StdAfx.h>
+
+/* do some other stuff */
+.EE
+
+SConstruct:
+.ES
+env=Environment()
+env['PCHSTOP'] = 'StdAfx.h'
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
+.EE
+
+For more information see the document for the PCH builder, and the PCH and
+PCHSTOP construction variables. To learn about the details of precompiled
+headers consult the MSDN documention for /Yc, /Yu, and /Yp.
+
+.SS Using Microsoft Visual C++ external debugging information
+
+Since including debugging information in programs and shared libraries can
+cause their size to increase significantly, Microsoft provides a mechanism
+for including the debugging information in an external file called a PDB
+file. SCons supports PDB files through the PDB construction
+variable.
+
+SConstruct:
+.ES
+env=Environment()
+env['PDB'] = 'MyApp.pdb'
+env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
+.EE
+
+For more information see the document for the PDB construction variable.
+
+.SH ENVIRONMENT
+
+.IP SCONS_LIB_DIR
+Specifies the directory that contains the SCons Python module directory
+(e.g. /home/aroach/scons-src-0.01/src/engine).
+
+.IP SCONSFLAGS
+A string of options that will be used by scons in addition to those passed
+on the command line.
+
+.SH "SEE ALSO"
+.B scons
+User Manual,
+.B scons
+Design Document,
+.B scons
+source code.
+
+.SH AUTHORS
+Steven Knight <knight@baldmt.com>
+.br
+Anthony Roach <aroach@electriceyeball.com>
+
diff --git a/doc/man/sconsign.1 b/doc/man/sconsign.1
new file mode 100644
index 0000000..6694784
--- /dev/null
+++ b/doc/man/sconsign.1
@@ -0,0 +1,208 @@
+.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation the rights to use, copy, modify, merge, publish,
+.\" distribute, sublicense, and/or sell copies of the Software, and to
+.\" permit persons to whom the Software is furnished to do so, subject to
+.\" the following conditions:
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" doc/man/sconsign.1 4577 2009/12/27 19:44:43 scons
+.\"
+.\" ES - Example Start - indents and turns off line fill
+.de ES
+.RS
+.nf
+..
+.\" EE - Example End - ends indent and turns line fill back on
+.de EE
+.RE
+.fi
+..
+.TH SCONSIGN 1 "December 2009"
+.SH NAME
+sconsign \- print SCons .sconsign file information
+.SH SYNOPSIS
+.B sconsign
+[
+.IR options ...
+]
+.IR file
+[ ... ]
+.SH DESCRIPTION
+
+The
+.B sconsign
+command
+displays the contents of one or more
+.B .sconsign
+files specified by the user.
+
+By default,
+.B sconsign
+dumps the entire contents of the
+specified file(s).
+Each entry is printed in the following format:
+
+ file: signature timestamp length
+ implicit_dependency_1: signature timestamp length
+ implicit_dependency_2: signature timestamp length
+ action_signature [action string]
+
+.B None
+is printed
+in place of any missing timestamp, bsig, or csig
+values for
+any entry
+or any of its dependencies.
+If the entry has no implicit dependencies,
+or no build action,
+the lines are simply omitted.
+
+By default,
+.B sconsign
+assumes that any
+.I file
+arguments that end with a
+.B .dbm
+suffix contains
+signature entries for
+more than one directory
+(that is,
+was specified by the
+.B SConsignFile ()
+function).
+Any
+.I file
+argument that does not end in
+.B .dbm
+is assumed to be a traditional
+.B .sconsign
+file containing the signature entries
+for a single directory.
+An explicit format
+may be specified using the
+.B -f
+or
+.B --file=
+options.
+
+.SH OPTIONS
+
+Various options control what information is printed
+and the format:
+
+.TP
+-a, --act, --action
+Prints the build action information
+for all entries or the specified entries.
+
+.TP
+-c, --csig
+Prints the content signature (csig) information
+for all entries or the specified entries.
+
+.TP
+-d DIRECTORY, --dir=DIRECTORY
+When the signatures are being
+read from a
+.B .dbm
+file, or the
+.B -f dbm
+or
+.B --format=dbm
+options are used,
+prints information about
+only the signatures
+for entries in the specified
+.IR DIRECTORY .
+
+.TP
+-e ENTRY, --entry=ENTRY
+Prints information about only the specified
+.IR ENTRY .
+Multiple -e options may be used,
+in which case information about each
+.I ENTRY
+is printed in the order in which the
+options are specified on the command line.
+
+.TP
+-f FORMAT, --format=FORMAT
+The file(s) to be printed
+are in the specified
+.IR FORMAT .
+Legal values are
+.B dbm
+(the DBM format used
+when the
+.BR SConsignFile ()
+function is used)
+or
+.B sconsign
+(the default format
+used for an individual
+.B .sconsign
+file in each directory).
+
+.TP
+-h, --help
+Prints a help message and exits.
+
+.TP
+-i, --implicit
+Prints the list of cached implicit dependencies
+for all entries or the the specified entries.
+
+.TP
+--raw
+Prints a pretty-printed representation
+of the raw Python dictionary that holds
+build information about individual entry
+(both the entry itself or its implicit dependencies).
+An entry's build action is still printed in its usual format.
+
+.TP
+-r, --readable
+Prints timestamps in a human-readable string,
+enclosed in single quotes.
+
+.TP
+-t, --timestamp
+Prints the timestamp information
+for all entries or the specified entries.
+
+.TP
+-v, --verbose
+Prints labels identifying each field being printed.
+
+.SH ENVIRONMENT
+
+.IP SCONS_LIB_DIR
+Specifies the directory that contains the SCons Python module directory
+(e.g. /home/aroach/scons-src-0.01/src/engine).
+on the command line.
+
+.SH "SEE ALSO"
+.BR scons ,
+.B scons
+User Manual,
+.B scons
+Design Document,
+.B scons
+source code.
+
+.SH AUTHORS
+Steven Knight <knight at baldmt dot com>
diff --git a/doc/python10/MANIFEST b/doc/python10/MANIFEST
new file mode 100644
index 0000000..c9484d8
--- /dev/null
+++ b/doc/python10/MANIFEST
@@ -0,0 +1,16 @@
+abstract.xml
+acks.xml
+arch.fig
+builder.fig
+copyright.xml
+design.xml
+future.xml
+install.xml
+intro.xml
+job-task.fig
+main.xml
+node.fig
+process.xml
+scanner.fig
+scons.mod
+sig.fig
diff --git a/doc/python10/abstract.xml b/doc/python10/abstract.xml
new file mode 100644
index 0000000..294180b
--- /dev/null
+++ b/doc/python10/abstract.xml
@@ -0,0 +1,32 @@
+<para>
+
+ &SCons; is a software construction tool (build tool, or make tool)
+ implemented in Python, which uses Python scripts as "configuration
+ files" for software builds. Based on the design which won the
+ Software Carpentry build tool competition, &SCons solves a number of
+ problems associated with other build tools, especially including the
+ classic and ubiquitous &Make; itself.
+
+</para>
+
+<para>
+
+ Distinctive features of &SCons; include: a modular design that
+ lends itself to being embedded in other applications; a global
+ view of all dependencies in the source tree; an improved model for
+ parallel (<option>-j</option>) builds; automatic scanning of files for
+ dependencies; use of MD5 signatures for deciding whether a file
+ is up-to-date; use of traditional file timestamps instead of
+ MD5 signatures available as an option;
+ use of Python functions or objects to build target files; easy user
+ extensibility.
+
+</para>
+
+<para>
+
+ This paper discusses the goals of the &SCons; project, gives an overview
+ of the design of &SCons; itself, describes the development process used,
+ and discusses future plans and directions for the tool.
+
+</para>
diff --git a/doc/python10/acks.xml b/doc/python10/acks.xml
new file mode 100644
index 0000000..895bad7
--- /dev/null
+++ b/doc/python10/acks.xml
@@ -0,0 +1,27 @@
+<para>
+
+ First, many thanks to the great group of developers who dove in right
+ from the beginning and have contributed the code and ideas to make
+ &SCons; a success: Chad Austin, Charles Crain, Steve Leblanc, and
+ Anthony Roach. Thanks also to those on the scons-devel mailing list
+ who have contributed greatly to the discussion, notably including
+ David Abrahams, Trent Mick, and Steven Shaw.
+
+</para>
+
+<para>
+
+ &SCons; would not exist today without the pioneering work of Bob
+ Sidebotham on the original &Cons; tool, and without Greg Wilson's
+ having started the Software Carpentry contest.
+
+</para>
+
+<para>
+
+ Thanks also to Peter Miller for: Aegis; the testing discipline that it
+ enforces, without which creating a stable but flexible tool would be
+ impossible; the "Recursive Make Considered Harmful" paper which led me
+ to experiment with &Cons; in the first place.
+
+</para>
diff --git a/doc/python10/arch.eps b/doc/python10/arch.eps
new file mode 100644
index 0000000..1fdd51f
--- /dev/null
+++ b/doc/python10/arch.eps
@@ -0,0 +1,134 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: build/doc/python10/arch.fig
+%%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d
+%%CreationDate: Sun Jan 2 01:21:05 2005
+%%For: knight@casablanca.home.baldmt.com (Steven Knight)
+%%BoundingBox: 0 0 218 182
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 182 moveto 0 0 lineto 218 0 lineto 218 182 lineto closepath clip newpath
+-215.3 324.7 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%
+% Fig objects follow
+%
+/Courier-Bold ff 300.00 scf sf
+3825 2925 m
+gs 1 -1 sc (scons) col0 sh gr
+/Times-Roman ff 300.00 scf sf
+3825 3225 m
+gs 1 -1 sc (Script) col0 sh gr
+/Times-Roman ff 300.00 scf sf
+5100 4875 m
+gs 1 -1 sc (Build Engine) col0 sh gr
+/Courier-Bold ff 300.00 scf sf
+4200 4875 m
+gs 1 -1 sc (SCons) col0 sh gr
+% Polyline
+7.500 slw
+n 3600 4200 m 7200 4200 l 7200 5400 l 3600 5400 l
+ cp gs col0 s gr
+/Courier-Bold ff 300.00 scf sf
+4725 4050 m
+gs 1 -1 sc (SCons) col0 sh gr
+/Times-Roman ff 300.00 scf sf
+5625 4050 m
+gs 1 -1 sc (API) col0 sh gr
+% Polyline
+n 3600 2400 m 3600 2400 l 3600 2400 l 3600 2400 l
+ cp gs col0 s gr
+% Polyline
+n 3600 2400 m 4800 2400 l 4800 3600 l 3600 3600 l
+ cp gs col0 s gr
+% Polyline
+n 3600 3600 m 7200 3600 l 7200 4200 l 3600 4200 l
+ cp gs col0 s gr
+% Polyline
+ [60] 0 sd
+n 6000 3600 m 7200 3600 l 7200 2400 l 6000 2400 l
+ cp gs col0 s gr [] 0 sd
+/Times-Italic ff 300.00 scf sf
+6300 2925 m
+gs 1 -1 sc (other) col0 sh gr
+/Times-Italic ff 300.00 scf sf
+6150 3225 m
+gs 1 -1 sc (interface) col0 sh gr
+$F2psEnd
+rs
diff --git a/doc/python10/arch.fig b/doc/python10/arch.fig
new file mode 100644
index 0000000..ae20bd4
--- /dev/null
+++ b/doc/python10/arch.fig
@@ -0,0 +1,35 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 3825 2775 4650 3300
+4 0 0 50 0 14 20 0.0000 4 135 825 3825 2925 scons\001
+4 0 0 50 0 0 20 0.0000 4 255 690 3825 3225 Script\001
+-6
+6 3600 4200 7200 5400
+6 4200 4650 6675 4950
+4 0 0 50 0 0 20 0.0000 4 255 1515 5100 4875 Build Engine\001
+4 0 0 50 0 14 20 0.0000 4 165 825 4200 4875 SCons\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 4200 7200 4200 7200 5400 3600 5400 3600 4200
+-6
+6 4725 3825 6150 4050
+4 0 0 50 0 14 20 0.0000 4 165 825 4725 4050 SCons\001
+4 0 0 50 0 0 20 0.0000 4 195 465 5625 4050 API\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 2400 3600 2400 3600 2400 3600 2400 3600 2400
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 2400 4800 2400 4800 3600 3600 3600 3600 2400
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 3600 7200 3600 7200 4200 3600 4200 3600 3600
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 6000 3600 7200 3600 7200 2400 6000 2400 6000 3600
+4 0 0 50 0 1 20 0.0000 4 210 570 6300 2925 other\001
+4 0 0 50 0 1 20 0.0000 4 270 975 6150 3225 interface\001
diff --git a/doc/python10/arch.jpg b/doc/python10/arch.jpg
new file mode 100644
index 0000000..4e69437
--- /dev/null
+++ b/doc/python10/arch.jpg
Binary files differ
diff --git a/doc/python10/builder.eps b/doc/python10/builder.eps
new file mode 100644
index 0000000..db87afc
--- /dev/null
+++ b/doc/python10/builder.eps
@@ -0,0 +1,325 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: build/doc/python10/builder.fig
+%%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d
+%%CreationDate: Sun Jan 2 01:21:05 2005
+%%For: knight@casablanca.home.baldmt.com (Steven Knight)
+%%BoundingBox: 0 0 668 290
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 290 moveto 0 0 lineto 668 0 lineto 668 290 lineto closepath clip newpath
+-53.3 342.7 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/reencdict 12 dict def /ReEncode { reencdict begin
+/newcodesandnames exch def /newfontname exch def /basefontname exch def
+/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def
+basefontdict { exch dup /FID ne { dup /Encoding eq
+{ exch dup length array copy newfont 3 1 roll put }
+{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall
+newfont /FontName newfontname put newcodesandnames aload pop
+128 1 255 { newfont /Encoding get exch /.notdef put } for
+newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat
+newfontname newfont definefont pop end } def
+/isovec [
+8#055 /minus 8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde
+8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis
+8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron
+8#220 /dotlessi 8#230 /oe 8#231 /OE
+8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling
+8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis
+8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot
+8#255 /hyphen 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus
+8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph
+8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine
+8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf
+8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute
+8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring
+8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute
+8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute
+8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve
+8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply
+8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex
+8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave
+8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring
+8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute
+8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute
+8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve
+8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide
+8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex
+8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def
+/Times-Roman /Times-Roman-iso isovec ReEncode
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%
+% Fig objects follow
+%
+% Polyline
+7.500 slw
+n 2700 1200 m 4500 1200 l 4500 1800 l 2700 1800 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+2925 1575 m
+gs 1 -1 sc (Environment) col0 sh gr
+% Polyline
+n 2700 2400 m 4500 2400 l 4500 3000 l 2700 3000 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+3600 2775 m
+gs 1 -1 sc (BuilderWrapper) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 2700 3600 m 4500 3600 l 4500 4200 l 2700 4200 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+3600 3975 m
+gs 1 -1 sc (BuilderBase) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 8400 3600 m 9900 3600 l 9900 4200 l 8400 4200 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+9150 3975 m
+gs 1 -1 sc (ActionBase) dup sw pop 2 div neg 0 rm col0 sh gr
+/Times-Roman-iso ff 240.00 scf sf
+4650 5175 m
+gs 1 -1 sc (MultiStep-) dup sw pop 2 div neg 0 rm col0 sh gr
+/Times-Roman-iso ff 240.00 scf sf
+4650 5460 m
+gs 1 -1 sc (Builder) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 3900 4800 m 5400 4800 l 5400 5700 l 3900 5700 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+2550 5175 m
+gs 1 -1 sc (Composite-) dup sw pop 2 div neg 0 rm col0 sh gr
+/Times-Roman-iso ff 240.00 scf sf
+2550 5460 m
+gs 1 -1 sc (Builder) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 1800 4800 m 3300 4800 l 3300 5700 l 1800 5700 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+7050 5175 m
+gs 1 -1 sc (Command) dup sw pop 2 div neg 0 rm col0 sh gr
+/Times-Roman-iso ff 240.00 scf sf
+7050 5460 m
+gs 1 -1 sc (Action) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 6300 4800 m 7800 4800 l 7800 5700 l 6300 5700 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+9150 5460 m
+gs 1 -1 sc (Action) dup sw pop 2 div neg 0 rm col0 sh gr
+/Times-Roman-iso ff 240.00 scf sf
+9150 5175 m
+gs 1 -1 sc (Function) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 8400 4800 m 9900 4800 l 9900 5700 l 8400 5700 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+11250 5175 m
+gs 1 -1 sc (List) dup sw pop 2 div neg 0 rm col0 sh gr
+/Times-Roman-iso ff 240.00 scf sf
+11250 5460 m
+gs 1 -1 sc (Action) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 10500 4800 m 12000 4800 l 12000 5700 l 10500 5700 l
+ cp gs col0 s gr
+% Polyline
+n 900 2400 m 2100 2400 l 2100 3000 l 900 3000 l
+ cp gs col0 s gr
+/Times-Roman-iso ff 240.00 scf sf
+1500 2775 m
+gs 1 -1 sc (Node) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 3600 4200 m 3525 4350 l 3675 4350 l
+ cp gs col0 s gr
+% Polyline
+n 3150 4800 m 3150 4500 l 4050 4500 l
+ 4050 4800 l gs col0 s gr
+% Polyline
+n 3600 4350 m
+ 3600 4500 l gs col0 s gr
+% Polyline
+n 9150 4200 m 9075 4350 l 9225 4350 l
+ cp gs col0 s gr
+% Polyline
+n 7050 4800 m 7050 4500 l 10950 4500 l
+ 10950 4800 l gs col0 s gr
+% Polyline
+n 9150 4350 m
+ 9150 4800 l gs col0 s gr
+% Polyline
+gs clippath
+9885 3870 m 9885 3930 l 10036 3930 l 9916 3900 l 10036 3870 l cp
+eoclip
+n 11550 4650 m 11550 3900 l
+ 9900 3900 l gs col0 s gr gr
+
+% arrowhead
+n 10036 3870 m 9916 3900 l 10036 3930 l 10036 3870 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+8415 3930 m 8415 3870 l 8264 3870 l 8384 3900 l 8264 3930 l cp
+eoclip
+n 4650 3900 m
+ 8400 3900 l gs col0 s gr gr
+
+% arrowhead
+n 8264 3930 m 8384 3900 l 8264 3870 l 8264 3930 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+3930 1785 m 3870 1785 l 3870 1936 l 3900 1816 l 3930 1936 l cp
+eoclip
+n 3900 2250 m
+ 3900 1800 l gs col0 s gr gr
+
+% arrowhead
+n 3930 1936 m 3900 1816 l 3870 1936 l 3930 1936 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+3270 2415 m 3330 2415 l 3330 2264 l 3300 2384 l 3270 2264 l cp
+eoclip
+n 3300 1950 m
+ 3300 2400 l gs col0 s gr gr
+
+% arrowhead
+n 3270 2264 m 3300 2384 l 3330 2264 l 3270 2264 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+3570 3615 m 3630 3615 l 3630 3464 l 3600 3584 l 3570 3464 l cp
+eoclip
+n 3600 3150 m
+ 3600 3600 l gs col0 s gr gr
+
+% arrowhead
+n 3570 3464 m 3600 3584 l 3630 3464 l 3570 3464 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4380 4185 m 4320 4185 l 4320 4336 l 4350 4216 l 4380 4336 l cp
+eoclip
+n 4350 4650 m
+ 4350 4200 l gs col0 s gr gr
+
+% arrowhead
+n 4380 4336 m 4350 4216 l 4320 4336 l 4380 4336 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+2880 4185 m 2820 4185 l 2820 4336 l 2850 4216 l 2880 4336 l cp
+eoclip
+n 2850 4650 m
+ 2850 4200 l gs col0 s gr gr
+
+% arrowhead
+n 2880 4336 m 2850 4216 l 2820 4336 l 2880 4336 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+2715 3930 m 2715 3870 l 2564 3870 l 2684 3900 l 2564 3930 l cp
+eoclip
+n 1500 3150 m 1500 3900 l
+ 2700 3900 l gs col0 s gr gr
+
+% arrowhead
+n 2564 3930 m 2684 3900 l 2564 3870 l 2564 3930 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 4650 3900 m 4575 3860 l 4500 3900 l 4575 3940 l
+ cp gs col0 s gr
+% Polyline
+n 1500 3000 m 1460 3075 l 1500 3150 l 1540 3075 l
+ cp gs col0 s gr
+% Polyline
+n 3600 3000 m 3560 3075 l 3600 3150 l 3640 3075 l
+ cp gs col0 s gr
+% Polyline
+n 3300 1800 m 3260 1875 l 3300 1950 l 3340 1875 l
+ cp gs col0 s gr
+% Polyline
+n 3900 2250 m 3860 2325 l 3900 2400 l 3940 2325 l
+ cp gs col0 s gr
+% Polyline
+n 4350 4650 m 4310 4725 l 4350 4800 l 4390 4725 l
+ cp gs col0 s gr
+% Polyline
+n 2850 4650 m 2810 4725 l 2850 4800 l 2890 4725 l
+ cp gs col0 s gr
+% Polyline
+n 11550 4650 m 11510 4725 l 11550 4800 l 11590 4725 l
+ cp gs col0 s gr
+% Polyline
+ [60] 0 sd
+n 3600 1200 m
+ 3600 900 l gs col0 s gr [] 0 sd
+$F2psEnd
+rs
diff --git a/doc/python10/builder.fig b/doc/python10/builder.fig
new file mode 100644
index 0000000..75e5ec0
--- /dev/null
+++ b/doc/python10/builder.fig
@@ -0,0 +1,128 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 2700 1200 4500 1800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 1200 4500 1200 4500 1800 2700 1800 2700 1200
+4 0 0 50 0 0 16 0.0000 4 165 1290 2925 1575 Environment\001
+-6
+6 2700 2400 4500 3000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 2400 4500 2400 4500 3000 2700 3000 2700 2400
+4 1 0 50 0 0 16 0.0000 4 225 1620 3600 2775 BuilderWrapper\001
+-6
+6 2700 3600 4500 4200
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 3600 4500 3600 4500 4200 2700 4200 2700 3600
+4 1 0 50 0 0 16 0.0000 4 165 1215 3600 3975 BuilderBase\001
+-6
+6 8400 3600 9900 4200
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 8400 3600 9900 3600 9900 4200 8400 4200 8400 3600
+4 1 0 50 0 0 16 0.0000 4 165 1140 9150 3975 ActionBase\001
+-6
+6 3900 4800 5400 5700
+6 4050 4950 5250 5475
+4 1 0 50 0 0 16 0.0000 4 225 1140 4650 5175 MultiStep-\001
+4 1 0 50 0 0 16 0.0000 4 165 750 4650 5460 Builder\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3900 4800 5400 4800 5400 5700 3900 5700 3900 4800
+-6
+6 1800 4800 3300 5700
+6 1950 4950 3150 5475
+4 1 0 50 0 0 16 0.0000 4 225 1200 2550 5175 Composite-\001
+4 1 0 50 0 0 16 0.0000 4 165 750 2550 5460 Builder\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1800 4800 3300 4800 3300 5700 1800 5700 1800 4800
+-6
+6 6300 4800 7800 5700
+6 6525 4950 7575 5475
+4 1 0 50 0 0 16 0.0000 4 165 1020 7050 5175 Command\001
+4 1 0 50 0 0 16 0.0000 4 165 675 7050 5460 Action\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 6300 4800 7800 4800 7800 5700 6300 5700 6300 4800
+-6
+6 8400 4800 9900 5700
+6 8700 4950 9600 5475
+4 1 0 50 0 0 16 0.0000 4 165 675 9150 5460 Action\001
+4 1 0 50 0 0 16 0.0000 4 165 870 9150 5175 Function\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 8400 4800 9900 4800 9900 5700 8400 5700 8400 4800
+-6
+6 10500 4800 12000 5700
+6 10875 4950 11625 5475
+4 1 0 50 0 0 16 0.0000 4 165 390 11250 5175 List\001
+4 1 0 50 0 0 16 0.0000 4 165 675 11250 5460 Action\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 10500 4800 12000 4800 12000 5700 10500 5700 10500 4800
+-6
+6 900 2400 2100 3000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 900 2400 2100 2400 2100 3000 900 3000 900 2400
+4 1 0 50 0 0 16 0.0000 4 165 525 1500 2775 Node\001
+-6
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 3600 4200 3525 4350 3675 4350 3600 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 3150 4800 3150 4500 4050 4500 4050 4800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3600 4350 3600 4500
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 9150 4200 9075 4350 9225 4350 9150 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 7050 4800 7050 4500 10950 4500 10950 4800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 9150 4350 9150 4800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 1 1 1.00 60.00 120.00
+ 11550 4650 11550 3900 9900 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4650 3900 8400 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3900 2250 3900 1800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3300 1950 3300 2400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3600 3150 3600 3600
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4350 4650 4350 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2850 4650 2850 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 1 1 1.00 60.00 120.00
+ 1500 3150 1500 3900 2700 3900
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 4650 3900 4575 3860 4500 3900 4575 3940 4650 3900
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 1500 3000 1460 3075 1500 3150 1540 3075 1500 3000
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3600 3000 3560 3075 3600 3150 3640 3075 3600 3000
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3300 1800 3260 1875 3300 1950 3340 1875 3300 1800
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3900 2250 3860 2325 3900 2400 3940 2325 3900 2250
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 4350 4650 4310 4725 4350 4800 4390 4725 4350 4650
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 2850 4650 2810 4725 2850 4800 2890 4725 2850 4650
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 11550 4650 11510 4725 11550 4800 11590 4725 11550 4650
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 3600 1200 3600 900
diff --git a/doc/python10/builder.jpg b/doc/python10/builder.jpg
new file mode 100644
index 0000000..e9085e8
--- /dev/null
+++ b/doc/python10/builder.jpg
Binary files differ
diff --git a/doc/python10/copyright.xml b/doc/python10/copyright.xml
new file mode 100644
index 0000000..d141fc6
--- /dev/null
+++ b/doc/python10/copyright.xml
@@ -0,0 +1,32 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<blockquote>
+ <para>
+
+ Copyright (c) 2001, 2002 Steven Knight
+
+ </para>
+</blockquote>
diff --git a/doc/python10/design.xml b/doc/python10/design.xml
new file mode 100644
index 0000000..cb58af9
--- /dev/null
+++ b/doc/python10/design.xml
@@ -0,0 +1,898 @@
+<para>
+
+ The &SCons; architecture consists of three layers:
+
+</para>
+
+<mediaobject>
+ <imageobject>
+ <imagedata fileref="arch" format="eps" align="center">
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="arch.jpg" format="jpg" align="center">
+ </imageobject>
+ <!-- PDF files?
+ <imageobject>
+ <imagedata fileref="arch.pdf" align="center">
+ </imageobject>
+ -->
+</mediaobject>
+
+<itemizedlist>
+
+ <listitem>
+ <para>
+
+ The &SCons; <emphasis>Build Engine</emphasis>, a package of Python
+ modules that handle dependency management and updating out-of-date
+ objects.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ The &SCons; <emphasis>API</emphasis> (applications programming
+ interface) between the Build Engine
+ and the user interface.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ The &scons; <emphasis>script</emphasis> itself (note lower case
+ <emphasis>sc</emphasis>), which is the pre-provided interface to
+ the Build Engine.
+
+ </para>
+ </listitem>
+
+</itemizedlist>
+
+<para>
+
+ Notice that this architecture separates the internal workings of
+ &SCons; (the Build Engine) from the
+ external user interface. The benefit is that the &SCons; Build Engine
+ can be imported into any other software package written in Python
+ to support a variety of user interfaces&mdash;or, to look at it
+ in reverse, other software interfaces can use the &SCons; Build
+ Engine to manage dependencies between their objects.
+
+</para>
+
+<para>
+
+ Because the
+ &SCons; package itself is modular, only those parts of the package
+ relevant to the embedding interface need be imported; for example,
+ a utility that wants to use only file timestamps for checking
+ whether a file is up-to-date
+ need not import the MD5 signature module.
+
+</para>
+
+<section>
+ <title>The &SCons; Build Engine</title>
+
+ <para>
+
+ The Build Engine is a package of Python modules that
+ form the heart of &SCons;.
+
+ The Build Engine can be broadly divided into five
+ architectural subsystems, each responsible
+ for a crucial part of &SCons; functionality:
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ A <emphasis>node</emphasis> subsystem, responsible for managing
+ the files (or other objects) to be built, and the dependency
+ relationships between them.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ A <emphasis>scanner</emphasis> subsystem, responsible for
+ scanning various file types for implicit dependencies.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ A <emphasis>signature</emphasis> subsystem, responsible for
+ deciding whether a given file (or other object) requires
+ rebuilding.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ A <emphasis>builder</emphasis> subsystem, responsible for
+ actually executing the necessary command or function to
+ build a file (or other object).
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ A <emphasis>job/task</emphasis> subsystem, responsible for
+ handling parallelization of builds.
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>
+
+ The rest of this section will provide a high-level overview of the
+ class structure of each of these Build Engine subsystems.
+
+ </para>
+
+ <section>
+ <title>Node Subsystem</title>
+
+ <para>
+
+ The node subsystem of the Build Engine is
+ responsible for managing the knowledge in &SCons; of
+ the relationships among the external objects
+ (files) it is responsible for updating.
+ The most important of these relationships is
+ the dependency relationship between various &Node; objects,
+ which &SCons; uses to determine the order
+ in which builds should be performed.
+
+ </para>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="node" format="eps" align="center">
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="node.jpg" format="jpg" align="center">
+ </imageobject>
+ <!-- PDF files?
+ <imageobject>
+ <imagedata fileref="node.pdf" align="center">
+ </imageobject>
+ -->
+ </mediaobject>
+
+ <para>
+
+ The &scons; script (or other
+ user interface)
+ tells the Build Engine
+ about dependencies
+ through its &consenv; API.
+ The Build Engine also discovers
+ dependencies automatically through the use of &Scanner; objects.
+
+ </para>
+
+ <para>
+
+ Subclasses of the &Node; class maintain additional
+ relationships that reflect the real-world
+ existence of these objects.
+ For example, the &Node_FS; subclass
+ is responsible for managing a
+ representation of the directory hierarchy
+ of a file system.
+
+ </para>
+
+ <para>
+
+ A &Walker; class is used by other subsystems
+ to walk the dependency tree maintained by the &Node; class.
+ The &Walker; class maintains a stack of &Node; objects
+ visited during its depth-first traversal of the
+ dependency tree,
+ and uses an intermediate node &Wrapper; class
+ to maintain state information about a
+ &Node; object's dependencies.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Scanner Subsystem</title>
+
+ <para>
+
+ The scanner subsystem is responsible for maintaining
+ objects that can scan the contents of a &Node;'s
+ for implicit dependencies.
+
+ </para>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="scanner" format="eps" align="center">
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="scanner.jpg" format="jpg" align="center">
+ </imageobject>
+ <!-- PDF files?
+ <imageobject>
+ <imagedata fileref="scanner.pdf" align="center">
+ </imageobject>
+ -->
+ </mediaobject>
+
+ <para>
+
+ In practice, a given &Scanner; subclass object
+ functions as a prototype,
+ returning clones of itself
+ depending on the &consenv;
+ values governing how the &Node;
+ should be scanned.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Signature Subsystem</title>
+
+ <para>
+
+ The signature subsystem is responsible for computing
+ signature information for &Node; objects.
+ The signature subsystem in &SCons;
+ supports multiple ways to
+ determine whether a &Node is up-to-date
+ by using an abstract &Sig; class
+ as a strategy wrapper:
+
+ </para>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="sig" format="eps" align="center">
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="sig.jpg" format="jpg" align="center">
+ </imageobject>
+ <!-- PDF files?
+ <imageobject>
+ <imagedata fileref="sig.pdf" align="center">
+ </imageobject>
+ -->
+ </mediaobject>
+
+ <para>
+
+ By default, &SCons; tracks dependencies by computing and
+ maintaining MD5 signatures for the contents of each source file
+ (or other object). The signature of a <emphasis>derived</emphasis>
+ file consists of the aggregate of the signatures of all the source
+ files <emphasis>plus</emphasis> the command-line string used to
+ build the file. These signatures are stored in a &sconsign; file
+ in each directory.
+
+ </para>
+
+ <para>
+
+ If the contents of any of the source files changes, the change to its
+ MD5 signature is propogated to the signature of the derived file(s). The
+ simple fact that the new signature does not match the stored signature
+ indicates that the derived file is not up to date and must be rebuilt.
+
+ </para>
+
+ <para>
+
+ A separate &TimeStamp; subclass of the &Sig; class supports
+ the use of traditional file timestamps for
+ deciding whether files are up-to-date.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builder Subsystem</title>
+
+ <para>
+
+ The &SCons; Build Engine records how out-of-date files
+ (or other objects) should be rebuilt in &Builder; objects,
+ maintained by the builder subsystem:
+
+ </para>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="builder" format="eps" align="center">
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="builder.jpg" format="jpg" align="center">
+ </imageobject>
+ <!-- PDF files?
+ <imageobject>
+ <imagedata fileref="builder.pdf" align="center">
+ </imageobject>
+ -->
+ </mediaobject>
+
+ <para>
+
+ The actual underlying class name is &BuilderBase;,
+ and there are subclasses that can encapsulate
+ multiple &Builder; objects for special purposes.
+ One subclass
+ (&CompositeBuilder;)
+ selects an appropriate encapsulated &Builder;
+ based on the file suffix of the target object.
+ The other
+ (&MultiStepBuilder;).
+ can chain together multiple
+ &Builder; objects,
+ for example,
+ to build an executable program from a source file
+ through an implicit intermediate object file.
+
+ </para>
+
+ <para>
+
+ A &BuilderBase; object has an associated
+ &ActionBase; object
+ responsible for actually executing
+ the appropriate steps
+ to update the target file.
+ There are three subclasses,
+ one for externally executable commands
+ (&CommandAction;),
+ one for Python functions
+ (&FunctionAction;),
+ and one for lists of
+ multiple &Action; objects
+ (&ListAction;).
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Job/Task Subsystem</title>
+
+ <para>
+
+ &SCons; supports parallel builds with a thread-based tasking
+ model, managed by the job/task subsystem.
+
+ </para>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="job-task" format="eps" align="center">
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="job-task.jpg" format="jpg" align="center">
+ </imageobject>
+ <!-- PDF files?
+ <imageobject>
+ <imagedata fileref="job-task.pdf" align="center">
+ </imageobject>
+ -->
+ </mediaobject>
+
+ <para>
+
+ Instead of performing an outer-loop recursive descent
+ of the dependency tree and then forking a task when it finds a
+ file that needs updating, &SCons; starts as many threads as are
+ requested, each thread managed by the &Jobs; class.
+ As a performance optimization,
+ the &Jobs; class maintains an internal
+ distinction between
+ &Serial; and &Parallel;
+ build jobs,
+ so that serial builds
+ don't pay any performance penalty
+ by using a multi-threaded implementation
+ written for &Parallel; builds.
+
+ </para>
+
+ <para>
+
+ Each &Jobs; object, running in its own thread,
+ then requests a &Task; from a central &Taskmaster;,
+ which is responsible
+ for handing out available &Task; objects for (re-)building
+ out-of-date nodes. A condition variable
+ makes sure that the &Jobs; objects
+ query the &Taskmaster; one at a time.
+
+ </para>
+
+ <para>
+
+ The &Taskmaster uses the node subsystem's
+ &Walker; class to walk the dependency tree,
+ and the &Sig; class to use the
+ appropriate method
+ of deciding if a &Node; is up-to-date.
+
+ </para>
+
+ <para>
+
+ This scheme has many advantages over the standard &Make;
+ implementation of <option>-j</option>.
+ Effective use of <option>-j</option> is difficult
+ with the usual recursive use of Make,
+ because the number of jobs started by <option>-j</option> multiply
+ at each level of the source tree.
+ This makes the actual number of jobs
+ executed at any moment very dependent on the size and layout of
+ the tree. &SCons;, in contrast, starts only as many jobs as are
+ requested, and keeps them constantly busy (excepting jobs that
+ block waiting for their dependency files to finish building).
+
+ </para>
+
+ </section>
+
+</section>
+
+<section>
+ <title>The &SCons; API</title>
+
+ <para>
+
+ This section provides an overview of the &SCons; interface. The
+ complete interface specification is both more detailed and flexible
+ than this overview.
+
+ </para>
+
+ <section>
+ <title>&ConsVars;</title>
+
+ <para>
+
+ In &SCons;, a &consenv; is an object through which an external
+ interface (such as the &scons; script) communicates dependency
+ information to the &SCons; Build Engine.
+
+ </para>
+
+ <para>
+
+ A construction environment is implemented as a dictionary
+ containing:
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ construction variables, string values that are substituted
+ into command lines or used by builder functions;
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ one or more &Builder; objects that can be invoked to update a
+ file or other object;
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ one or more &Scanner; objects that can be used to
+ scan a file automatically for dependencies (such as
+ files specified on <literal>#include</literal> lines).
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>
+
+ &Consenvs; are instantiated as follows:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env_debug = Environment(CCFLAGS = '-g')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>&Builder; Objects</title>
+
+ <para>
+
+ An &SCons; &Builder; object encapsulates information about how to
+ build a specific type of file: an executable program, an object
+ file, a library, etc. A &Builder; object is associated with a
+ file through an associated &consenv; method and later invoked to
+ actually build the file. The &Builder; object will typically use
+ construction variables (such as &CCFLAGS;, &LIBPATH;) to influence
+ the specific build execution.
+
+ </para>
+
+ <para>
+
+ &Builder; objects are instantiated as follows:
+
+ </para>
+
+ <programlisting>
+ bld = Builder(name = 'Program', action = "$CC -o $TARGET $SOURCES")
+ </programlisting>
+
+ <para>
+
+ In the above example, the <literal>action</literal> is a
+ command-line string in which the Build Engine will
+ interpolate the values of construction
+ variables before execution. The actual
+ <literal>action</literal> specified, though,
+ may be a function:
+
+ </para>
+
+ <programlisting>
+ def update(dest):
+ # [code to update the object]
+ return 0
+
+ bld = Builder(name = 'Program', function = update)
+ </programlisting>
+
+ <para>
+
+ Or a callable Python object (or class):
+
+ </para>
+
+ <programlisting>
+ class class_a:
+ def __call__(self, kw):
+ # build the desired object
+ return 0
+
+ builder = SCons.Builder.Builder(action = class_a())
+ </programlisting>
+
+ <para>
+
+ A &Builder; object may have the <literal>prefix</literal> and
+ <literal>suffix</literal> of its target file type specified
+ as keyword arguments at instantiation. Additionally, the
+ suffix of the <emphasis>source files</emphasis> used by this
+ &Builder; to build its target files may be specified using the
+ <literal>src_suffix</literal> keyword argument:
+
+ </para>
+
+ <programlisting>
+ bld_lib = Builder(name = 'Library', action = "$AR r $TARGET $SOURCES",
+ prefix = 'lib', suffix = '.a', src_suffix = '.o')
+ </programlisting>
+
+ <para>
+
+ The specified <literal>prefix</literal> and
+ <literal>suffix</literal> will be appended to the name of any
+ target file built by this &Builder; object, if they are not
+ already part of the file name. The <literal>src_suffix</literal>
+ is used by the &SCons; Build Engine to chain together
+ multiple &Builder; objects to create,
+ for example, a library from the original source
+ files without having to specify the
+ intermediate <literal>.o</literal> files.
+
+ </para>
+
+ <para>
+
+ &Builder; objects are associated with a &consenv; through a
+ &consvar; named &BUILDERS;, a list of the &Builder objects that
+ will be available for execution through the &consenv:
+
+ </para>
+
+ <programlisting>
+ env = Environment(BUILDERS = [ Object, Library, WebPage, Program ])
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>&Scanner; Objects</title>
+
+ <para>
+
+ &Scanner; objects perform automatic checking for dependencies
+ by scanning the contents of files. The canonical
+ example is scanning a C source file or header file for
+ files specified on <literal>#include</literal> lines.
+
+ </para>
+
+ <para>
+
+ A &Scanner; object is instantiated as follows:
+
+ </para>
+
+ <programlisting>
+ def c_scan(contents):
+ # scan contents of file
+ return # list of files found
+
+ c_scanner = Scanner(name = 'CScan', function = c_scan,
+ argument = None,
+ skeys = ['.c', '.C', '.h', '.H')
+ </programlisting>
+
+ <para>
+
+ The <literal>skeys</literal> argument specifies a list of file
+ suffixes for file types that this &Scanner; knows how to scan.
+
+ </para>
+
+ <para>
+
+ &Scanner; objects are associated with a &consenv; through a
+ &consvar; named &SCANNERS;, a list of the &Scanner; objects that
+ will be available through the &consenv:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SCANNERS = [ CScan, M4Scan ])
+ </programlisting>
+
+ <para>
+
+ For utilities that will build files with a variety of file
+ suffixes, or which require unusual scanning rules, a &Scanner;
+ object may be associated explicitly with a &Builder; object as
+ follows:
+
+ </para>
+
+ <programlisting>
+ def tool_scan(contents):
+ # scan contents of file
+ return # list of files found
+
+ tool_scanner = Scanner(name = 'TScan', function = tool_scan)
+
+ bld = Builder(name = 'Tool', scanner = tool_scanner)
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>&BuildDir;</title>
+
+ <para>
+
+ &SCons; supports a flexible mechanism for building target
+ files in a separate build directory from the source files.
+ The &BuildDir; syntax is straightforward:
+
+ </para>
+
+ <programlisting>
+ BuildDir(source = 'src', build = 'bld')
+ </programlisting>
+
+ <para>
+
+ By
+ default, source files are linked or copied into the build
+ directory, because exactly replicating the source directory
+ is sometimes necessary for certain combinations of use of
+ <literal>#include "..."</literal> and <option>-I</option> search
+ paths.
+
+ An option exists to specify that only output files should be placed in
+ the build directory:
+
+ </para>
+
+ <programlisting>
+ BuildDir(source = 'src', build = 'bld', no_sources = 1)
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>&Repository;</title>
+
+ <para>
+
+ &SCons; supports the ability to search a list of code repositories
+ for source files and derived files. This works much like
+ &Make;'s <varname>VPATH</varname> feature, as implemented in
+ recent versions of GNU &Make;.
+ (The POSIX standard for &Make; specifies slightly
+ different behavior for <varname>VPATH</varname>.)
+ The syntax is:
+
+ </para>
+
+ <programlisting>
+ Repository('/home/source/1.1', '/home/source/1.0')
+ </programlisting>
+
+ <para>
+
+ A command-line <option>-Y</option> option exists to allow
+ repositories to be specified on the command line, or in the
+ &SCONSFLAGS; environment variable (not construction variable!).
+ This avoids a chicken-and-egg situation and allows the top-level
+ &SConstruct; file to be found in a repository as well.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>&Cache;</title>
+
+ <para>
+
+ &SCons; supports a way for developers to share derived files. Again, the
+ syntax is straightforward:
+
+ </para>
+
+ <programlisting>
+ Cache('/var/build.cache/i386')
+ </programlisting>
+
+ <para>
+
+ Copies of any derived files built will be placed in the specified
+ directory with their MD5 signature. If another build results in an
+ out-of-date derived file with the same signature, the derived file
+ will be copied from the cache instead of being rebuilt.
+
+ </para>
+
+ </section>
+
+</section>
+
+<section>
+ <title>The &scons; Script</title>
+
+ <para>
+
+ The &scons; script provides an interface
+ that looks roughly equivalent to the
+ classic &Make; utility&mdash;that is, execution from the command
+ line, and dependency information read from configuration files.
+
+ </para>
+
+ <para>
+
+ The most noticeable difference between &scons; and &Make;, or most
+ other build tools, is that the configuration files are actually
+ Python scripts, generically called "SConscripts" (although the
+ top-level "Makefile" is named &SConstruct). Users do not have to
+ learn a new language syntax, but instead configure dependency
+ information by making direct calls to the Python API of the
+ &SCons; Build Engine. Here is an example &SConstruct file which
+ builds a program in side-by-side normal and debug versions:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ debug = env.Copy(CCFLAGS = '-g')
+
+ source_files = ['f1.c', 'f2.c', 'f3.c']
+
+ env.Program(target = 'foo', sources = source_files)
+ debug.Program(target = 'foo-debug', sources = source_files)
+ </programlisting>
+
+ <para>
+
+ Notice the fact that this file is a Python script, which allows us
+ to define and re-use an array that lists the source files.
+
+ </para>
+
+ <para>
+
+ Because quoting individul strings in long
+ lists of files can get tedious and error-prone, the &SCons;
+ methods support a short-cut of listing multiple files in a single
+ string, separated by white space.
+ This would change
+ the assignment in the above example to a more easily-readable:
+
+ </para>
+
+ <programlisting>
+ source_files = 'f1.c f2.c f3.c'
+ </programlisting>
+
+ <para>
+
+ The mechanism to establish hierarchical builds is to "include" any
+ subsidiary configuration files in the build by listing them explicitly
+ in a call to the &SConscript; function:
+
+ </para>
+
+ <programlisting>
+ SConscript('src/SConscript', 'lib/SConscript')
+ </programlisting>
+
+ <para>
+
+ By convention, configuration files in subdirectories are named
+ &SConscript;.
+
+ </para>
+
+ <para>
+
+ The &scons; script has intentionally been made to look, from
+ the outside, as much like &Make; as is practical. To this
+ end, the &scons; script supports all of the same command-line
+ options supported by GNU &Make;: <option>-f</option> FILE,
+ <option>-j</option>, <option>-k</option>, <option>-s</option>,
+ etc. For compatibility, &scons; ignores those GNU &Make; options
+ that don't make sense for the &SCons; architecture, such as
+ <option>-b</option>, <option>-m</option>, <option>-S</option>,
+ and <option>-t</option>. The
+ intention is that, given an equivalent &SConstruct; file for a
+ &Makefile;, a user could use &SCons; as a drop-in replacement for
+ &Make;. Additional command-line options are, where possible, taken
+ from the Perl &Cons; utility on which the &SCons; design is based.
+
+ </para>
+
+</section>
diff --git a/doc/python10/future.xml b/doc/python10/future.xml
new file mode 100644
index 0000000..272d508
--- /dev/null
+++ b/doc/python10/future.xml
@@ -0,0 +1,170 @@
+<para>
+
+ There are a number of things we would like to do to continue to
+ improve &SCons; in the future.
+
+</para>
+
+<section>
+ <title>Distutils Cooperation</title>
+
+ <para>
+
+ There is a certain amount of overlap between what &SCons; does
+ to search out and make use of various compilers on a system, and
+ the impressively complete job that the Distutils do of describing
+ much the same thing. Collaborating to provide some sort of common
+ interface between the two tools would benefit both tools.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Additional Builder Support</title>
+
+ <para>
+
+ Adding additional builders would broaden the
+ potential user base. In rough order of importance:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Java</term>
+ <listitem>
+ <para>
+
+ Given the popularity of Java, support for it would greatly
+ increase the appeal of &SCons; in the large community of Java
+ users.
+
+ </para>
+
+ <para>
+
+ Good support for Java is, however, a tricky
+ proposition. Because the Java compiler can make decisions
+ about compiling other files based on what classes it finds
+ in a file, it behaves "unpredictably" from the point of
+ view of an outside build tool like &SCons; or &Make;. Some
+ sort of sophisticated scanning of Java source code to
+ identify what other classes are likely to be compiled
+ would be an obvious first step, but notice that here
+ &SCons; would be scanning the file to find additional
+ targets to be built. This is the inverse of the sort of
+ <literal>#include</literal> scanning performed
+ for C files, in which &SCons; is looking for additional
+ <emphasis>dependencies</emphasis>.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Documentation toolchains</term>
+ <listitem>
+ <para>
+
+ A number of early adopters
+ are using &SCons; to
+ build documents
+ from TeX or DocBook source files.
+ Built-in support for
+ various documentation toolchains
+ would be an obvious boon
+ for many people.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>C#</term>
+ <listitem>
+ <para>
+
+ The reality is that anything that Microsoft does will doubtless
+ have a wide audience. Turning &SCons;' back on that would be
+ cutting off its nose to spite its face.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Fortran</term>
+ <listitem>
+ <para>
+
+ Despite the fact that &SCons; is no longer directly
+ associated with Software Carpentry, it still shares the
+ same goal: to make programming easier for more than just
+ programmers. To that end, good Fortran support would
+ help a great many physical scientists and other computer
+ <emphasis>users</emphasis> out there who still rely on Fortran
+ for a great deal of their work.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+</section>
+
+<section>
+ <title>Database Interface</title>
+
+ <para>
+
+ The Nodes in an &SCons; dependency graph aren't only restricted to
+ files. Creating an interface to mSQL or MySQL databases would allow
+ the possibility of updating external files in response to changes in
+ database fields, or vice versa. This could be handy, for example,
+ for generating a cache of static web pages from a database that only
+ need re-generating when the appropriate database objects change.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Tool Integration</title>
+
+ <para>
+
+ &SCons; should work well with as many popular Integrated Development
+ Environments (IDEs) and tool chains as possible: Komodo, Microsoft
+ Visual Studio, ClearCase, etc. Suggestions for additional tools are
+ welcome.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Makefile Interface</title>
+
+ <para>
+
+ Because the &SCons; Build Engine can be embedded in any Python
+ interface, there isn't any technical reason why a &Makefile;
+ interpreter couldn't be written in Python and use the &SCons; Build
+ Engine for its dependency analysis.
+
+ </para>
+
+ <para>
+
+ Proof-of-concept for the idea already exists. Gary Holt's
+ <literal>make++</literal> (also known as <literal>makepp</literal>)
+ is a Perl implementation of just such a &Makefile; interpreter. It
+ could possible serve as a model for a Python version, in much the
+ same way the &Cons; design served as the prototype for &SCons;.
+
+ </para>
+
+</section>
diff --git a/doc/python10/install.xml b/doc/python10/install.xml
new file mode 100644
index 0000000..d150beb
--- /dev/null
+++ b/doc/python10/install.xml
@@ -0,0 +1,179 @@
+<para>
+
+ Initial installation of a new utility provides the first, lasting
+ impression of how well the software is likely to perform. From the
+ start, &SCons; has made clean installation a priority.
+
+</para>
+
+<section>
+ <title>Version Control</title>
+
+ <para>
+
+ Distributing an application like &SCons; that depends
+ on a package normally found in a library poses a
+ problem. If the &scons; script and the &SCons; Build Engine
+ are installed separately, it could be easy
+ to introduce a version mismatch between the Build Engine
+ installed in
+ <filename>/usr/lib/python*/site-packages</filename>
+ and the &scons; script installed in
+ <filename>/usr/bin</filename>.
+ Such a mismatch
+ could possible mean exceptions that prevent builds, or even worse,
+ silently unreliable builds.
+
+ </para>
+
+ <para>
+
+ To reduce the possibility of a version mismatch,
+ the &scons; script looks first for its
+ imported modules in <filename>/usr/lib/scons-{version}/</filename>,
+ then in <filename>/usr/lib/scons/</filename>,
+ and then in the normal &PYTHONPATH; locations,
+ including <filename>/usr/lib/python*/site-packages</filename>).
+ Searching in a version-specific library directory first
+ makes it convenient to install and use multiple
+ side-by-side versions of &SCons;,
+ which is sometimes important
+ when verifying that a new version does not introduce any
+ errors into the local build process.
+ Searching next in an &SCons;-specific library directory
+ makes it convenient for other software to find
+ the &SCons; Build Engine without having to worry about
+ installing separate copies for
+ multiple versions of Python.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Packages</title>
+
+ <para>
+
+ &SCons; is currently distributed in the following packages:
+
+ </para>
+
+ <table>
+ <title></title>
+ <tgroup cols="2">
+ <tbody>
+
+ <row valign="top">
+ <entry>
+ <literal>scons-</literal><emphasis>version</emphasis><literal>.tar.gz</literal>
+ </entry>
+ <entry><para>
+
+ The traditional <literal>.tar.gz</literal> file,
+ installable by running <filename>setup.py</filename>.
+
+ </para></entry>
+ </row>
+
+ <row valign="top">
+ <entry>
+ <literal>scons-</literal><emphasis>version</emphasis><literal>.noarch.rpm</literal>
+ </entry>
+ <entry><para>
+
+ An RPM file for typical installation.
+
+ </para></entry>
+ </row>
+
+ <row valign="top">
+ <entry>
+ <literal>scons-</literal><emphasis>version</emphasis><literal>_all.deb</literal>
+ </entry>
+ <entry><para>
+
+ A Debian package.
+
+ </para></entry>
+ </row>
+
+ <row valign="top">
+ <entry>
+ <literal>scons-</literal><emphasis>version</emphasis><literal>.win32.exe</literal>
+ </entry>
+ <entry><para>
+
+ A Windows installer.
+
+ </para></entry>
+ </row>
+
+ <row valign="top">
+ <entry>
+ <literal>scons-</literal><emphasis>version</emphasis><literal>.src.rpm</literal>
+ </entry>
+ <entry><para>
+
+ A source RPM file.
+
+ </para></entry>
+ </row>
+
+ <row valign="top">
+ <entry>
+ <literal>scons-src-</literal><emphasis>version</emphasis><literal>.tar.gz</literal>
+ </entry>
+ <entry><para>
+
+ A tarball of the &SCons; source tree,
+ including the full set of regression tests.
+
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+
+ Like other software written in Python, &SCons; benefits greatly from
+ the tremendous effort put into the <literal>distutils</literal> by
+ Greg Ward and others. These take care of 90% of the work by making
+ it almost trivial to generate the appropriate RPM files, Debian
+ packages, and Windows installer.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Default Builder Objects</title>
+
+ <para>
+
+ As part of the installation process, &SCons; runs a set of scripts
+ that look for popular compilers and other tools and set up
+ appropriate default &Builder; objects for the tools found. These
+ &Builder; objects are then used to initialize the default &consenv;
+ values.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Default Scanner Objects</title>
+
+ <para>
+
+ Additionally, &SCons; comes with a stock set of &Scanner; objects
+ for the various file types that it supports out of the box. Any
+ unusal &Scanner; objects required for a specific tool will be
+ detected at installation time and associated with the appropriate
+ &Builder; object for the tool.
+
+ </para>
+
+</section>
diff --git a/doc/python10/intro.xml b/doc/python10/intro.xml
new file mode 100644
index 0000000..d3057be
--- /dev/null
+++ b/doc/python10/intro.xml
@@ -0,0 +1,212 @@
+<para>
+
+ More than twenty years after its creation, the classic UNIX &Make;
+ utility and its descendants are still the dominant way in which
+ software is built. &Make; has maintained this position despite the
+ fact that the intervening years have revealed many
+ shortcomings of the &Make; model for building software:
+
+</para>
+
+<itemizedlist>
+
+ <listitem>
+ <para>
+
+ The use of timestamps to decide when a file has been updated is
+ imprecise and prone to error, especially across distributed file
+ systems such as NFS.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Builds of typical large software systems still take hours, if not
+ days, despite the tremendous advances in CPU and disk speeds over
+ recent years.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ &Make; maintains static definitions of dependencies in its
+ &Makefiles;. Much effort has been put into
+ utilities (<application>mkdepend</application>, <application>gcc
+ -M</application>) and schemes (<filename>Makefile.d</filename>
+ files) to try to keep &Makefile; dependencies up-to-date,
+ but these only confirm that &Make;'s static dependencies are
+ inherently fragile.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ The standard recursive use of &Make; for build hierarchies leads
+ to incomplete dependency graphs, which must be overcome by
+ manually changing the order in which directories are built, or
+ through the use of multiple build passes.
+
+ </para>
+ </listitem>
+
+</itemizedlist>
+
+<para>
+
+ One need only look at the plethora of helper and wrapper utilities
+ (automake, easymake, imake, jmake, makeLib, maketool, mkmed, shake,
+ SMake, TMAKE) and complete alternatives to &Make; (Ant, bake, bau,
+ bras, Cake, Cons, Cook, Jam, jmk, jus, makeme, mash, MK, nmake, Odin,
+ VMake) that have been created over the years to realize that vanilla
+ &Make; is not satisfying everyone's build requirements. So why Yet
+ Another build tool?
+
+</para>
+
+<section>
+ <title>Enter Software Carpentry</title>
+
+ <para>
+
+ Most of the build tools just mentioned
+ were written by programmers and for
+ programmers. The fact that most programmer-friendly
+ utilities do a poor job of fulfilling the needs
+ of non-programmers prompted Greg Wilson to
+ organize the Software Carpentry competition in January 2000.
+ Software Carpentry was an
+ open design contest with the express goal of producing a set of
+ next-generation utilities, including a build tool, that would be
+ accessible
+ not only to
+ programmers
+ but also to computer <emphasis>users</emphasis>
+ such as physical scientists.
+
+ </para>
+
+ <para>
+
+ The key to this usability would be that all of
+ these utilities, including the build tool, would be
+ written in Python.
+ This provided the catalyst for actually
+ pursuing an idea
+ that had been floating around one of the more
+ intriguing &Make; alternatives,
+ a Perl utility called &Cons;.
+ What if the friendlier syntax of Python
+ could be married to the
+ architectural advantages of &Cons;?
+
+ </para>
+
+ <para>
+
+ The resulting merged design, at that time named &ScCons;,
+ won the Software Carpentry build tool competition. CodeSourcery (by
+ then the administrators of the competition) ultimately decided not to
+ fund development of the build tool, but the seed had been planted and the
+ design had taken root.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Cons</title>
+
+ <para>
+
+ It helps to know something about &Cons;.
+ &Cons; was first released in 1996 by Bob Sidebotham,
+ then an employee of Fore Systems,
+ and it has a number of
+ distinctive features that set it apart from most &Make;-alikes:
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ &Cons; "configuration files" are not Yet Another
+ invented mini-language, but are actually <emphasis>Perl
+ scripts</emphasis>, which means the full power and flexibility of
+ a real scripting language can be applied to build problems.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ &Cons; builds everything from a single process at the top of the
+ source tree, with a global view of the dependencies.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ &Cons; scans files automatically for dependencies such as
+ files specified on <literal>#include</literal> lines.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ &Cons; decides if a file was out-of-date by using MD5 checksums of
+ the contents of files, not timestamps.
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>
+
+ Despite all of these intriguing architectural features, the great
+ strength of &Cons;&mdash;being written in Perl&mdash;was also one of
+ its weaknesses, turning away many potential users due to the
+ (real or perceived) steep learning curve of Perl.
+
+ </para>
+
+</section>
+
+<section>
+ <title>&SCons;</title>
+
+ <para>
+
+ Through the &ScCons; contest entry,
+ &SCons; is the direct descendant of the &Cons; architecture,
+ and is currently
+ under active, supported development with a growing body of
+ users. Its first release was 13 December 2001, under the simple and
+ non-restrictive MIT license, and from the outset, the goal of the
+ members of the &SCons; project has been to deliver a stable, reliable
+ tool that can be used for industrial-strength software builds.
+
+ </para>
+
+ <para>
+
+ The rest of this paper will give an overview of the &SCons; design
+ (including its architecture and interface), describe the development
+ methodology used, and discuss future directions for &SCons;.
+
+ </para>
+
+</section>
diff --git a/doc/python10/job-task.eps b/doc/python10/job-task.eps
new file mode 100644
index 0000000..b3eeaff
--- /dev/null
+++ b/doc/python10/job-task.eps
@@ -0,0 +1,238 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: build/doc/python10/job-task.fig
+%%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d
+%%CreationDate: Sun Jan 2 01:21:05 2005
+%%For: knight@casablanca.home.baldmt.com (Steven Knight)
+%%BoundingBox: 0 0 416 236
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 236 moveto 0 0 lineto 416 0 lineto 416 236 lineto closepath clip newpath
+-35.3 342.7 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%
+% Fig objects follow
+%
+% Polyline
+7.500 slw
+n 4200 3900 m 5100 3900 l 5100 4500 l 4200 4500 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+4650 4275 m
+gs 1 -1 sc (Task) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 4200 5100 m 5100 5100 l 5100 5700 l 4200 5700 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+4650 5475 m
+gs 1 -1 sc (Node) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 6300 2100 m 7200 2100 l 7200 2700 l 6300 2700 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+6750 2475 m
+gs 1 -1 sc (Sig) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 6300 3300 m 7500 3300 l 7500 3900 l 6300 3900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+6900 3675 m
+gs 1 -1 sc (Walker) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 4200 2100 m 5700 2100 l 5700 2700 l 4200 2700 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+4950 2475 m
+gs 1 -1 sc (TaskMaster) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 2400 3300 m 3600 3300 l 3600 3900 l 2400 3900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+3000 3675 m
+gs 1 -1 sc (Parallel) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 600 3300 m 1800 3300 l 1800 3900 l 600 3900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+1200 3675 m
+gs 1 -1 sc (Serial) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 1200 2100 m 3000 2100 l 3000 2700 l 1200 2700 l
+ cp gs col0 s gr
+/Times-Roman ff 255.00 scf sf
+2099 2475 m
+gs 1 -1 sc (Jobs) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 2700 2700 m 2660 2775 l 2700 2850 l 2740 2775 l
+ cp gs col0 s gr
+% Polyline
+n 5700 2400 m 5775 2440 l 5850 2400 l 5775 2360 l
+ cp gs col0 s gr
+% Polyline
+n 5400 2700 m 5360 2775 l 5400 2850 l 5440 2775 l
+ cp gs col0 s gr
+% Polyline
+n 4650 2700 m 4610 2775 l 4650 2850 l 4690 2775 l
+ cp gs col0 s gr
+% Polyline
+n 1500 2700 m 1460 2775 l 1500 2850 l 1540 2775 l
+ cp gs col0 s gr
+% Polyline
+n 4650 4500 m 4610 4575 l 4650 4650 l 4690 4575 l
+ cp gs col0 s gr
+% Polyline
+gs clippath
+1470 3315 m 1530 3315 l 1530 3164 l 1500 3284 l 1470 3164 l cp
+eoclip
+n 1500 2850 m
+ 1500 3300 l gs col0 s gr gr
+
+% arrowhead
+n 1470 3164 m 1500 3284 l 1530 3164 l 1470 3164 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+2670 3315 m 2730 3315 l 2730 3164 l 2700 3284 l 2670 3164 l cp
+eoclip
+n 2700 2850 m
+ 2700 3300 l gs col0 s gr gr
+
+% arrowhead
+n 2670 3164 m 2700 3284 l 2730 3164 l 2670 3164 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4620 3915 m 4680 3915 l 4680 3764 l 4650 3884 l 4620 3764 l cp
+eoclip
+n 4650 2850 m
+ 4650 3900 l gs col0 s gr gr
+
+% arrowhead
+n 4620 3764 m 4650 3884 l 4680 3764 l 4620 3764 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4620 5115 m 4680 5115 l 4680 4964 l 4650 5084 l 4620 4964 l cp
+eoclip
+n 4650 4650 m
+ 4650 5100 l gs col0 s gr gr
+
+% arrowhead
+n 4620 4964 m 4650 5084 l 4680 4964 l 4620 4964 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+6315 3630 m 6315 3570 l 6164 3570 l 6284 3600 l 6164 3630 l cp
+eoclip
+n 5400 2850 m 5400 3600 l
+ 6300 3600 l gs col0 s gr gr
+
+% arrowhead
+n 6164 3630 m 6284 3600 l 6164 3570 l 6164 3630 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+6315 2430 m 6315 2370 l 6164 2370 l 6284 2400 l 6164 2430 l cp
+eoclip
+n 5850 2400 m
+ 6300 2400 l gs col0 s gr gr
+
+% arrowhead
+n 6164 2430 m 6284 2400 l 6164 2370 l 6164 2430 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 3000 2400 m 3075 2440 l 3150 2400 l 3075 2360 l
+ cp gs col0 s gr
+% Polyline
+gs clippath
+4215 2430 m 4215 2370 l 4064 2370 l 4184 2400 l 4064 2430 l cp
+eoclip
+n 3150 2400 m
+ 4200 2400 l gs col0 s gr gr
+
+% arrowhead
+n 4064 2430 m 4184 2400 l 4064 2370 l 4064 2430 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [60] 0 sd
+n 2100 2100 m
+ 2100 1800 l gs col0 s gr [] 0 sd
+% Polyline
+ [60] 0 sd
+n 4950 2100 m
+ 4950 1800 l gs col0 s gr [] 0 sd
+% Polyline
+ [60] 0 sd
+n 6750 2100 m
+ 6750 1800 l gs col0 s gr [] 0 sd
+$F2psEnd
+rs
diff --git a/doc/python10/job-task.fig b/doc/python10/job-task.fig
new file mode 100644
index 0000000..6febd97
--- /dev/null
+++ b/doc/python10/job-task.fig
@@ -0,0 +1,90 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 4200 3900 5100 4500
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4200 3900 5100 3900 5100 4500 4200 4500 4200 3900
+4 1 0 50 0 0 16 0.0000 4 165 465 4650 4275 Task\001
+-6
+6 4200 5100 5100 5700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4200 5100 5100 5100 5100 5700 4200 5700 4200 5100
+4 1 0 50 0 0 16 0.0000 4 165 525 4650 5475 Node\001
+-6
+6 6300 2100 7200 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 6300 2100 7200 2100 7200 2700 6300 2700 6300 2100
+4 1 0 50 0 0 16 0.0000 4 225 330 6750 2475 Sig\001
+-6
+6 6300 3300 7500 3900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 6300 3300 7500 3300 7500 3900 6300 3900 6300 3300
+4 1 0 50 0 0 16 0.0000 4 165 735 6900 3675 Walker\001
+-6
+6 4200 2100 5700 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4200 2100 5700 2100 5700 2700 4200 2700 4200 2100
+4 1 0 50 0 0 16 0.0000 4 165 1155 4950 2475 TaskMaster\001
+-6
+6 2400 3300 3600 3900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2400 3300 3600 3300 3600 3900 2400 3900 2400 3300
+4 1 0 50 0 0 16 0.0000 4 165 765 3000 3675 Parallel\001
+-6
+6 600 3300 1800 3900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 600 3300 1800 3300 1800 3900 600 3900 600 3300
+4 1 0 50 0 0 16 0.0000 4 165 585 1200 3675 Serial\001
+-6
+6 1200 2100 3000 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1200 2100 3000 2100 3000 2700 1200 2700 1200 2100
+4 1 0 50 0 0 17 0.0000 4 165 420 2099 2475 Jobs\001
+-6
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 2700 2700 2660 2775 2700 2850 2740 2775 2700 2700
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 5700 2400 5775 2440 5850 2400 5775 2360 5700 2400
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 5400 2700 5360 2775 5400 2850 5440 2775 5400 2700
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 4650 2700 4610 2775 4650 2850 4690 2775 4650 2700
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 1500 2700 1460 2775 1500 2850 1540 2775 1500 2700
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 4650 4500 4610 4575 4650 4650 4690 4575 4650 4500
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1500 2850 1500 3300
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2700 2850 2700 3300
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4650 2850 4650 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4650 4650 4650 5100
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 1 1 1.00 60.00 120.00
+ 5400 2850 5400 3600 6300 3600
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 5850 2400 6300 2400
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3000 2400 3075 2440 3150 2400 3075 2360 3000 2400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3150 2400 4200 2400
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 2100 2100 2100 1800
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 4950 2100 4950 1800
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 6750 2100 6750 1800
diff --git a/doc/python10/job-task.jpg b/doc/python10/job-task.jpg
new file mode 100644
index 0000000..ff3ded0
--- /dev/null
+++ b/doc/python10/job-task.jpg
Binary files differ
diff --git a/doc/python10/main.xml b/doc/python10/main.xml
new file mode 100644
index 0000000..42bc4af
--- /dev/null
+++ b/doc/python10/main.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+
+ <!ENTITY % scons SYSTEM "scons.mod">
+ %scons;
+
+ <!ENTITY abstract SYSTEM "abstract.xml">
+ <!ENTITY acks SYSTEM "acks.xml">
+ <!ENTITY copyright SYSTEM "copyright.xml">
+ <!ENTITY design SYSTEM "design.xml">
+ <!ENTITY future SYSTEM "future.xml">
+ <!ENTITY install SYSTEM "install.xml">
+ <!ENTITY intro SYSTEM "intro.xml">
+ <!ENTITY process SYSTEM "process.xml">
+
+]>
+
+<article>
+ <articleinfo>
+ <title>SCons Design and Implementation</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <copyright>
+ <year>2001</year>
+ <year>2002</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <pubdate>2002</pubdate>
+
+ <confgroup>
+ <confdates>4-7 February 2002</confdates>
+ <conftitle>The Tenth International Python Conference</conftitle>
+ <address>Alexandria, Virginia</address>
+ </confgroup>
+
+ <revhistory>
+ <revision>
+ <revnumber>0.2</revnumber>
+ <date>16 December 2001</date>
+ <revremark>Internal re-review.</revremark>
+ </revision>
+ <revision>
+ <revnumber>0.1</revnumber>
+ <date>8 October 2001</date>
+ <revremark>Submitted for Python10 conference.</revremark>
+ </revision>
+ </revhistory>
+
+ </articleinfo>
+
+ <abstract>
+ &abstract;
+ </abstract>
+
+ <section id="sect-intro">
+ <title>Introduction</title>
+ &intro;
+ </section>
+
+ <section id="sect-design">
+ <title>Architecture</title>
+ &design;
+ </section>
+
+ <section id="sect-install">
+ <title>Installation</title>
+ &install;
+ </section>
+
+ <section id="sect-process">
+ <title>Development Process</title>
+ &process;
+ </section>
+
+ <section id="sect-future">
+ <title>Future Directions</title>
+ &future;
+ </section>
+
+ <section id="sect-summary">
+ <title>Summary</title>
+ <para>
+
+ This paper has introduced &SCons;, a next-generation build tool
+ with a modular, embeddable architecture and a direct Python
+ interface. &SCons; has a global view of the dependencies in a source
+ tree, uses MD5 signatures to decide if derived files are out of date,
+ and automatically scans files for dependencies, all of which make &SCons;
+ builds exceptionally reliable. The &SCons; development methodology has
+ been described, notable for its emphasis on automated regression
+ testing to ensure a robust and reliable tool from day one. Several
+ future directions for &SCons; have also been discussed.
+
+ </para>
+ </section>
+
+ <section id="sect-acks">
+ <title>Acknowledgements</title>
+ &acks;
+ </section>
+
+ <!--
+ <section id="sect-refs">
+ <title>References</title>
+ <para>
+ </para>
+ </section>
+ -->
+
+ <bibliography id="sect-refs">
+ <title>References</title>
+
+ <biblioentry>
+ <abbrev>1</abbrev>
+ <authorgroup>
+ <author><firstname>Stuart I.</firstname><surname>Feldman</surname></author>
+ </authorgroup>
+ <copyright>
+ <year>Aug 1978</year>
+ </copyright>
+ <publisher>
+ <publishername>Bell Laboratories</publishername>
+ </publisher>
+ <title>Make - A Program for Maintaining Computer Programs</title>
+ </biblioentry>
+
+ <biblioentry>
+ <abbrev>2</abbrev>
+ <authorgroup>
+ <author><firstname>Peter</firstname><surname>Miller</surname></author>
+ </authorgroup>
+ <copyright>
+ <year>1997</year>
+ <holder>Peter Miller</holder>
+ </copyright>
+ <title>Recursive Make Considered Harmful</title>
+ <!--http://www.pcug.org.au/~millerp/rmch/recu-make-cons-harm.html-->
+ </biblioentry>
+
+ <biblioentry>
+ <abbrev>3</abbrev>
+ <authorgroup>
+ <author><firstname>Andrew</firstname><surname>Oram</surname></author>
+ <author><firstname>Steve</firstname><surname>Talbott</surname></author>
+ </authorgroup>
+ <copyright>
+ <year>1986</year>
+ <year>1991</year>
+ <holder>O'Reilly &amp; Associates, Inc.</holder>
+ </copyright>
+ <publisher>
+ <publishername>O'Reilly & Associates, Inc.</publishername>
+ </publisher>
+ <title>Managing Projects with Make, 2nd Ed.</title>
+ </biblioentry>
+
+ <biblioentry>
+ <abbrev>4</abbrev>
+ <authorgroup>
+ <author><firstname>Richard M.</firstname><surname>Stallman</surname></author>
+ <author><firstname>Roland</firstname><surname>McGrath</surname></author>
+ </authorgroup>
+ <copyright>
+ <year>1988</year>
+ <year>'89</year>
+ <year>'90</year>
+ <year>'91</year>
+ <year>'92</year>
+ <year>'93</year>
+ <year>'94</year>
+ <year>'95</year>
+ <year>'96</year>
+ <year>'97</year>
+ <year>'98</year>
+ <year>'99</year>
+ <year>2000</year>
+ <holder>Free Software Foundation, Inc.</holder>
+ </copyright>
+ <publisher>
+ <publishername>Free Software Foundation, Inc.</publishername>
+ </publisher>
+ <title>GNU Make</title>
+ <subtitle>A Program for Directing Recompilation</subtitle>
+ </biblioentry>
+
+ </bibliography>
+
+</article>
diff --git a/doc/python10/node.eps b/doc/python10/node.eps
new file mode 100644
index 0000000..995235d
--- /dev/null
+++ b/doc/python10/node.eps
@@ -0,0 +1,351 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: build/doc/python10/node.fig
+%%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d
+%%CreationDate: Sun Jan 2 01:21:05 2005
+%%For: knight@casablanca.home.baldmt.com (Steven Knight)
+%%BoundingBox: 0 0 452 362
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 362 moveto 0 0 lineto 452 0 lineto 452 362 lineto closepath clip newpath
+0.7 414.7 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%
+% Fig objects follow
+%
+% Polyline
+7.500 slw
+n 2700 1200 m 4500 1200 l 4500 1800 l 2700 1800 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+2925 1575 m
+gs 1 -1 sc (Environment) col0 sh gr
+% Polyline
+n 2700 3600 m 4500 3600 l 4500 4200 l 2700 4200 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+3375 3975 m
+gs 1 -1 sc (Node) col0 sh gr
+% Polyline
+n 5700 1800 m 6900 1800 l 6900 2400 l 5700 2400 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+5925 2175 m
+gs 1 -1 sc (Walker) col0 sh gr
+% Polyline
+n 2100 2400 m 3300 2400 l 3300 3000 l 2100 3000 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+2325 2775 m
+gs 1 -1 sc (Builder) col0 sh gr
+% Polyline
+n 3900 2400 m 5100 2400 l 5100 3000 l 3900 3000 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+4125 2775 m
+gs 1 -1 sc (Scanner) col0 sh gr
+% Polyline
+n 2400 6300 m 3300 6300 l 3300 6900 l 2400 6900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+2700 6675 m
+gs 1 -1 sc (Dir) col0 sh gr
+% Polyline
+n 0 6300 m 900 6300 l 900 6900 l 0 6900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+150 6675 m
+gs 1 -1 sc (Entry) col0 sh gr
+% Polyline
+n 1200 6300 m 2100 6300 l 2100 6900 l 1200 6900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+1425 6675 m
+gs 1 -1 sc (File) col0 sh gr
+% Polyline
+n 1050 5100 m 2250 5100 l 2250 5700 l 1050 5700 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+1200 5475 m
+gs 1 -1 sc (Node.FS) col0 sh gr
+% Polyline
+n 1650 5700 m 1575 5850 l 1725 5850 l
+ cp gs col0 s gr
+% Polyline
+n 450 6300 m 450 6000 l 2700 6000 l
+ 2700 6300 l gs col0 s gr
+% Polyline
+n 1650 6300 m
+ 1650 5850 l gs col0 s gr
+% Polyline
+ [60] 0 sd
+n 5100 6300 m 6300 6300 l 6300 6900 l 5100 6900 l
+ cp gs col0 s gr [] 0 sd
+/Times-Roman ff 240.00 scf sf
+5325 6675 m
+gs 1 -1 sc (Record) col0 sh gr
+% Polyline
+ [60] 0 sd
+n 6600 6300 m 7500 6300 l 7500 6900 l 6600 6900 l
+ cp gs col0 s gr [] 0 sd
+/Times-Roman ff 240.00 scf sf
+6750 6675 m
+gs 1 -1 sc (Field) col0 sh gr
+% Polyline
+ [60] 0 sd
+n 4950 5100 m 6150 5100 l 6150 5700 l 4950 5700 l
+ cp gs col0 s gr [] 0 sd
+/Times-Roman ff 240.00 scf sf
+5100 5475 m
+gs 1 -1 sc (Node.DB) col0 sh gr
+% Polyline
+n 5550 5700 m 5475 5850 l 5625 5850 l
+ cp gs col0 s gr
+% Polyline
+ [60] 0 sd
+n 4350 6300 m 4350 6000 l 7050 6000 l
+ 7050 6300 l gs col0 s gr [] 0 sd
+% Polyline
+ [60] 0 sd
+n 5550 5850 m
+ 5550 6300 l gs col0 s gr [] 0 sd
+% Polyline
+ [60] 0 sd
+n 3900 6300 m 4800 6300 l 4800 6900 l 3900 6900 l
+ cp gs col0 s gr [] 0 sd
+/Times-Roman ff 240.00 scf sf
+4050 6675 m
+gs 1 -1 sc (Table) col0 sh gr
+% Polyline
+n 5700 3000 m 6900 3000 l 6900 3600 l 5700 3600 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+5850 3375 m
+gs 1 -1 sc (Wrapper) col0 sh gr
+% Polyline
+n 900 1200 m 1800 1200 l 1800 1800 l 900 1800 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+1200 1575 m
+gs 1 -1 sc (FS) col0 sh gr
+% Polyline
+n 3600 4200 m 3525 4350 l 3675 4350 l
+ cp gs col0 s gr
+% Polyline
+n 1800 5100 m 1800 4800 l 5550 4800 l
+ 5550 5100 l gs col0 s gr
+% Polyline
+n 3600 4800 m
+ 3600 4350 l gs col0 s gr
+% Polyline
+n 4200 1800 m 4160 1875 l 4200 1950 l 4240 1875 l
+ cp gs col0 s gr
+% Polyline
+n 3000 6150 m 2960 6225 l 3000 6300 l 3040 6225 l
+ cp gs col0 s gr
+% Polyline
+n 6300 3600 m 6260 3675 l 6300 3750 l 6340 3675 l
+ cp gs col0 s gr
+% Polyline
+n 6300 2400 m 6260 2475 l 6300 2550 l 6340 2475 l
+ cp gs col0 s gr
+% Polyline
+n 3000 4200 m 2960 4275 l 3000 4350 l 3040 4275 l
+ cp gs col0 s gr
+% Polyline
+n 4200 3450 m 4160 3525 l 4200 3600 l 4240 3525 l
+ cp gs col0 s gr
+% Polyline
+n 3000 3450 m 2960 3525 l 3000 3600 l 3040 3525 l
+ cp gs col0 s gr
+% Polyline
+gs clippath
+2235 5370 m 2235 5430 l 2386 5430 l 2266 5400 l 2386 5370 l cp
+eoclip
+n 3000 6150 m 3000 5400 l
+ 2250 5400 l gs col0 s gr gr
+
+% arrowhead
+n 2386 5370 m 2266 5400 l 2386 5430 l 2386 5370 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+2715 3930 m 2715 3870 l 2564 3870 l 2684 3900 l 2564 3930 l cp
+eoclip
+n 3000 4350 m 3000 4500 l 1800 4500 l 1800 3900 l
+ 2700 3900 l gs col0 s gr gr
+
+% arrowhead
+n 2564 3930 m 2684 3900 l 2564 3870 l 2564 3930 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4485 3870 m 4485 3930 l 4636 3930 l 4516 3900 l 4636 3870 l cp
+eoclip
+n 6300 3750 m 6300 3900 l
+ 4500 3900 l gs col0 s gr gr
+
+% arrowhead
+n 4636 3870 m 4516 3900 l 4636 3930 l 4636 3870 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4230 2985 m 4170 2985 l 4170 3136 l 4200 3016 l 4230 3136 l cp
+eoclip
+n 4200 3450 m
+ 4200 3000 l gs col0 s gr gr
+
+% arrowhead
+n 4230 3136 m 4200 3016 l 4170 3136 l 4230 3136 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+3030 2985 m 2970 2985 l 2970 3136 l 3000 3016 l 3030 3136 l cp
+eoclip
+n 3000 3450 m
+ 3000 3000 l gs col0 s gr gr
+
+% arrowhead
+n 3030 3136 m 3000 3016 l 2970 3136 l 3030 3136 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 3000 1800 m 2960 1875 l 3000 1950 l 3040 1875 l
+ cp gs col0 s gr
+% Polyline
+gs clippath
+2970 2415 m 3030 2415 l 3030 2264 l 3000 2384 l 2970 2264 l cp
+eoclip
+n 3000 1950 m
+ 3000 2400 l gs col0 s gr gr
+
+% arrowhead
+n 2970 2264 m 3000 2384 l 3030 2264 l 2970 2264 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4170 2415 m 4230 2415 l 4230 2264 l 4200 2384 l 4170 2264 l cp
+eoclip
+n 4200 1950 m
+ 4200 2400 l gs col0 s gr gr
+
+% arrowhead
+n 4170 2264 m 4200 2384 l 4230 2264 l 4170 2264 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+6270 3015 m 6330 3015 l 6330 2864 l 6300 2984 l 6270 2864 l cp
+eoclip
+n 6300 2550 m
+ 6300 3000 l gs col0 s gr gr
+
+% arrowhead
+n 6270 2864 m 6300 2984 l 6330 2864 l 6270 2864 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+4785 6570 m 4785 6630 l 4936 6630 l 4816 6600 l 4936 6570 l cp
+eoclip
+n 5100 6600 m
+ 4800 6600 l gs col0 s gr gr
+
+% arrowhead
+n 4936 6570 m 4816 6600 l 4936 6630 l 4936 6570 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+6285 6570 m 6285 6630 l 6436 6630 l 6316 6600 l 6436 6570 l cp
+eoclip
+n 6600 6600 m
+ 6300 6600 l gs col0 s gr gr
+
+% arrowhead
+n 6436 6570 m 6316 6600 l 6436 6630 l 6436 6570 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 1350 1800 m 1310 1875 l 1350 1950 l 1390 1875 l
+ cp gs col0 s gr
+% Polyline
+gs clippath
+1320 5115 m 1380 5115 l 1380 4964 l 1350 5084 l 1320 4964 l cp
+eoclip
+n 1350 1950 m
+ 1350 5100 l gs col0 s gr gr
+
+% arrowhead
+n 1320 4964 m 1350 5084 l 1380 4964 l 1320 4964 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [60] 0 sd
+n 1350 1200 m
+ 1350 900 l gs col0 s gr [] 0 sd
+% Polyline
+ [60] 0 sd
+n 3600 1200 m
+ 3600 900 l gs col0 s gr [] 0 sd
+$F2psEnd
+rs
diff --git a/doc/python10/node.fig b/doc/python10/node.fig
new file mode 100644
index 0000000..3c40f07
--- /dev/null
+++ b/doc/python10/node.fig
@@ -0,0 +1,165 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 2700 1200 4500 1800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 1200 4500 1200 4500 1800 2700 1800 2700 1200
+4 0 0 50 0 0 16 0.0000 4 165 1290 2925 1575 Environment\001
+-6
+6 2700 3600 4500 4200
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 3600 4500 3600 4500 4200 2700 4200 2700 3600
+4 0 0 50 0 0 16 0.0000 4 165 525 3375 3975 Node\001
+-6
+6 5700 1800 6900 2400
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 5700 1800 6900 1800 6900 2400 5700 2400 5700 1800
+4 0 0 50 0 0 16 0.0000 4 165 735 5925 2175 Walker\001
+-6
+6 2100 2400 3300 3000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2100 2400 3300 2400 3300 3000 2100 3000 2100 2400
+4 0 0 50 0 0 16 0.0000 4 165 750 2325 2775 Builder\001
+-6
+6 3900 2400 5100 3000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3900 2400 5100 2400 5100 3000 3900 3000 3900 2400
+4 0 0 50 0 0 16 0.0000 4 165 780 4125 2775 Scanner\001
+-6
+6 0 5100 3300 6900
+6 2400 6300 3300 6900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2400 6300 3300 6300 3300 6900 2400 6900 2400 6300
+4 0 0 50 0 0 16 0.0000 4 165 345 2700 6675 Dir\001
+-6
+6 0 6300 900 6900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 0 6300 900 6300 900 6900 0 6900 0 6300
+4 0 0 50 0 0 16 0.0000 4 225 555 150 6675 Entry\001
+-6
+6 1200 6300 2100 6900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1200 6300 2100 6300 2100 6900 1200 6900 1200 6300
+4 0 0 50 0 0 16 0.0000 4 165 390 1425 6675 File\001
+-6
+6 1050 5100 2250 5700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1050 5100 2250 5100 2250 5700 1050 5700 1050 5100
+4 0 0 50 0 0 16 0.0000 4 165 855 1200 5475 Node.FS\001
+-6
+6 450 5700 2700 6300
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 1650 5700 1575 5850 1725 5850 1650 5700
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 450 6300 450 6000 2700 6000 2700 6300
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 1650 6300 1650 5850
+-6
+-6
+6 3900 5100 7500 6900
+6 5100 6300 6300 6900
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 5100 6300 6300 6300 6300 6900 5100 6900 5100 6300
+4 0 0 50 0 0 16 0.0000 4 165 705 5325 6675 Record\001
+-6
+6 6600 6300 7500 6900
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 6600 6300 7500 6300 7500 6900 6600 6900 6600 6300
+4 0 0 50 0 0 16 0.0000 4 165 510 6750 6675 Field\001
+-6
+6 4950 5100 6150 5700
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 4950 5100 6150 5100 6150 5700 4950 5700 4950 5100
+4 0 0 50 0 0 16 0.0000 4 165 930 5100 5475 Node.DB\001
+-6
+6 4350 5700 7050 6300
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 5550 5700 5475 5850 5625 5850 5550 5700
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 4
+ 4350 6300 4350 6000 7050 6000 7050 6300
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 5550 5850 5550 6300
+-6
+6 3900 6300 4800 6900
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 3900 6300 4800 6300 4800 6900 3900 6900 3900 6300
+4 0 0 50 0 0 16 0.0000 4 165 555 4050 6675 Table\001
+-6
+-6
+6 5700 3000 6900 3600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 5700 3000 6900 3000 6900 3600 5700 3600 5700 3000
+4 0 0 50 0 0 16 0.0000 4 225 870 5850 3375 Wrapper\001
+-6
+6 900 1200 1800 1800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 900 1200 1800 1200 1800 1800 900 1800 900 1200
+4 0 0 50 0 0 16 0.0000 4 165 270 1200 1575 FS\001
+-6
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 3600 4200 3525 4350 3675 4350 3600 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 1800 5100 1800 4800 5550 4800 5550 5100
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3600 4800 3600 4350
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 4200 1800 4160 1875 4200 1950 4240 1875 4200 1800
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3000 6150 2960 6225 3000 6300 3040 6225 3000 6150
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 6300 3600 6260 3675 6300 3750 6340 3675 6300 3600
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 6300 2400 6260 2475 6300 2550 6340 2475 6300 2400
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3000 4200 2960 4275 3000 4350 3040 4275 3000 4200
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 4200 3450 4160 3525 4200 3600 4240 3525 4200 3450
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3000 3450 2960 3525 3000 3600 3040 3525 3000 3450
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 3
+ 1 1 1.00 60.00 120.00
+ 3000 6150 3000 5400 2250 5400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 5
+ 1 1 1.00 60.00 120.00
+ 3000 4350 3000 4500 1800 4500 1800 3900 2700 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 3
+ 1 1 1.00 60.00 120.00
+ 6300 3750 6300 3900 4500 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4200 3450 4200 3000
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3000 3450 3000 3000
+2 3 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3000 1800 2960 1875 3000 1950 3040 1875 3000 1800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3000 1950 3000 2400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4200 1950 4200 2400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6300 2550 6300 3000
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 5100 6600 4800 6600
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6600 6600 6300 6600
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 1350 1800 1310 1875 1350 1950 1390 1875 1350 1800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1350 1950 1350 5100
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 1350 1200 1350 900
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 3600 1200 3600 900
diff --git a/doc/python10/node.jpg b/doc/python10/node.jpg
new file mode 100644
index 0000000..98ceb5e
--- /dev/null
+++ b/doc/python10/node.jpg
Binary files differ
diff --git a/doc/python10/process.xml b/doc/python10/process.xml
new file mode 100644
index 0000000..f1b2479
--- /dev/null
+++ b/doc/python10/process.xml
@@ -0,0 +1,353 @@
+<para>
+
+ The &SCons; project has paid particular attention from day one to the
+ development process. One of the first internal documents produced was
+ a set of Developer's Guidelines to provide a loose framework for what
+ we were trying to accomplish and how we would go about accomplishing
+ it. These Guidelines cover things like:
+
+</para>
+
+<itemizedlist>
+
+ <listitem>
+ <para>
+
+ &SCons; will be written to Python version 1.5.2 (to ensure
+ usability by a wide install base).
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ How &SCons; is be tested: which infrastructure modules to use,
+ what platforms to test on, etc.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Expectations for developers (subscribe to the mailing list,
+ encouraged to register at SourceForge).
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Brief outline of how to use the change management systems (Aegis and
+ CVS) for &SCons; development;.
+
+ </para>
+ </listitem>
+
+</itemizedlist>
+
+<para>
+
+ Establishing these guidelines up front had two purposes: 1)
+ Demonstrate the seriousness of the project to anyone wondering about
+ joining the effort; 2) Give potential developers an idea up front as
+ to whether their development style would mesh with the rest of the
+ project.
+
+</para>
+
+<section>
+ <title>Aegis</title>
+
+ <para>
+
+ One of the most important aspects of the &SCons; development process
+ is the use of Peter Miller's Aegis change management system. I
+ had been using Aegis for personal projects for several years, and
+ found its development methodology vastly improved the quality of my
+ programming. I was consequently committed to using it for &SCons;
+ development.
+
+ </para>
+
+ <para>
+
+ Aegis provides a number of things, including:
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ A flexible source code control and branching model.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ A defined process with separate development, review and
+ integration steps.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ A distributed development model based on distribution of atomic
+ change sets.
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>
+
+ The single most important reason for using Aegis, however, is its
+ management of automated tests as part of the development process.
+
+ </para>
+
+</section>
+
+<section>
+ <title>Testing, Testing, Testing</title>
+
+ <para>
+
+ The &SCons; project has made extensive use of automated tests from day
+ one, taking inspiration mostly from Aegis, partly from the eXtreme
+ Programming model, and with a little home-brew scripting for glue.
+
+ </para>
+
+ <section>
+ <title>Testing Criteria</title>
+
+ <para>
+
+ The underlying criteria for testing changes to the &SCons; code
+ are taken from Aegis:
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ Every change must have one or more new or modified tests
+ checked in along with the code.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ The new code being checked in must pass all of the new and/or
+ modified tests.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ The <emphasis>old</emphasis>, already checked-in code in must
+ <emphasis>fail</emphasis> all of the new and/or modified
+ tests.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ The new code being checked in must pass all unmodified,
+ already checked-in tests.
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>
+
+ In practice, these restrictions can be overridden as necessary­for
+ example, when changing comments or documentation.
+
+ </para>
+
+ <para>
+
+ The criterion that surprises many people is having the old code
+ fail the tests in the change. This makes sure that the new tests
+ or modified tests really do exercise the bug fix or feature being
+ added by the change.
+
+ </para>
+
+ <para>
+
+ Together, these criteria ensure that every newly checked-in
+ version &SCons; conforms to defined behavior, as defined by
+ the tests. Whenever a bug is found, its fix is checked in with
+ a new or modified test that guarantees the bug will not recur
+ in the future. We have already built up a regression test base
+ of almost 90 tests that cover the vast majority of &SCons;'
+ functionality.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Testing Infrastructure</title>
+
+ <para>
+
+ Testing standards are no good if they're too much of a burden for
+ developers, who will at best work around or ignore the testing
+ requirements, or at worst stop contributing code and go join a
+ project that's more fun. To this end, good testing infrastructure
+ that makes it easy to write tests is crucial.
+
+ </para>
+
+ <para>
+
+ &SCons; development uses two development methodologies, one for
+ the individual modules in the build engine, and the other for
+ end-to-end tests of the &SCons; script.
+
+ </para>
+
+ <para>
+
+ For the build engine modules, we use PyUnit. Every change to a
+ build engine module must have a change to its corresponding unit
+ tests, which live side-by-side in a separate file that imports
+ module. As we build up a large body of unit tests, this ensures
+ that the build engine will perform correctly whenever someone uses
+ it in some application other than the &SCons; script itself.
+
+ </para>
+
+ <para>
+
+ For end-to-end script tests, we have developed two modules to make
+ writing tests easy. The first, <filename>TestCmd.py</filename>,
+ is a generic module for
+ testing commands or scripts (in any language, not just Python).
+
+ The second module, <filename>TestScons.py</filename>,
+ is a subclass of the generic
+ <filename>TestCmd.py</filename> module.
+ <filename>TestScons.py</filename>
+ takes care of initialization and
+ displaying error conditions
+ specific to testing &SCons;.
+
+ </para>
+
+ <para>
+
+ In practice, simple tests only
+ need to initialize a test object, use the object to write some
+ input files, run &SCons;, and then check whatever criteria
+ determine whether the test passed or failed. A complete test of
+ the &Program; method, for example, looks like this:
+
+ </para>
+
+ <programlisting>
+ test = TestSCons.TestSCons()
+
+ test.write('SConstruct',
+ """env = Environment()
+ env.Program(target = 'foo', source = 'foo.c')
+ """)
+
+ test.write('foo.c',
+ """
+ int
+ main(int argc, char *argv[])
+ {
+ argv[argc++] = "-"; /* dummy use of args */
+ printf("foo.c successfully compiled\\n");
+ exit (0);
+ }
+ """)
+
+ test.run(arguments = 'foo') # runs SCons
+
+ test.run(program = test.workpath('foo'))
+
+ test.fail_test(test.stdout() != "foo.c successfully compiled\n")
+
+ test.pass_test()
+ </programlisting>
+
+ </section>
+
+</section>
+
+<section>
+ <title>SourceForge</title>
+
+ <para>
+
+ Registration of the &SCons; project was approved at SourceForge on
+ 29 June 2001. Within a week, the initial code base was checked in,
+ mailing lists were created, and the web site was set up. We started
+ making use of the task-list manager to track what we had to finish
+ for initial release.
+
+ </para>
+
+ <para>
+
+ The obvious complication was how to use
+ structured testing methodology of Aegis when SourceForge uses
+ CVS for source control. Not using the SourceForge CVS tree would
+ have had two significant disadvantages: one, missing out on the
+ archiving and central location in the event of disaster; two, people
+ coming to the SourceForge project page wouldn't be able to browse
+ the source. The latter was particularly important in
+ the early stages of development, in order to avoid any impression
+ that this was Yet Another Project that starts with a bang and then
+ dwindles as the initial enthusiasm starts to wear off.
+
+ </para>
+
+ <para>
+
+ The solution was to use the SourceForge CVS repository for read-only
+ access to the source. &SCons; developers are welcome to use CVS for
+ their development, but the changes are <emphasis>not</emphasis>
+ committed to the SourceForge repository. Instead, patches are sent
+ to the integrator for processing through Aegis. When the change
+ has been integrated into the Aegis repository, a home-brew
+ script translates the Aegis change into a virtual shell script
+ of commands that copy the necessary files from Aegis and check them
+ in to CVS at SourceForge.
+
+ </para>
+
+ <para>
+
+ (In practice, write access is not actually disabled for registered
+ developers, but if they do make any changes directly at SourceForge,
+ they can be overwritten at the next Aegis update.)
+
+ </para>
+
+</section>
diff --git a/doc/python10/scanner.eps b/doc/python10/scanner.eps
new file mode 100644
index 0000000..35614f8
--- /dev/null
+++ b/doc/python10/scanner.eps
@@ -0,0 +1,168 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: build/doc/python10/scanner.fig
+%%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d
+%%CreationDate: Sun Jan 2 01:21:05 2005
+%%For: knight@casablanca.home.baldmt.com (Steven Knight)
+%%BoundingBox: 0 0 398 200
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 200 moveto 0 0 lineto 398 0 lineto 398 200 lineto closepath clip newpath
+-17.3 360.7 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%
+% Fig objects follow
+%
+% Polyline
+7.500 slw
+n 2700 5400 m 4500 5400 l 4500 6000 l 2700 6000 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+3000 5775 m
+gs 1 -1 sc (ProgScanner) col0 sh gr
+% Polyline
+n 2700 4200 m 4500 4200 l 4500 4800 l 2700 4800 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+3225 4575 m
+gs 1 -1 sc (Scanner) col0 sh gr
+% Polyline
+n 2700 3000 m 4500 3000 l 4500 3600 l 2700 3600 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+2925 3375 m
+gs 1 -1 sc (Environment) col0 sh gr
+% Polyline
+ [60] 0 sd
+n 5100 5400 m 6900 5400 l 6900 6000 l 5100 6000 l
+ cp gs col0 s gr [] 0 sd
+/Times-Roman ff 240.00 scf sf
+5400 5775 m
+gs 1 -1 sc (JavaScanner) col0 sh gr
+% Polyline
+n 300 5400 m 2100 5400 l 2100 6000 l 300 6000 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+750 5775 m
+gs 1 -1 sc (CScanner) col0 sh gr
+% Polyline
+n 600 3300 m 1500 3300 l 1500 3900 l 600 3900 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+825 3675 m
+gs 1 -1 sc (Node) col0 sh gr
+% Polyline
+n 1200 5400 m 1200 5100 l 6000 5100 l
+ 6000 5400 l gs col0 s gr
+% Polyline
+n 3600 4950 m
+ 3600 5400 l gs col0 s gr
+% Polyline
+n 3600 4800 m 3525 4950 l 3675 4950 l
+ cp gs col0 s gr
+% Polyline
+n 3600 3600 m 3560 3675 l 3600 3750 l 3640 3675 l
+ cp gs col0 s gr
+% Polyline
+n 1050 3900 m 1010 3975 l 1050 4050 l 1090 3975 l
+ cp gs col0 s gr
+% Polyline
+gs clippath
+2715 4530 m 2715 4470 l 2564 4470 l 2684 4500 l 2564 4530 l cp
+eoclip
+n 1050 4050 m 1050 4500 l
+ 2700 4500 l gs col0 s gr gr
+
+% arrowhead
+n 2564 4530 m 2684 4500 l 2564 4470 l 2564 4530 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+3570 4215 m 3630 4215 l 3630 4064 l 3600 4184 l 3570 4064 l cp
+eoclip
+n 3600 3750 m
+ 3600 4200 l gs col0 s gr gr
+
+% arrowhead
+n 3570 4064 m 3600 4184 l 3630 4064 l 3570 4064 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [60] 0 sd
+n 3600 3000 m
+ 3600 2700 l gs col0 s gr [] 0 sd
+$F2psEnd
+rs
diff --git a/doc/python10/scanner.fig b/doc/python10/scanner.fig
new file mode 100644
index 0000000..01488e8
--- /dev/null
+++ b/doc/python10/scanner.fig
@@ -0,0 +1,57 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 2700 5400 4500 6000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 5400 4500 5400 4500 6000 2700 6000 2700 5400
+4 0 0 50 0 0 16 0.0000 4 225 1245 3000 5775 ProgScanner\001
+-6
+6 2700 4200 4500 4800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 4200 4500 4200 4500 4800 2700 4800 2700 4200
+4 0 0 50 0 0 16 0.0000 4 165 780 3225 4575 Scanner\001
+-6
+6 2700 3000 4500 3600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 3000 4500 3000 4500 3600 2700 3600 2700 3000
+4 0 0 50 0 0 16 0.0000 4 165 1290 2925 3375 Environment\001
+-6
+6 5100 5400 6900 6000
+2 2 1 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 5100 5400 6900 5400 6900 6000 5100 6000 5100 5400
+4 0 0 50 0 0 16 0.0000 4 165 1200 5400 5775 JavaScanner\001
+-6
+6 300 5400 2100 6000
+2 2 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 300 5400 2100 5400 2100 6000 300 6000 300 5400
+4 0 0 50 0 0 16 0.0000 4 165 945 750 5775 CScanner\001
+-6
+6 600 3300 1500 3900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 600 3300 1500 3300 1500 3900 600 3900 600 3300
+4 0 0 50 0 0 16 0.0000 4 165 525 825 3675 Node\001
+-6
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 4
+ 1200 5400 1200 5100 6000 5100 6000 5400
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 3600 4950 3600 5400
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 3600 4800 3525 4950 3675 4950 3600 4800
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 3600 3600 3560 3675 3600 3750 3640 3675 3600 3600
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 1050 3900 1010 3975 1050 4050 1090 3975 1050 3900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 3
+ 1 1 1.00 60.00 120.00
+ 1050 4050 1050 4500 2700 4500
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3600 3750 3600 4200
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 3600 3000 3600 2700
diff --git a/doc/python10/scanner.jpg b/doc/python10/scanner.jpg
new file mode 100644
index 0000000..08e5dcb
--- /dev/null
+++ b/doc/python10/scanner.jpg
Binary files differ
diff --git a/doc/python10/scons.mod b/doc/python10/scons.mod
new file mode 100644
index 0000000..8df6d2c
--- /dev/null
+++ b/doc/python10/scons.mod
@@ -0,0 +1,428 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ An SCons-specific DTD module, for use with SCons DocBook
+ documentation, that contains names, phrases, acronyms, etc. used
+ throughout the SCons documentation.
+
+-->
+
+
+
+<!--
+
+ Other applications that we reference.
+
+-->
+
+<!ENTITY Aegis "<application>Aegis</application>">
+<!ENTITY Ant "<application>Ant</application>">
+<!ENTITY Autoconf "<application>Autoconf</application>">
+<!ENTITY Automake "<application>Automake</application>">
+<!ENTITY cc "<application>cc</application>">
+<!ENTITY Cons "<application>Cons</application>">
+<!ENTITY cp "<application>cp</application>">
+<!ENTITY csh "<application>csh</application>">
+<!ENTITY gcc "<application>gcc</application>">
+<!ENTITY Jam "<application>Jam</application>">
+<!ENTITY jar "<application>jar</application>">
+<!ENTITY javac "<application>javac</application>">
+<!ENTITY javah "<application>javah</application>">
+<!ENTITY Make "<application>Make</application>">
+<!ENTITY Makepp "<application>Make++</application>">
+<!ENTITY Python "<application>Python</application>">
+<!ENTITY ranlib "<application>ranlib</application>">
+<!ENTITY rmic "<application>rmic</application>">
+<!ENTITY SCons "<application>SCons</application>">
+<!ENTITY scons "<application>scons</application>">
+<!ENTITY ScCons "<application>ScCons</application>">
+<!ENTITY tar "<application>tar</application>">
+<!ENTITY touch "<application>touch</application>">
+<!ENTITY zip "<application>zip</application>">
+
+
+<!--
+
+ Classes.
+
+-->
+
+<!ENTITY Action "<classname>Action</classname>">
+<!ENTITY ActionBase "<classname>ActionBase</classname>">
+<!ENTITY CommandAction "<classname>CommandAction</classname>">
+<!ENTITY FunctionAction "<classname>FunctionAction</classname>">
+<!ENTITY ListAction "<classname>ListAction</classname>">
+<!ENTITY Builder "<classname>Builder</classname>">
+<!ENTITY BuilderBase "<classname>BuilderBase</classname>">
+<!ENTITY CompositeBuilder "<classname>CompositeBuilder</classname>">
+<!ENTITY MultiStepBuilder "<classname>MultiStepBuilder</classname>">
+<!ENTITY Job "<classname>Job</classname>">
+<!ENTITY Jobs "<classname>Jobs</classname>">
+<!ENTITY Serial "<classname>Serial</classname>">
+<!ENTITY Parallel "<classname>Parallel</classname>">
+<!ENTITY Node "<classname>Node</classname>">
+<!ENTITY Node_FS "<classname>Node.FS</classname>">
+<!ENTITY Scanner "<classname>Scanner</classname>">
+<!ENTITY Sig "<classname>Sig</classname>">
+<!ENTITY Signature "<classname>Signature</classname>">
+<!ENTITY Taskmaster "<classname>Taskmaster</classname>">
+<!ENTITY TimeStamp "<classname>TimeStamp</classname>">
+<!ENTITY Walker "<classname>Walker</classname>">
+<!ENTITY Wrapper "<classname>Wrapper</classname>">
+
+
+
+<!--
+
+ Options, command-line.
+
+-->
+
+<!ENTITY debug-explain "<literal>--debug=explain</literal>">
+<!ENTITY implicit-cache "<literal>--implicit-cache</literal>">
+<!ENTITY implicit-deps-changed "<literal>--implicit-deps-changed</literal>">
+<!ENTITY implicit-deps-unchanged "<literal>--implicit-deps-unchanged</literal>">
+<!ENTITY Q "<literal>-Q</literal>">
+
+<!--
+
+ Options, SConscript-settable.
+
+-->
+
+<!ENTITY implicit_cache "<literal>implicit_cache</literal>">
+<!ENTITY implicit_deps_changed "<literal>implicit_deps_changed</literal>">
+<!ENTITY implicit_deps_unchanged "<literal>implicit_deps_unchanged</literal>">
+
+
+
+<!--
+
+ File and directory names.
+
+-->
+
+<!ENTITY build "<filename>build</filename>">
+<!ENTITY Makefile "<filename>Makefile</filename>">
+<!ENTITY Makefiles "<filename>Makefiles</filename>">
+<!ENTITY SConscript "<filename>SConscript</filename>">
+<!ENTITY SConstruct "<filename>SConstruct</filename>">
+<!ENTITY Sconstruct "<filename>Sconstruct</filename>">
+<!ENTITY sconstruct "<filename>sconstruct</filename>">
+<!ENTITY sconsign "<filename>.sconsign</filename>">
+<!ENTITY src "<filename>src</filename>">
+
+
+
+<!--
+
+ Methods and functions. This includes functions from both
+ the Build Engine and the Native Python Interface.
+
+-->
+
+<!ENTITY Add "<function>Add</function>">
+<!ENTITY AddOptions "<function>AddOptions</function>">
+<!ENTITY Alias "<function>Alias</function>">
+<!ENTITY Aliases "<function>Aliases</function>">
+<!ENTITY Append "<function>Append</function>">
+<!ENTITY BoolOption "<function>BoolOption</function>">
+<!ENTITY Build "<function>Build</function>">
+<!ENTITY CacheDir "<function>CacheDir</function>">
+<!ENTITY Clean "<function>Clean</function>">
+<!ENTITY Clone "<function>Clone</function>">
+<!ENTITY Command "<function>Command</function>">
+<!ENTITY Configure "<function>Configure</function>">
+<!ENTITY Copy "<function>Copy</function>">
+<!ENTITY Default "<function>Default</function>">
+<!ENTITY DefaultRules "<function>DefaultRules</function>">
+<!ENTITY Depends "<function>Depends</function>">
+<!ENTITY Dir "<function>Dir</function>">
+<!ENTITY Entry "<function>Entry</function>">
+<!ENTITY EnumOption "<function>EnumOption</function>">
+<!ENTITY Environment "<function>Environment</function>">
+<!ENTITY Export "<function>Export</function>">
+<!ENTITY File "<function>File</function>">
+<!ENTITY Finish "<function>Finish</function>">
+<!ENTITY GenerateHelpText "<function>GenerateHelpText</function>">
+<!ENTITY Help "<function>Help</function>">
+<!ENTITY Ignore "<function>Ignore</function>">
+<!ENTITY Import "<function>Import</function>">
+<!ENTITY Install "<function>Install</function>">
+<!ENTITY InstallAs "<function>InstallAs</function>">
+<!ENTITY Link "<function>Link</function>">
+<!ENTITY ListOption "<function>ListOption</function>">
+<!ENTITY Local "<function>Local</function>">
+<!ENTITY Module "<function>Module</function>">
+<!ENTITY Objects "<function>Objects</function>">
+<!ENTITY Options "<function>Options</function>">
+<!ENTITY PackageOption "<function>PackageOption</function>">
+<!ENTITY PathOption "<function>PathOption</function>">
+<!ENTITY Precious "<function>Precious</function>">
+<!ENTITY Prepend "<function>Prepend</function>">
+<!ENTITY Replace "<function>Replace</function>">
+<!ENTITY Repository "<function>Repository</function>">
+<!ENTITY Return "<function>Return</function>">
+<!ENTITY RuleSet "<function>RuleSet</function>">
+<!ENTITY Salt "<function>Salt</function>">
+<!ENTITY SetBuildSignatureType "<function>SetBuildSignatureType</function>">
+<!ENTITY SetContentSignatureType "<function>SetContentSignatureType</function>">
+<!ENTITY SourceSignature "<function>SourceSignature</function>">
+<!ENTITY SourceSignatures "<function>SourceSignatures</function>">
+<!ENTITY Split "<function>Split</function>">
+<!ENTITY TargetSignatures "<function>TargetSignatures</function>">
+<!ENTITY Task "<function>Task</function>">
+
+<!-- Environment methods -->
+<!ENTITY subst "<function>subst</function>">
+
+<!-- Configure context functions -->
+<!ENTITY Message "<function>Message</function>">
+<!ENTITY Result "<function>Result</function>">
+<!ENTITY CheckCHeader "<function>CheckCHeader</function>">
+<!ENTITY CheckCXXHeader "<function>CheckCXXHeader</function>">
+<!ENTITY CheckFunc "<function>CheckFunc</function>">
+<!ENTITY CheckHeader "<function>CheckHeader</function>">
+<!ENTITY CheckLib "<function>CheckLib</function>">
+<!ENTITY CheckLibWithHeader "<function>CheckLibWithHeader</function>">
+<!ENTITY CheckType "<function>CheckType</function>">
+<!ENTITY TryAction "<function>TryAction</function>">
+<!ENTITY TryBuild "<function>TryBuild</function>">
+<!ENTITY TryCompile "<function>TryCompile</function>">
+<!ENTITY TryLink "<function>TryLink</function>">
+<!ENTITY TryRun "<function>TryRun</function>">
+
+<!-- Python functions -->
+<!ENTITY str "<function>str</function>">
+<!ENTITY zipfile "<function>zipfile</function>">
+
+<!-- Obsolete, but referenced in old documents. -->
+<!ENTITY Cache "<function>Cache</function>">
+
+
+
+<!--
+
+ Global variables.
+
+-->
+
+<!ENTITY ARGUMENTS "<varname>ARGUMENTS</varname>">
+<!ENTITY BUILD_TARGETS "<varname>BUILD_TARGETS</varname>">
+<!ENTITY COMMAND_LINE_TARGETS "<varname>COMMAND_LINE_TARGETS</varname>">
+<!ENTITY DEFAULT_TARGETS "<varname>DEFAULT_TARGETS</varname>">
+
+
+
+<!--
+
+ Construction variables.
+
+-->
+
+<!ENTITY BUILDERMAP "<varname>BUILDERMAP</varname>">
+<!ENTITY BUILDERS "<varname>BUILDERS</varname>">
+<!ENTITY CC "<varname>CC</varname>">
+<!ENTITY CCFLAGS "<varname>CCFLAGS</varname>">
+<!ENTITY CCCOM "<varname>CCCOM</varname>">
+<!ENTITY COLOR "<varname>COLOR</varname>">
+<!ENTITY COLORS "<varname>COLORS</varname>">
+<!ENTITY CONFIG "<varname>CONFIG</varname>">
+<!ENTITY CPPDEFINES "<varname>CPPDEFINES</varname>">
+<!ENTITY ENV "<varname>ENV</varname>">
+<!ENTITY JAVACLASSDIR "<varname>JAVACLASSDIR</varname>">
+<!ENTITY LIBDIRPREFIX "<varname>LIBDIRPREFIX</varname>">
+<!ENTITY LIBDIRSUFFIX "<varname>LIBDIRSUFFIX</varname>">
+<!ENTITY LIBLINKPREFIX "<varname>LIBLINKPREFIX</varname>">
+<!ENTITY LIBLINKSUFFIX "<varname>LIBLINKSUFFIX</varname>">
+<!ENTITY LIBPATH "<varname>LIBPATH</varname>">
+<!ENTITY LIBS "<varname>LIBS</varname>">
+<!ENTITY LINK "<varname>LINK</varname>">
+<!ENTITY LINKCOM "<varname>LINKCOM</varname>">
+<!ENTITY LINKFLAGS "<varname>LINKFLAGS</varname>">
+<!ENTITY RELEASE "<varname>RELEASE</varname>">
+<!ENTITY RELEASE_BUILD "<varname>RELEASE_BUILD</varname>">
+<!ENTITY SCANNERMAP "<varname>SCANNERMAP</varname>">
+<!ENTITY SCANNERS "<varname>SCANNERS</varname>">
+<!ENTITY TARFLAGS "<varname>TARFLAGS</varname>">
+<!ENTITY TARSUFFIX "<varname>TARSUFFIX</varname>">
+
+
+
+<!--
+
+ Environment variables.
+
+-->
+
+<!ENTITY PATH "<varname>PATH</varname>">
+<!ENTITY PYTHONPATH "<varname>PYTHONPATH</varname>">
+<!ENTITY SCONSFLAGS "<varname>SCONSFLAGS</varname>">
+
+
+
+<!--
+
+ Function and method arguments.
+
+-->
+
+<!ENTITY allowed_values "<varname>allowed_values</varname>">
+<!ENTITY build_dir "<varname>build_dir</varname>">
+<!ENTITY map "<varname>map</varname>">
+<!ENTITY ignorecase "<varname>ignorecase</varname>">
+<!ENTITY options "<varname>options</varname>">
+<!ENTITY exports "<varname>exports</varname>">
+<!ENTITY source "<varname>source</varname>">
+<!ENTITY target "<varname>target</varname>">
+
+
+
+<!--
+
+ Values of function and method arguments.
+
+-->
+
+<!ENTITY all "<literal>all</literal>">
+<!ENTITY none "<literal>none</literal>">
+
+
+
+<!--
+
+ Builder and Scanner objects.
+
+-->
+
+<!ENTITY BuildDir "<function>BuildDir</function>">
+<!ENTITY CFile "<function>CFile</function>">
+<!ENTITY CXXFile "<function>CXXFile</function>">
+<!ENTITY DVI "<function>DVI</function>">
+<!ENTITY Jar "<function>Jar</function>">
+<!ENTITY Java "<function>Java</function>">
+<!ENTITY JavaH "<function>JavaH</function>">
+<!ENTITY Library "<function>Library</function>">
+<!ENTITY Object "<function>Object</function>">
+<!ENTITY PCH "<function>PCH</function>">
+<!ENTITY PDF "<function>PDF</function>">
+<!ENTITY PostScript "<function>PostScript</function>">
+<!ENTITY Program "<function>Program</function>">
+<!ENTITY RES "<function>RES</function>">
+<!ENTITY RMIC "<function>RMIC</function>">
+<!ENTITY SharedLibrary "<function>SharedLibrary</function>">
+<!ENTITY SharedObject "<function>SharedObject</function>">
+<!ENTITY StaticLibrary "<function>StaticLibrary</function>">
+<!ENTITY StaticObject "<function>StaticObject</function>">
+<!ENTITY Tar "<function>Tar</function>">
+<!ENTITY Zip "<function>Zip</function>">
+
+<!-- Obsolete, but referenced in old documents. -->
+<!ENTITY MakeBuilder "<function>Make</function>">
+
+
+
+<!--
+
+ Terms. Define both singular and plural forms in various
+ case-sensitive combinations for use in titles, in-line, etc.
+
+-->
+
+<!ENTITY buildfunc "<literal>builder function</literal>">
+<!ENTITY builder_method "<literal>builder method</literal>">
+
+<!ENTITY Configure_Contexts "<literal>Configure Contexts</literal>">
+<!ENTITY configure_context "<literal>configure context</literal>">
+
+<!ENTITY ConsEnv "<literal>Construction Environment</literal>">
+<!ENTITY ConsEnvs "<literal>Construction Environments</literal>">
+<!ENTITY Consenv "<literal>Construction environment</literal>">
+<!ENTITY Consenvs "<literal>Construction environments</literal>">
+<!ENTITY consenv "<literal>construction environment</literal>">
+<!ENTITY consenvs "<literal>construction environments</literal>">
+
+<!ENTITY ConsVar "<literal>Construction Variable</literal>">
+<!ENTITY ConsVars "<literal>Construction Variables</literal>">
+<!ENTITY Consvar "<literal>Construction variable</literal>">
+<!ENTITY Consvars "<literal>Construction variables</literal>">
+<!ENTITY consvar "<literal>construction variable</literal>">
+<!ENTITY consvars "<literal>construction variables</literal>">
+
+<!ENTITY CPPPATH "<literal>CPPPATH</literal>">
+
+<!ENTITY Dictionary "<literal>Dictionary</literal>">
+
+<!ENTITY Emitter "<literal>Emitter</literal>">
+<!ENTITY emitter "<literal>emitter</literal>">
+<!ENTITY Generator "<literal>Generator</literal>">
+<!ENTITY generator "<literal>generator</literal>">
+
+<!ENTITY Nodes "<literal>Nodes</literal>">
+
+<!ENTITY signature "<literal>signature</literal>">
+<!ENTITY buildsignature "<literal>build signature</literal>">
+
+<!ENTITY true "<literal>true</literal>">
+<!ENTITY false "<literal>false</literal>">
+
+<!ENTITY typedef "<literal>typedef</literal>">
+
+<!--
+
+ File and program names used in examples.
+
+-->
+
+<!ENTITY bar "<application>bar</application>">
+<!ENTITY common1_c "<filename>common1.c</filename>">
+<!ENTITY common2_c "<filename>common2.c</filename>">
+<!ENTITY custom_py "<filename>custom.py</filename>">
+<!ENTITY goodbye "<application>goodbye</application>">
+<!ENTITY goodbye_o "<filename>goodbye.o</filename>">
+<!ENTITY goodbye_obj "<filename>goodbye.obj</filename>">
+<!ENTITY file_dll "<filename>file.dll</filename>">
+<!ENTITY file_in "<filename>file.in</filename>">
+<!ENTITY file_lib "<filename>file.lib</filename>">
+<!ENTITY file_o "<filename>file.o</filename>">
+<!ENTITY file_obj "<filename>file.obj</filename>">
+<!ENTITY file_out "<filename>file.out</filename>">
+<!ENTITY foo "<application>foo</application>">
+<!ENTITY foo_o "<filename>foo.o</filename>">
+<!ENTITY foo_obj "<filename>foo.obj</filename>">
+<!ENTITY hello "<application>hello</application>">
+<!ENTITY hello_c "<filename>hello.c</filename>">
+<!ENTITY hello_exe "<filename>hello.exe</filename>">
+<!ENTITY hello_h "<filename>hello.h</filename>">
+<!ENTITY hello_o "<filename>hello.o</filename>">
+<!ENTITY hello_obj "<filename>hello.obj</filename>">
+<!ENTITY libfile_a "<filename>libfile_a</filename>">
+<!ENTITY libfile_so "<filename>libfile_so</filename>">
+<!ENTITY new_hello "<application>new_hello</application>">
+<!ENTITY new_hello_exe "<application>new_hello.exe</application>">
+<!ENTITY prog "<filename>prog</filename>">
+<!ENTITY prog1 "<filename>prog1</filename>">
+<!ENTITY prog2 "<filename>prog2</filename>">
+<!ENTITY prog_c "<filename>prog.c</filename>">
+<!ENTITY prog_exe "<filename>prog.exe</filename>">
+<!ENTITY stdio_h "<filename>stdio.h</filename>">
+
+<!--
+
+ Punctuation.
+
+-->
+
+<!ENTITY plus "<literal>+</literal>">
+<!ENTITY hash "<literal>#</literal>">
+
+<!--
+
+ Mailing lists
+
+-->
+
+<!ENTITY scons-announce "<literal>announce@scons.tigris.org</literal>">
+<!ENTITY scons-devel "<literal>dev@scons.tigris.org</literal>">
+<!ENTITY scons-users "<literal>users@scons.tigris.org</literal>">
diff --git a/doc/python10/sig.eps b/doc/python10/sig.eps
new file mode 100644
index 0000000..26aabaa
--- /dev/null
+++ b/doc/python10/sig.eps
@@ -0,0 +1,147 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: build/doc/python10/sig.fig
+%%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d
+%%CreationDate: Sun Jan 2 01:21:05 2005
+%%For: knight@casablanca.home.baldmt.com (Steven Knight)
+%%BoundingBox: 0 0 308 128
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 128 moveto 0 0 lineto 308 0 lineto 308 128 lineto closepath clip newpath
+-71.3 288.7 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%
+% Fig objects follow
+%
+% Polyline
+7.500 slw
+n 1200 3000 m 2700 3000 l 2700 3600 l 1200 3600 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+1950 3375 m
+gs 1 -1 sc (Taskmaster) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 3300 4200 m 4500 4200 l 4500 4800 l 3300 4800 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+3900 4575 m
+gs 1 -1 sc (MD5) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 5100 4200 m 6300 4200 l 6300 4800 l 5100 4800 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+5700 4575 m
+gs 1 -1 sc (TStamp) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 4200 3000 m 5400 3000 l 5400 3600 l 4200 3600 l
+ cp gs col0 s gr
+/Times-Roman ff 240.00 scf sf
+4800 3375 m
+gs 1 -1 sc (Sig) dup sw pop 2 div neg 0 rm col0 sh gr
+% Polyline
+n 2700 3300 m 2775 3340 l 2850 3300 l 2775 3260 l
+ cp gs col0 s gr
+% Polyline
+n 4800 3600 m 4725 3750 l 4875 3750 l
+ cp gs col0 s gr
+% Polyline
+n 3900 4200 m 3900 3900 l 5700 3900 l
+ 5700 4200 l gs col0 s gr
+% Polyline
+n 4800 3750 m
+ 4800 3900 l gs col0 s gr
+% Polyline
+gs clippath
+4215 3330 m 4215 3270 l 4064 3270 l 4184 3300 l 4064 3330 l cp
+eoclip
+n 2850 3300 m
+ 4200 3300 l gs col0 s gr gr
+
+% arrowhead
+n 4064 3330 m 4184 3300 l 4064 3270 l 4064 3330 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [60] 0 sd
+n 1950 3000 m
+ 1950 2700 l gs col0 s gr [] 0 sd
+% Polyline
+ [60] 0 sd
+n 4800 3000 m
+ 4800 2700 l gs col0 s gr [] 0 sd
+$F2psEnd
+rs
diff --git a/doc/python10/sig.fig b/doc/python10/sig.fig
new file mode 100644
index 0000000..823d59e
--- /dev/null
+++ b/doc/python10/sig.fig
@@ -0,0 +1,44 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 1200 3000 2700 3600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1200 3000 2700 3000 2700 3600 1200 3600 1200 3000
+4 1 0 50 0 0 16 0.0000 4 165 1125 1950 3375 Taskmaster\001
+-6
+6 3300 4200 4500 4800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3300 4200 4500 4200 4500 4800 3300 4800 3300 4200
+4 1 0 50 0 0 16 0.0000 4 165 525 3900 4575 MD5\001
+-6
+6 5100 4200 6300 4800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 5100 4200 6300 4200 6300 4800 5100 4800 5100 4200
+4 1 0 50 0 0 16 0.0000 4 225 780 5700 4575 TStamp\001
+-6
+6 4200 3000 5400 3600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4200 3000 5400 3000 5400 3600 4200 3600 4200 3000
+4 1 0 50 0 0 16 0.0000 4 225 330 4800 3375 Sig\001
+-6
+2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 2700 3300 2775 3340 2850 3300 2775 3260 2700 3300
+2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4
+ 4800 3600 4725 3750 4875 3750 4800 3600
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 4
+ 3900 4200 3900 3900 5700 3900 5700 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 4800 3750 4800 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2850 3300 4200 3300
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 1950 3000 1950 2700
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 4800 3000 4800 2700
diff --git a/doc/python10/sig.jpg b/doc/python10/sig.jpg
new file mode 100644
index 0000000..0c7e0df
--- /dev/null
+++ b/doc/python10/sig.jpg
Binary files differ
diff --git a/doc/reference/Alias.xml b/doc/reference/Alias.xml
new file mode 100644
index 0000000..b87967d
--- /dev/null
+++ b/doc/reference/Alias.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &Alias; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/CFile.xml b/doc/reference/CFile.xml
new file mode 100644
index 0000000..f76c390
--- /dev/null
+++ b/doc/reference/CFile.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &CFile; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/CXXFile.xml b/doc/reference/CXXFile.xml
new file mode 100644
index 0000000..c1c038e
--- /dev/null
+++ b/doc/reference/CXXFile.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &CXXFile; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/Command.xml b/doc/reference/Command.xml
new file mode 100644
index 0000000..abb3a58
--- /dev/null
+++ b/doc/reference/Command.xml
@@ -0,0 +1,73 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head2 The C<Command> method
+
+The C<Command> method is a catchall method which can be used to arrange for
+any build action to be executed to update the target. For this command, a
+target file and list of inputs is provided. In addition, a build action
+is specified as the last argument. The build action is typically a
+command line or lines, but may also contain Perl code to be executed;
+see the section above on build actions for details.
+
+The C<Command> method is called as follows:
+
+ Command $env <target>, <inputs>, <build action>;
+
+The target is made dependent upon the list of input files specified, and the
+inputs must be built successfully or Cons will not attempt to build the
+target.
+
+To specify a command with multiple targets, you can specify a reference to a
+list of targets. In Perl, a list reference can be created by enclosing a
+list in square brackets. Hence the following command:
+
+ Command $env ['foo.h', 'foo.c'], 'foo.template', q(
+ gen %1
+ );
+
+could be used in a case where the command C<gen> creates two files, both
+F<foo.h> and F<foo.c>.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &Command; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/Install.xml b/doc/reference/Install.xml
new file mode 100644
index 0000000..2d06e3b
--- /dev/null
+++ b/doc/reference/Install.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &Install; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/InstallAs.xml b/doc/reference/InstallAs.xml
new file mode 100644
index 0000000..ed8cb78
--- /dev/null
+++ b/doc/reference/InstallAs.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &InstallAs; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/Library.xml b/doc/reference/Library.xml
new file mode 100644
index 0000000..19a3e96
--- /dev/null
+++ b/doc/reference/Library.xml
@@ -0,0 +1,152 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head2 The C<Library> method
+
+The C<Library> method arranges to create the specified library from the
+specified object files. It is invoked as follows:
+
+ Library $env <library name>, <source or object files>;
+
+The library name will have the value of the C<SUFLIB> construction
+variable appended (by default, C<.lib> on Win32 systems, C<.a> on Unix
+systems) if the suffix is not already present.
+
+Source files may be specified in place of objects files-,-the C<Objects>
+method will be invoked to arrange the conversion of all the files into
+object files, and hence all the observations about the C<Objects> method,
+above, apply to this method also.
+
+The actual creation of the library will be handled by an external
+command which results from expanding the C<ARCOM> construction variable,
+with C<%E<lt>> set to the library members (in the order presented),
+and C<%E<gt>> to the library to be created. (See the section above
+on construction variable expansion for details.) The user may set
+variables in the construction environment which will affect the
+operation of the command. These include C<AR>, the archive program
+to use, C<ARFLAGS>, which can be used to modify the flags given to
+the program specified by C<AR>, and C<RANLIB>, the name of a archive
+index generation program, if needed (if the particular need does not
+require the latter functionality, then C<ARCOM> must be redefined to not
+reference C<RANLIB>).
+
+The C<Library> method allows the same library to be specified in multiple
+method invocations. All of the contributing objects from all the invocations
+(which may be from different directories) are combined and generated by a
+single archive command. Note, however, that if you prune a build so that
+only part of a library is specified, then only that part of the library will
+be generated (the rest will disappear!).
+
+-->
+
+ <section>
+ <title>Linking With a Library</title>
+
+ <programlisting>
+ env = Environment(CC = 'gcc',
+ LIBS = 'world')
+ env.Program('hello.c')
+ </programlisting>
+
+ <literallayout>
+ % <userinput>scons</userinput>
+ gcc -c hello.c -o hello.o
+ gcc -c world.c -o world.o
+ gcc -o hello hello.o -lworld
+ </literallayout>
+
+ </section>
+
+ <section>
+ <title>Creating a Library</title>
+
+ <programlisting>
+ env = Environment(CC = 'gcc',
+ LIBS = 'world')
+ env.Program('hello.c')
+ env.Library('world.c')
+ </programlisting>
+
+ <literallayout>
+ % <userinput>scons</userinput>
+ gcc -c hello.c -o hello.o
+ gcc -c world.c -o world.o
+ ar r libworld.a world.o
+ ar: creating libworld.a
+ ranlib libworld.a
+ gcc -o hello hello.o libworld.a
+ </literallayout>
+
+ </section>
+
+<!--
+
+A key simplification of Cons is the idea of a B<construction environment>. A
+construction environment is an B<object> characterized by a set of key/value
+pairs and a set of B<methods>. In order to tell Cons how to build something,
+you invoke the appropriate method via an appropriate construction
+environment. Consider the following example:
+
+
+
+ $env = new cons(
+ CC => 'gcc',
+ LIBS => 'libworld.a'
+ );
+
+ Program $env 'hello', 'hello.c';
+
+In this case, rather than using the default construction environment, as is,
+we have overridden the value of C<CC> so that the GNU C Compiler equivalent
+is used, instead. Since this version of B<Hello, World!> requires a library,
+F<libworld.a>, we have specified that any program linked in this environment
+should be linked with that library. If the library exists already, well and
+good, but if not, then we'll also have to include the statement:
+
+
+
+ Library $env 'libworld', 'world.c';
+
+Now if you type C<cons hello>, the library will be built before the program
+is linked, and, of course, C<gcc> will be used to compile both modules:
+
+
+
+ % cons hello
+
+-->
+
+ <section>
+ <title>The &Library; Builder</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/MANIFEST b/doc/reference/MANIFEST
new file mode 100644
index 0000000..438aada
--- /dev/null
+++ b/doc/reference/MANIFEST
@@ -0,0 +1,21 @@
+Alias.xml
+CFile.xml
+CXXFile.xml
+Command.xml
+Install.xml
+InstallAs.xml
+Library.xml
+Object.xml
+PCH.xml
+PDF.xml
+PostScript.xml
+Program.xml
+RES.xml
+SharedLibrary.xml
+SharedObject.xml
+StaticLibrary.xml
+StaticObject.xml
+copyright.xml
+errors.xml
+main.xml
+preface.xml
diff --git a/doc/reference/Object.xml b/doc/reference/Object.xml
new file mode 100644
index 0000000..9e887d8
--- /dev/null
+++ b/doc/reference/Object.xml
@@ -0,0 +1,71 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head2 The C<Objects> method
+
+The C<Objects> method arranges to create the object files that correspond to
+the specified source files. It is invoked as shown below:
+
+ @files = Objects $env <source or object files>;
+
+Under Unix, source files ending in F<.s> and F<.c> are currently
+supported, and will be compiled into a name of the same file ending
+in F<.o>. By default, all files are created by invoking the external
+command which results from expanding the C<CCCOM> construction variable,
+with C<%E<lt>> and C<%E<gt>> set to the source and object files,
+respectively. (See the section above on construction variable expansion
+for details). The variable C<CPPPATH> is also used when scanning source
+files for dependencies. This is a colon separated list of pathnames, and
+is also used to create the construction variable C<_IFLAGS,> which will
+contain the appropriate list of -C<I> options for the compilation. Any
+relative pathnames in C<CPPPATH> is interpreted relative to the
+directory in which the associated construction environment was created
+(absolute and top-relative names may also be used). This variable is
+used by C<CCCOM>. The behavior of this command can be modified by
+changing any of the variables which are interpolated into C<CCCOM>, such
+as C<CC>, C<CFLAGS>, and, indirectly, C<CPPPATH>. It's also possible
+to replace the value of C<CCCOM>, itself. As a convenience, this file
+returns the list of object filenames.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &Object; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/PCH.xml b/doc/reference/PCH.xml
new file mode 100644
index 0000000..b2a4d75
--- /dev/null
+++ b/doc/reference/PCH.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &PCH; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/PDF.xml b/doc/reference/PDF.xml
new file mode 100644
index 0000000..b3a25dc
--- /dev/null
+++ b/doc/reference/PDF.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &PDF; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/PostScript.xml b/doc/reference/PostScript.xml
new file mode 100644
index 0000000..f5a6579
--- /dev/null
+++ b/doc/reference/PostScript.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &PostScript; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/Program.xml b/doc/reference/Program.xml
new file mode 100644
index 0000000..30f90d2
--- /dev/null
+++ b/doc/reference/Program.xml
@@ -0,0 +1,77 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head2 The C<Program> method
+
+The C<Program> method arranges to link the specified program with the
+specified object files. It is invoked in the following manner:
+
+ Program $env <program name>, <source or object files>;
+
+The program name will have the value of the C<SUFEXE> construction
+variable appended (by default, C<.exe> on Win32 systems, nothing on Unix
+systems) if the suffix is not already present.
+
+Source files may be specified in place of objects files-,-the C<Objects>
+method will be invoked to arrange the conversion of all the files into
+object files, and hence all the observations about the C<Objects> method,
+above, apply to this method also.
+
+The actual linking of the program will be handled by an external command
+which results from expanding the C<LINKCOM> construction variable, with
+C<%E<lt>> set to the object files to be linked (in the order presented),
+and C<%E<gt>> set to the target. (See the section above on construction
+variable expansion for details.) The user may set additional variables
+in the construction environment, including C<LINK>, to define which
+program to use for linking, C<LIBPATH>, a colon-separated list of
+library search paths, for use with library specifications of the form
+I<-llib>, and C<LIBS>, specifying the list of libraries to link against
+(in either I<-llib> form or just as pathnames. Relative pathnames in
+both C<LIBPATH> and C<LIBS> are interpreted relative to the directory
+in which the associated construction environment is created (absolute
+and top-relative names may also be used). Cons automatically sets up
+dependencies on any libraries mentioned in C<LIBS>: those libraries will
+be built before the command is linked.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &Program; Builder</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/RES.xml b/doc/reference/RES.xml
new file mode 100644
index 0000000..15c0aea
--- /dev/null
+++ b/doc/reference/RES.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &RES; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/SharedLibrary.xml b/doc/reference/SharedLibrary.xml
new file mode 100644
index 0000000..603dab1
--- /dev/null
+++ b/doc/reference/SharedLibrary.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &SharedLibrary; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/SharedObject.xml b/doc/reference/SharedObject.xml
new file mode 100644
index 0000000..0860769
--- /dev/null
+++ b/doc/reference/SharedObject.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &SharedObject; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/StaticLibrary.xml b/doc/reference/StaticLibrary.xml
new file mode 100644
index 0000000..ea7ae5b
--- /dev/null
+++ b/doc/reference/StaticLibrary.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &StaticLibrary; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/StaticObject.xml b/doc/reference/StaticObject.xml
new file mode 100644
index 0000000..ff8dae8
--- /dev/null
+++ b/doc/reference/StaticObject.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>The &StaticObject; Method</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/copyright.xml b/doc/reference/copyright.xml
new file mode 100644
index 0000000..7f6059c
--- /dev/null
+++ b/doc/reference/copyright.xml
@@ -0,0 +1,32 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<blockquote>
+ <para>
+
+ SCons User's Guide Copyright (c) 2003 Steven Knight
+
+ </para>
+</blockquote>
diff --git a/doc/reference/errors.xml b/doc/reference/errors.xml
new file mode 100644
index 0000000..448777f
--- /dev/null
+++ b/doc/reference/errors.xml
@@ -0,0 +1,41 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>X</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/reference/main.xml b/doc/reference/main.xml
new file mode 100644
index 0000000..ed122f6
--- /dev/null
+++ b/doc/reference/main.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+
+ <!ENTITY % version SYSTEM "../version.xml">
+ %version;
+
+ <!ENTITY % scons SYSTEM "../scons.mod">
+ %scons;
+
+ <!-- Builders -->
+ <!ENTITY Alias_file SYSTEM "Alias.xml">
+ <!ENTITY CFile_file SYSTEM "CFile.xml">
+ <!ENTITY CXXFile_file SYSTEM "CXXFile.xml">
+ <!ENTITY Command_file SYSTEM "Command.xml">
+ <!ENTITY Install_file SYSTEM "Install.xml">
+ <!ENTITY InstallAs_file SYSTEM "InstallAs.xml">
+ <!ENTITY Library_file SYSTEM "Library.xml">
+ <!ENTITY Object_file SYSTEM "Object.xml">
+ <!ENTITY PCH_file SYSTEM "PCH.xml">
+ <!ENTITY PDF_file SYSTEM "PDF.xml">
+ <!ENTITY PostScript_file SYSTEM "PostScript.xml">
+ <!ENTITY Program_file SYSTEM "Program.xml">
+ <!ENTITY RES_file SYSTEM "RES.xml">
+ <!ENTITY SharedLibrary_file SYSTEM "SharedLibrary.xml">
+ <!ENTITY SharedObject_file SYSTEM "SharedObject.xml">
+ <!ENTITY StaticLibrary_file SYSTEM "StaticLibrary.xml">
+ <!ENTITY StaticObject_file SYSTEM "StaticObject.xml">
+
+ <!-- Construction Variables -->
+
+ <!ENTITY copyright SYSTEM "copyright.xml">
+ <!ENTITY errors SYSTEM "errors.xml">
+ <!ENTITY preface SYSTEM "preface.xml">
+
+]>
+
+<book>
+ <bookinfo>
+ <title>SCons Reference Manual &buildversion;</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <edition>Revision &buildrevision; (&builddate;)</edition>
+
+ <pubdate>2003</pubdate>
+
+ <copyright>
+ <year>2003</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <legalnotice>
+ &copyright;
+ </legalnotice>
+
+ <releaseinfo>version &buildversion;</releaseinfo>
+
+ </bookinfo>
+
+ <chapter id="chap-preface">
+ <title>Preface</title>
+ &preface;
+ </chapter>
+
+ <chapter id="chap-builders">
+ <title>Builder Reference</title>
+
+ <section id="sect-Alias">
+ <title>The Alias Builder</title>
+ &Alias_file;
+ </section>
+
+ <section id="sect-CFile">
+ <title>The CFile Builder</title>
+ &CFile_file;
+ </section>
+
+ <section id="sect-Command">
+ <title>The Command Builder</title>
+ &Command_file;
+ </section>
+
+ <section id="sect-CXXFile">
+ <title>The CXXFile Builder</title>
+ &CXXFile_file;
+ </section>
+
+ <section id="sect-Install">
+ <title>The Install Builder</title>
+ &Install_file;
+ </section>
+
+ <section id="sect-InstallAs">
+ <title>The InstallAs Builder</title>
+ &InstallAs_file;
+ </section>
+
+ <section id="sect-Library">
+ <title>The Library Builder</title>
+ &Library_file;
+ </section>
+
+ <section id="sect-Object">
+ <title>The Object Builder</title>
+ &Object_file;
+ </section>
+
+ <section id="sect-PCH">
+ <title>The PCH Builder</title>
+ &PCH_file;
+ </section>
+
+ <section id="sect-PDF">
+ <title>The PDF Builder</title>
+ &PDF_file;
+ </section>
+
+ <section id="sect-PostScript">
+ <title>The PDF Builder</title>
+ &PostScript_file;
+ </section>
+
+ <section id="sect-Program">
+ <title>The Program Builder</title>
+ &Program_file;
+ </section>
+
+ <section id="sect-RES">
+ <title>The RES Builder</title>
+ &RES_file;
+ </section>
+
+ <section id="sect-SharedLibrary">
+ <title>The SharedLibrary Builder</title>
+ &SharedLibrary_file;
+ </section>
+
+ <section id="sect-SharedObject">
+ <title>The SharedObject Builder</title>
+ &SharedObject_file;
+ </section>
+
+ <section id="sect-StaticLibrary">
+ <title>The StaticLibrary Builder</title>
+ &StaticLibrary_file;
+ </section>
+
+ <section id="sect-StaticObject">
+ <title>The StaticObject Builder</title>
+ &StaticObject_file;
+ </section>
+
+ </chapter>
+
+ <chapter id="chap-variables">
+ <title>&ConsVar; Reference</title>
+
+ <section id="sect-AR">
+ <title>AR</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
+
+ </chapter>
+
+ <appendix id="chap-errors">
+ <title>Errors Generated by &SCons;</title>
+ &errors;
+ </appendix>
+
+</book>
diff --git a/doc/reference/preface.xml b/doc/reference/preface.xml
new file mode 100644
index 0000000..82ea44a
--- /dev/null
+++ b/doc/reference/preface.xml
@@ -0,0 +1,85 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003 Steven Knight
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ X
+
+ </para>
+
+ <section>
+ <title>Why &SCons;?</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>History</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Conventions</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Acknowledgements</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Contact</title>
+
+ <para>
+
+ X
+
+ </para>
+
+ </section>
diff --git a/doc/scons.mod b/doc/scons.mod
new file mode 100644
index 0000000..8ac58ca
--- /dev/null
+++ b/doc/scons.mod
@@ -0,0 +1,521 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ An SCons-specific DTD module, for use with SCons DocBook
+ documentation, that contains names, phrases, acronyms, etc. used
+ throughout the SCons documentation.
+
+-->
+
+
+
+<!--
+
+ Other applications that we reference.
+
+-->
+
+<!ENTITY Aegis "<application>Aegis</application>">
+<!ENTITY Ant "<application>Ant</application>">
+<!ENTITY ar "<application>ar</application>">
+<!ENTITY as "<application>as</application>">
+<!ENTITY Autoconf "<application>Autoconf</application>">
+<!ENTITY Automake "<application>Automake</application>">
+<!ENTITY bison "<application>bison</application>">
+<!ENTITY cc "<application>cc</application>">
+<!ENTITY Cons "<application>Cons</application>">
+<!ENTITY cp "<application>cp</application>">
+<!ENTITY csh "<application>csh</application>">
+<!ENTITY f77 "<application>f77</application>">
+<!ENTITY f90 "<application>f90</application>">
+<!ENTITY f95 "<application>f95</application>">
+<!ENTITY gas "<application>gas</application>">
+<!ENTITY gcc "<application>gcc</application>">
+<!ENTITY g77 "<application>g77</application>">
+<!ENTITY gXX "<application>gXX</application>">
+<!ENTITY Jam "<application>Jam</application>">
+<!ENTITY jar "<application>jar</application>">
+<!ENTITY javac "<application>javac</application>">
+<!ENTITY javah "<application>javah</application>">
+<!ENTITY latex "<application>latex</application>">
+<!ENTITY lex "<application>lex</application>">
+<!ENTITY m4 "<application>m4</application>">
+<!ENTITY Make "<application>Make</application>">
+<!ENTITY Makepp "<application>Make++</application>">
+<!ENTITY pdflatex "<application>pdflatex</application>">
+<!ENTITY pdftex "<application>pdftex</application>">
+<!ENTITY Python "<application>Python</application>">
+<!ENTITY ranlib "<application>ranlib</application>">
+<!ENTITY rmic "<application>rmic</application>">
+<!ENTITY SCons "<application>SCons</application>">
+<!ENTITY ScCons "<application>ScCons</application>">
+<!ENTITY swig "<application>swig</application>">
+<!ENTITY tar "<application>tar</application>">
+<!ENTITY tex "<application>tex</application>">
+<!ENTITY touch "<application>touch</application>">
+<!ENTITY yacc "<application>yacc</application>">
+<!ENTITY zip "<application>zip</application>">
+
+
+<!--
+
+ Classes.
+
+-->
+
+<!ENTITY Action "<classname>Action</classname>">
+<!ENTITY ActionBase "<classname>ActionBase</classname>">
+<!ENTITY CommandAction "<classname>CommandAction</classname>">
+<!ENTITY FunctionAction "<classname>FunctionAction</classname>">
+<!ENTITY ListAction "<classname>ListAction</classname>">
+<!ENTITY Builder "<classname>Builder</classname>">
+<!ENTITY BuilderBase "<classname>BuilderBase</classname>">
+<!ENTITY CompositeBuilder "<classname>CompositeBuilder</classname>">
+<!ENTITY MultiStepBuilder "<classname>MultiStepBuilder</classname>">
+<!ENTITY Job "<classname>Job</classname>">
+<!ENTITY Jobs "<classname>Jobs</classname>">
+<!ENTITY Serial "<classname>Serial</classname>">
+<!ENTITY Parallel "<classname>Parallel</classname>">
+<!ENTITY Node "<classname>Node</classname>">
+<!ENTITY Node_FS "<classname>Node.FS</classname>">
+<!ENTITY Scanner "<classname>Scanner</classname>">
+<!ENTITY Sig "<classname>Sig</classname>">
+<!ENTITY Signature "<classname>Signature</classname>">
+<!ENTITY Taskmaster "<classname>Taskmaster</classname>">
+<!ENTITY TimeStamp "<classname>TimeStamp</classname>">
+<!ENTITY Walker "<classname>Walker</classname>">
+<!ENTITY Wrapper "<classname>Wrapper</classname>">
+
+
+
+<!--
+
+ Options, command-line.
+
+-->
+
+<!ENTITY config "<literal>--config</literal>">
+<!ENTITY debug-explain "<literal>--debug=explain</literal>">
+<!ENTITY debug-findlibs "<literal>--debug=findlibs</literal>">
+<!ENTITY debug-includes "<literal>--debug=includes</literal>">
+<!ENTITY debug-presub "<literal>--debug=presub</literal>">
+<!ENTITY debug-stacktrace "<literal>--debug=stacktrace</literal>">
+<!ENTITY implicit-cache "<literal>--implicit-cache</literal>">
+<!ENTITY implicit-deps-changed "<literal>--implicit-deps-changed</literal>">
+<!ENTITY implicit-deps-unchanged "<literal>--implicit-deps-unchanged</literal>">
+<!ENTITY profile "<literal>--profile</literal>">
+<!ENTITY taskmastertrace "<literal>--taskmastertrace</literal>">
+<!ENTITY tree "<literal>--tree</literal>">
+<!ENTITY Q "<literal>-Q</literal>">
+
+<!--
+
+ Options, SConscript-settable.
+
+-->
+
+<!ENTITY implicit_cache "<literal>implicit_cache</literal>">
+<!ENTITY implicit_deps_changed "<literal>implicit_deps_changed</literal>">
+<!ENTITY implicit_deps_unchanged "<literal>implicit_deps_unchanged</literal>">
+
+
+
+<!--
+
+ File and directory names.
+
+-->
+
+<!ENTITY build "<filename>build</filename>">
+<!ENTITY Makefile "<filename>Makefile</filename>">
+<!ENTITY Makefiles "<filename>Makefiles</filename>">
+<!ENTITY scons "<filename>scons</filename>">
+<!ENTITY SConscript "<filename>SConscript</filename>">
+<!ENTITY SConstruct "<filename>SConstruct</filename>">
+<!ENTITY Sconstruct "<filename>Sconstruct</filename>">
+<!ENTITY sconstruct "<filename>sconstruct</filename>">
+<!ENTITY sconsign "<filename>.sconsign</filename>">
+<!ENTITY src "<filename>src</filename>">
+
+
+
+<!--
+
+ Methods and functions. This includes functions from both
+ the Build Engine and the Native Python Interface.
+
+-->
+
+<!ENTITY Add "<function>Add</function>">
+<!ENTITY AddMethod "<function>AddMethod</function>">
+<!ENTITY AddPostAction "<function>AddPostAction</function>">
+<!ENTITY AddPreAction "<function>AddPreAction</function>">
+<!ENTITY AddOption "<function>AddOption</function>">
+<!ENTITY AddOptions "<function>AddOptions</function>">
+<!ENTITY AddVariables "<function>AddVariables</function>">
+<!ENTITY Alias "<function>Alias</function>">
+<!ENTITY Aliases "<function>Aliases</function>">
+<!ENTITY AlwaysBuild "<function>AlwaysBuild</function>">
+<!ENTITY Append "<function>Append</function>">
+<!ENTITY AppendENVPath "<function>AppendENVPath</function>">
+<!ENTITY AppendUnique "<function>AppendUnique</function>">
+<!ENTITY BoolOption "<function>BoolOption</function>">
+<!ENTITY BoolVariable "<function>BoolVariable</function>">
+<!ENTITY Build "<function>Build</function>">
+<!ENTITY CacheDir "<function>CacheDir</function>">
+<!ENTITY Chmod "<function>Chmod</function>">
+<!ENTITY Clean "<function>Clean</function>">
+<!ENTITY Clone "<function>Clone</function>">
+<!ENTITY Command "<function>Command</function>">
+<!ENTITY Configure "<function>Configure</function>">
+<!ENTITY Copy "<function>Copy</function>">
+<!ENTITY Decider "<function>Decider</function>">
+<!ENTITY Default "<function>Default</function>">
+<!ENTITY DefaultEnvironment "<function>DefaultEnvironment</function>">
+<!ENTITY DefaultRules "<function>DefaultRules</function>">
+<!ENTITY Delete "<function>Delete</function>">
+<!ENTITY Depends "<function>Depends</function>">
+<!ENTITY Dir "<function>Dir</function>">
+<!ENTITY Dump "<function>Dump</function>">
+<!ENTITY Entry "<function>Entry</function>">
+<!ENTITY EnumOption "<function>EnumOption</function>">
+<!ENTITY EnumVariable "<function>EnumVariable</function>">
+<!ENTITY EnsurePythonVersion "<function>EnsurePythonVersion</function>">
+<!ENTITY EnsureSConsVersion "<function>EnsureSConsVersion</function>">
+<!ENTITY Environment "<function>Environment</function>">
+<!ENTITY Execute "<function>Execute</function>">
+<!ENTITY Exit "<function>Exit</function>">
+<!ENTITY Export "<function>Export</function>">
+<!ENTITY File "<function>File</function>">
+<!ENTITY FindFile "<function>FindFile</function>">
+<!ENTITY FindInstalledFiles "<function>FindInstalledFiles</function>">
+<!ENTITY Finish "<function>Finish</function>">
+<!ENTITY Flatten "<function>Flatten</function>">
+<!ENTITY GenerateHelpText "<function>GenerateHelpText</function>">
+<!ENTITY GetBuildFailures "<function>GetBuildFailures</function>">
+<!ENTITY GetLaunchDir "<function>GetLaunchDir</function>">
+<!ENTITY GetOption "<function>GetOption</function>">
+<!ENTITY Glob "<function>Glob</function>">
+<!ENTITY Help "<function>Help</function>">
+<!ENTITY Ignore "<function>Ignore</function>">
+<!ENTITY Import "<function>Import</function>">
+<!ENTITY Install "<function>Install</function>">
+<!ENTITY InstallAs "<function>InstallAs</function>">
+<!ENTITY Link "<function>Link</function>">
+<!ENTITY ListOption "<function>ListOption</function>">
+<!ENTITY ListVariable "<function>ListVariable</function>">
+<!ENTITY Local "<function>Local</function>">
+<!ENTITY MergeFlags "<function>MergeFlags</function>">
+<!ENTITY Mkdir "<function>Mkdir</function>">
+<!ENTITY Module "<function>Module</function>">
+<!ENTITY Move "<function>Move</function>">
+<!ENTITY NoClean "<function>NoClean</function>">
+<!ENTITY NoCache "<function>NoCache</function>">
+<!ENTITY Objects "<function>Objects</function>">
+<!ENTITY Options "<function>Options</function>">
+<!ENTITY Variables "<function>Variables</function>">
+<!ENTITY PackageOption "<function>PackageOption</function>">
+<!ENTITY PackageVariable "<function>PackageVariable</function>">
+<!ENTITY ParseConfig "<function>ParseConfig</function>">
+<!ENTITY ParseDepends "<function>ParseDepends</function>">
+<!ENTITY ParseFlags "<function>ParseFlags</function>">
+<!ENTITY PathOption "<function>PathOption</function>">
+<!ENTITY PathOption_PathAccept "<function>PathOption.PathAccept</function>">
+<!ENTITY PathOption_PathExists "<function>PathOption.PathExists</function>">
+<!ENTITY PathOption_PathIsDir "<function>PathOption.PathIsDir</function>">
+<!ENTITY PathOption_PathIsDirCreate "<function>PathOption.PathIsDirCreate</function>">
+<!ENTITY PathOption_PathIsFile "<function>PathOption.PathIsFile</function>">
+<!ENTITY PathVariable "<function>PathVariable</function>">
+<!ENTITY PathVariable_PathAccept "<function>PathVariable.PathAccept</function>">
+<!ENTITY PathVariable_PathExists "<function>PathVariable.PathExists</function>">
+<!ENTITY PathVariable_PathIsDir "<function>PathVariable.PathIsDir</function>">
+<!ENTITY PathVariable_PathIsDirCreate "<function>PathVariable.PathIsDirCreate</function>">
+<!ENTITY PathVariable_PathIsFile "<function>PathVariable.PathIsFile</function>">
+<!ENTITY Precious "<function>Precious</function>">
+<!ENTITY Prepend "<function>Prepend</function>">
+<!ENTITY PrependENVPath "<function>PrependENVPath</function>">
+<!ENTITY PrependUnique "<function>PrependUnique</function>">
+<!ENTITY Progress "<function>Progress</function>">
+<!ENTITY Replace "<function>Replace</function>">
+<!ENTITY Repository "<function>Repository</function>">
+<!ENTITY Requires "<function>Requires</function>">
+<!ENTITY Return "<function>Return</function>">
+<!ENTITY RuleSet "<function>RuleSet</function>">
+<!ENTITY Salt "<function>Salt</function>">
+<!ENTITY SetBuildSignatureType "<function>SetBuildSignatureType</function>">
+<!ENTITY SetContentSignatureType "<function>SetContentSignatureType</function>">
+<!ENTITY SetDefault "<function>SetDefault</function>">
+<!ENTITY SetOption "<function>SetOption</function>">
+<!ENTITY SideEffect "<function>SideEffect</function>">
+<!ENTITY SourceSignature "<function>SourceSignature</function>">
+<!ENTITY SourceSignatures "<function>SourceSignatures</function>">
+<!ENTITY Split "<function>Split</function>">
+<!ENTITY Tag "<function>Tag</function>">
+<!ENTITY TargetSignatures "<function>TargetSignatures</function>">
+<!ENTITY Task "<function>Task</function>">
+<!ENTITY Touch "<function>Touch</function>">
+<!ENTITY UnknownOptions "<function>UnknownOptions</function>">
+<!ENTITY UnknownVariables "<function>UnknownVariables</function>">
+
+<!-- Environment methods -->
+<!ENTITY subst "<function>subst</function>">
+
+<!-- Configure context functions -->
+<!ENTITY Message "<function>Message</function>">
+<!ENTITY Result "<function>Result</function>">
+<!ENTITY CheckCHeader "<function>CheckCHeader</function>">
+<!ENTITY CheckCXXHeader "<function>CheckCXXHeader</function>">
+<!ENTITY CheckFunc "<function>CheckFunc</function>">
+<!ENTITY CheckHeader "<function>CheckHeader</function>">
+<!ENTITY CheckLib "<function>CheckLib</function>">
+<!ENTITY CheckLibWithHeader "<function>CheckLibWithHeader</function>">
+<!ENTITY CheckType "<function>CheckType</function>">
+<!ENTITY TryAction "<function>TryAction</function>">
+<!ENTITY TryBuild "<function>TryBuild</function>">
+<!ENTITY TryCompile "<function>TryCompile</function>">
+<!ENTITY TryLink "<function>TryLink</function>">
+<!ENTITY TryRun "<function>TryRun</function>">
+
+
+<!-- Python functions -->
+<!ENTITY str "<function>str</function>">
+<!ENTITY zipfile "<function>zipfile</function>">
+
+<!-- Obsolete, but referenced in old documents. -->
+<!ENTITY Cache "<function>Cache</function>">
+
+
+
+<!--
+
+ Global variables.
+
+-->
+
+<!ENTITY ARGLIST "<varname>ARGLIST</varname>">
+<!ENTITY ARGUMENTS "<varname>ARGUMENTS</varname>">
+<!ENTITY BUILD_TARGETS "<varname>BUILD_TARGETS</varname>">
+<!ENTITY COMMAND_LINE_TARGETS "<varname>COMMAND_LINE_TARGETS</varname>">
+<!ENTITY DEFAULT_TARGETS "<varname>DEFAULT_TARGETS</varname>">
+
+
+
+<!--
+
+ Construction variables.
+
+-->
+
+<!ENTITY BUILDERMAP "<varname>BUILDERMAP</varname>">
+<!ENTITY COLOR "<varname>COLOR</varname>">
+<!ENTITY COLORS "<varname>COLORS</varname>">
+<!ENTITY CONFIG "<varname>CONFIG</varname>">
+<!ENTITY CPPDEFINES "<varname>CPPDEFINES</varname>">
+<!ENTITY RELEASE "<varname>RELEASE</varname>">
+<!ENTITY RELEASE_BUILD "<varname>RELEASE_BUILD</varname>">
+<!ENTITY SCANNERMAP "<varname>SCANNERMAP</varname>">
+<!ENTITY TARFLAGS "<varname>TARFLAGS</varname>">
+<!ENTITY TARSUFFIX "<varname>TARSUFFIX</varname>">
+
+
+
+<!--
+
+ Environment variables.
+
+-->
+
+<!ENTITY PATH "<varname>PATH</varname>">
+<!ENTITY PYTHONPATH "<varname>PYTHONPATH</varname>">
+<!ENTITY SCONSFLAGS "<varname>SCONSFLAGS</varname>">
+
+
+
+<!--
+
+ Function and method arguments.
+
+-->
+
+<!ENTITY allowed_values "<varname>allowed_values</varname>">
+<!ENTITY build_dir "<varname>build_dir</varname>">
+<!ENTITY map "<varname>map</varname>">
+<!ENTITY ignorecase "<varname>ignorecase</varname>">
+<!ENTITY options "<varname>options</varname>">
+<!ENTITY exports "<varname>exports</varname>">
+<!ENTITY source "<varname>source</varname>">
+<!ENTITY target "<varname>target</varname>">
+<!ENTITY variables "<varname>variables</varname>">
+<!ENTITY variant_dir "<varname>variant_dir</varname>">
+
+
+
+<!--
+
+ Values of function and method arguments.
+
+-->
+
+<!ENTITY all "<literal>all</literal>">
+<!ENTITY none "<literal>none</literal>">
+
+
+
+<!--
+
+ Builder and Scanner objects.
+
+-->
+
+<!ENTITY BuildDir "<function>BuildDir</function>">
+<!ENTITY CFile "<function>CFile</function>">
+<!ENTITY CXXFile "<function>CXXFile</function>">
+<!ENTITY DVI "<function>DVI</function>">
+<!ENTITY Jar "<function>Jar</function>">
+<!ENTITY Java "<function>Java</function>">
+<!ENTITY JavaH "<function>JavaH</function>">
+<!ENTITY Library "<function>Library</function>">
+<!ENTITY Object "<function>Object</function>">
+<!ENTITY PCH "<function>PCH</function>">
+<!ENTITY PDF "<function>PDF</function>">
+<!ENTITY PostScript "<function>PostScript</function>">
+<!ENTITY Program "<function>Program</function>">
+<!ENTITY RES "<function>RES</function>">
+<!ENTITY RMIC "<function>RMIC</function>">
+<!ENTITY SharedLibrary "<function>SharedLibrary</function>">
+<!ENTITY SharedObject "<function>SharedObject</function>">
+<!ENTITY StaticLibrary "<function>StaticLibrary</function>">
+<!ENTITY StaticObject "<function>StaticObject</function>">
+<!ENTITY Substfile "<function>Substfile</function>">
+<!ENTITY Tar "<function>Tar</function>">
+<!ENTITY Textfile "<function>Textfile</function>">
+<!ENTITY VariantDir "<function>VariantDir</function>">
+<!ENTITY Zip "<function>Zip</function>">
+
+<!-- Obsolete, but referenced in old documents. -->
+<!ENTITY MakeBuilder "<function>Make</function>">
+
+
+
+<!--
+
+ Terms. Define both singular and plural forms in various
+ case-sensitive combinations for use in titles, in-line, etc.
+
+-->
+
+<!ENTITY buildfunc "<literal>builder function</literal>">
+<!ENTITY build_action "<literal>build action</literal>">
+<!ENTITY build_actions "<literal>build actions</literal>">
+<!ENTITY builder_method "<literal>builder method</literal>">
+
+<!ENTITY Configure_Contexts "<literal>Configure Contexts</literal>">
+<!ENTITY configure_context "<literal>configure context</literal>">
+
+<!ENTITY ConsEnv "<literal>Construction Environment</literal>">
+<!ENTITY ConsEnvs "<literal>Construction Environments</literal>">
+<!ENTITY Consenv "<literal>Construction environment</literal>">
+<!ENTITY Consenvs "<literal>Construction environments</literal>">
+<!ENTITY consenv "<literal>construction environment</literal>">
+<!ENTITY consenvs "<literal>construction environments</literal>">
+
+<!ENTITY ConsVar "<literal>Construction Variable</literal>">
+<!ENTITY ConsVars "<literal>Construction Variables</literal>">
+<!ENTITY Consvar "<literal>Construction variable</literal>">
+<!ENTITY Consvars "<literal>Construction variables</literal>">
+<!ENTITY consvar "<literal>construction variable</literal>">
+<!ENTITY consvars "<literal>construction variables</literal>">
+
+<!ENTITY CPPPATH "<literal>CPPPATH</literal>">
+
+<!ENTITY Dictionary "<literal>Dictionary</literal>">
+
+<!ENTITY Emitter "<literal>Emitter</literal>">
+<!ENTITY emitter "<literal>emitter</literal>">
+
+<!ENTITY factory "<literal>factory</literal>">
+
+<!ENTITY Generator "<literal>Generator</literal>">
+<!ENTITY generator "<literal>generator</literal>">
+
+<!ENTITY Nodes "<literal>Nodes</literal>">
+
+<!ENTITY signature "<literal>signature</literal>">
+<!ENTITY buildsignature "<literal>build signature</literal>">
+
+<!ENTITY true "<literal>true</literal>">
+<!ENTITY false "<literal>false</literal>">
+
+<!ENTITY typedef "<literal>typedef</literal>">
+
+<!--
+
+ Python keyword arguments
+
+-->
+
+<!ENTITY action "<literal>action=</literal>">
+<!ENTITY batch_key "<literal>batch_key=</literal>">
+<!ENTITY cmdstr "<literal>cmdstr=</literal>">
+<!ENTITY exitstatfunc "<literal>exitstatfunc=</literal>">
+<!ENTITY strfunction "<literal>strfunction=</literal>">
+<!ENTITY varlist "<literal>varlist=</literal>">
+
+<!--
+
+ File and program names used in examples.
+
+-->
+
+<!ENTITY bar "<filename>bar</filename>">
+<!ENTITY common1_c "<filename>common1.c</filename>">
+<!ENTITY common2_c "<filename>common2.c</filename>">
+<!ENTITY custom_py "<filename>custom.py</filename>">
+<!ENTITY goodbye "<filename>goodbye</filename>">
+<!ENTITY goodbye_o "<filename>goodbye.o</filename>">
+<!ENTITY goodbye_obj "<filename>goodbye.obj</filename>">
+<!ENTITY file_dll "<filename>file.dll</filename>">
+<!ENTITY file_in "<filename>file.in</filename>">
+<!ENTITY file_lib "<filename>file.lib</filename>">
+<!ENTITY file_o "<filename>file.o</filename>">
+<!ENTITY file_obj "<filename>file.obj</filename>">
+<!ENTITY file_out "<filename>file.out</filename>">
+<!ENTITY foo "<filename>foo</filename>">
+<!ENTITY foo_o "<filename>foo.o</filename>">
+<!ENTITY foo_obj "<filename>foo.obj</filename>">
+<!ENTITY hello "<filename>hello</filename>">
+<!ENTITY hello_c "<filename>hello.c</filename>">
+<!ENTITY hello_exe "<filename>hello.exe</filename>">
+<!ENTITY hello_h "<filename>hello.h</filename>">
+<!ENTITY hello_o "<filename>hello.o</filename>">
+<!ENTITY hello_obj "<filename>hello.obj</filename>">
+<!ENTITY libfile_a "<filename>libfile_a</filename>">
+<!ENTITY libfile_so "<filename>libfile_so</filename>">
+<!ENTITY new_hello "<filename>new_hello</filename>">
+<!ENTITY new_hello_exe "<filename>new_hello.exe</filename>">
+<!ENTITY prog "<filename>prog</filename>">
+<!ENTITY prog1 "<filename>prog1</filename>">
+<!ENTITY prog2 "<filename>prog2</filename>">
+<!ENTITY prog_c "<filename>prog.c</filename>">
+<!ENTITY prog_exe "<filename>prog.exe</filename>">
+<!ENTITY stdio_h "<filename>stdio.h</filename>">
+
+<!--
+
+ Punctuation.
+
+-->
+
+<!ENTITY plus "<literal>+</literal>">
+<!ENTITY hash "<literal>#</literal>">
+
+<!--
+
+ Mailing lists
+
+-->
+
+<!ENTITY scons-announce "<literal>announce@scons.tigris.org</literal>">
+<!ENTITY scons-devel "<literal>dev@scons.tigris.org</literal>">
+<!ENTITY scons-users "<literal>users@scons.tigris.org</literal>">
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.
+
+ <scons_example name="Foo">
+ <file name="SConstruct">
+ env = Environment()
+ print env.Dump("CC")
+ </file>
+ </scons_example>
+
+ <scons_output example="Foo">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
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
--- /dev/null
+++ b/doc/user/SCons-win32-install-1.jpg
Binary files 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
--- /dev/null
+++ b/doc/user/SCons-win32-install-2.jpg
Binary files 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
--- /dev/null
+++ b/doc/user/SCons-win32-install-3.jpg
Binary files 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
--- /dev/null
+++ b/doc/user/SCons-win32-install-4.jpg
Binary files 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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Build actions
+
+Cons supports several types of B<build actions> that can be performed
+to construct one or more target files. Usually, a build action is
+a construction command, that is, a command-line string that invokes
+an external command. Cons can also execute Perl code embedded in a
+command-line string, and even supports an experimental ability to build
+a target file by executing a Perl code reference directly.
+
+A build action is usually specified as the value of a construction
+variable:
+
+ $env = new cons(
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ LINKCOM => '[perl] &link_executable("%>", "%<")',
+ ARCOM => sub { my($env, $target, @sources) = @_;
+ # code to create an archive
+ }
+ );
+
+A build action may be associated directly with one or more target files
+via the C<Command> method; see below.
+
+=head2 Construction commands
+
+A construction command goes through expansion of construction variables
+and C<%-> pseudo-variables, as described above, to create the actual
+command line that Cons will execute to generate the target file or
+files.
+
+After substitution occurs, strings of white space are converted into
+single blanks, and leading and trailing white space is eliminated. It
+is therefore currently not possible to introduce variable length white
+space in strings passed into a command.
+
+If a multi-line command string is provided, the commands are executed
+sequentially. If any of the commands fails, then none of the rest are
+executed, and the target is not marked as updated, i.e. a new signature is
+not stored for the target.
+
+Normally, if all the commands succeed, and return a zero status (or whatever
+platform-specific indication of success is required), then a new signature
+is stored for the target. If a command erroneously reports success even
+after a failure, then Cons will assume that the target file created by that
+command is accurate and up-to-date.
+
+The first word of each command string, after expansion, is assumed to be an
+executable command looked up on the C<PATH> environment variable (which is,
+in turn, specified by the C<ENV> construction variable). If this command is
+found on the path, then the target will depend upon it: the command will
+therefore be automatically built, as necessary. It's possible to write
+multi-part commands to some shells, separated by semi-colons. Only the first
+command word will be depended upon, however, so if you write your command
+strings this way, you must either explicitly set up a dependency (with the
+C<Depends> method), or be sure that the command you are using is a system
+command which is expected to be available. If it isn't available, you will,
+of course, get an error.
+
+Cons normally prints a command before executing it. This behavior is
+suppressed if the first character of the command is C<@>. Note that
+you may need to separate the C<@> from the command name or escape it to
+prevent C<@cmd> from looking like an array to Perl quote operators that
+perform interpolation:
+
+ # The first command line is incorrect,
+ # because "@cp" looks like an array
+ # to the Perl qq// function.
+ # Use the second form instead.
+ Command $env 'foo', 'foo.in', qq(
+ @cp %< tempfile
+ @ cp tempfile %>
+ );
+
+If there are shell meta characters anywhere in the expanded command line,
+such as C<E<lt>>, C<E<gt>>, quotes, or semi-colon, then the command
+will actually be executed by invoking a shell. This means that a command
+such as:
+
+ cd foo
+
+alone will typically fail, since there is no command C<cd> on the path. But
+the command string:
+
+ cd $<:d; tar cf $>:f $<:f
+
+when expanded will still contain the shell meta character semi-colon, and a
+shell will be invoked to interpret the command. Since C<cd> is interpreted
+by this sub-shell, the command will execute as expected.
+
+=head2 Perl expressions
+
+If any command (even one within a multi-line command) begins with
+C<[perl]>, the remainder of that command line will be evaluated by the
+running Perl instead of being forked by the shell. If an error occurs
+in parsing the Perl code, or if the Perl expression returns 0 or undef,
+the command will be considered to have failed. For example, here is a
+simple command which creates a file C<foo> directly from Perl:
+
+ $env = new cons();
+ Command $env 'foo',
+ qq([perl] open(FOO,'>foo');print FOO "hi\\n"; close(FOO); 1);
+
+Note that when the command is executed, you are in the same package as
+when the F<Construct> or F<Conscript> file was read, so you can call
+Perl functions you've defined in the same F<Construct> or F<Conscript>
+file in which the C<Command> appears:
+
+ $env = new cons();
+ sub create_file {
+ my $file = shift;
+ open(FILE, ">$file");
+ print FILE "hi\n";
+ close(FILE);
+ return 1;
+ }
+ Command $env 'foo', "[perl] &create_file('%>')";
+
+The Perl string will be used to generate the signature for the derived
+file, so if you change the string, the file will be rebuilt. The contents
+of any subroutines you call, however, are not part of the signature,
+so if you modify a called subroutine such as C<create_file> above,
+the target will I<not> be rebuilt. Caveat user.
+
+=head2 Perl code references [EXPERIMENTAL]
+
+Cons supports the ability to create a derived file by directly executing
+a Perl code reference. This feature is considered EXPERIMENTAL and
+subject to change in the future.
+
+A code reference may either be a named subroutine referenced by the
+usual C<\&> syntax:
+
+ sub build_output {
+ my($env, $target, @sources) = @_;
+ print "build_output building $target\n";
+ open(OUT, ">$target");
+ foreach $src (@sources) {
+ if (! open(IN, "<$src")) {
+ print STDERR "cannot open '$src': $!\n";
+ return undef;
+ }
+ print OUT, <IN>;
+ }
+ close(OUT);
+ return 1;
+ }
+ Command $env 'output', \&build_output;
+
+or the code reference may be an anonymous subroutine:
+
+ Command $env 'output', sub {
+ my($env, $target, @sources) = @_;
+ print "building $target\n";
+ open(FILE, ">$target");
+ print FILE "hello\n";
+ close(FILE);
+ return 1;
+ };
+
+To build the target file, the referenced subroutine is passed, in order:
+the construction environment used to generate the target; the path
+name of the target itself; and the path names of all the source files
+necessary to build the target file.
+
+The code reference is expected to generate the target file, of course,
+but may manipulate the source and target files in any way it chooses.
+The code reference must return a false value (C<undef> or C<0>) if
+the build of the file failed. Any true value indicates a successful
+build of the target.
+
+Building target files using code references is considered EXPERIMENTAL
+due to the following current limitations:
+
+=over 4
+
+Cons does I<not> print anything to indicate the code reference is being
+called to build the file. The only way to give the user any indication
+is to have the code reference explicitly print some sort of "building"
+message, as in the above examples.
+
+Cons does not generate any signatures for code references, so if the
+code in the reference changes, the target will I<not> be rebuilt.
+
+Cons has no public method to allow a code reference to extract
+construction variables. This would be good to allow generalization of
+code references based on the current construction environment, but would
+also complicate the problem of generating meaningful signatures for code
+references.
+
+=back
+
+Support for building targets via code references has been released in
+this version to encourage experimentation and the seeking of possible
+solutions to the above limitations.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ b = Builder(action = 'build < $SOURCE > $TARGET')
+ </sconstruct>
+
+ <para>
+
+ Is equivalent to:
+
+ </para>
+
+ <sconstruct>
+ b = Builder(action = Action('build < $SOURCE > $TARGET'))
+ </sconstruct>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Command Strings as Actions</title>
+
+ <section>
+ <title>Suppressing Command-Line Printing</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Ignoring Exit Status</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Argument Lists as Actions</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Python Functions as Actions</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Modifying How an Action is Printed</title>
+
+ <section>
+ <title>XXX: the &strfunction; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>XXX: the &cmdstr; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Making an Action Depend on Variable Contents: the &varlist; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>chdir=1</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Batch Building of Multiple Targets from Separate Sources: the &batch_key; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Manipulating the Exit Status of an Action: the &exitstatfunc; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <!--
+
+ ???
+
+ <section>
+ <title>presub=</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Build actions
+
+Cons supports several types of B<build actions> that can be performed
+to construct one or more target files. Usually, a build action is
+a construction command, that is, a command-line string that invokes
+an external command. Cons can also execute Perl code embedded in a
+command-line string, and even supports an experimental ability to build
+a target file by executing a Perl code reference directly.
+
+A build action is usually specified as the value of a construction
+variable:
+
+ $env = new cons(
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ LINKCOM => '[perl] &link_executable("%>", "%<")',
+ ARCOM => sub { my($env, $target, @sources) = @_;
+ # code to create an archive
+ }
+ );
+
+A build action may be associated directly with one or more target files
+via the C<Command> method; see below.
+
+=head2 Construction commands
+
+A construction command goes through expansion of construction variables
+and C<%-> pseudo-variables, as described above, to create the actual
+command line that Cons will execute to generate the target file or
+files.
+
+After substitution occurs, strings of white space are converted into
+single blanks, and leading and trailing white space is eliminated. It
+is therefore currently not possible to introduce variable length white
+space in strings passed into a command.
+
+If a multi-line command string is provided, the commands are executed
+sequentially. If any of the commands fails, then none of the rest are
+executed, and the target is not marked as updated, i.e. a new signature is
+not stored for the target.
+
+Normally, if all the commands succeed, and return a zero status (or whatever
+platform-specific indication of success is required), then a new signature
+is stored for the target. If a command erroneously reports success even
+after a failure, then Cons will assume that the target file created by that
+command is accurate and up-to-date.
+
+The first word of each command string, after expansion, is assumed to be an
+executable command looked up on the C<PATH> environment variable (which is,
+in turn, specified by the C<ENV> construction variable). If this command is
+found on the path, then the target will depend upon it: the command will
+therefore be automatically built, as necessary. It's possible to write
+multi-part commands to some shells, separated by semi-colons. Only the first
+command word will be depended upon, however, so if you write your command
+strings this way, you must either explicitly set up a dependency (with the
+C<Depends> method), or be sure that the command you are using is a system
+command which is expected to be available. If it isn't available, you will,
+of course, get an error.
+
+Cons normally prints a command before executing it. This behavior is
+suppressed if the first character of the command is C<@>. Note that
+you may need to separate the C<@> from the command name or escape it to
+prevent C<@cmd> from looking like an array to Perl quote operators that
+perform interpolation:
+
+ # The first command line is incorrect,
+ # because "@cp" looks like an array
+ # to the Perl qq// function.
+ # Use the second form instead.
+ Command $env 'foo', 'foo.in', qq(
+ @cp %< tempfile
+ @ cp tempfile %>
+ );
+
+If there are shell meta characters anywhere in the expanded command line,
+such as C<E<lt>>, C<E<gt>>, quotes, or semi-colon, then the command
+will actually be executed by invoking a shell. This means that a command
+such as:
+
+ cd foo
+
+alone will typically fail, since there is no command C<cd> on the path. But
+the command string:
+
+ cd $<:d; tar cf $>:f $<:f
+
+when expanded will still contain the shell meta character semi-colon, and a
+shell will be invoked to interpret the command. Since C<cd> is interpreted
+by this sub-shell, the command will execute as expected.
+
+=head2 Perl expressions
+
+If any command (even one within a multi-line command) begins with
+C<[perl]>, the remainder of that command line will be evaluated by the
+running Perl instead of being forked by the shell. If an error occurs
+in parsing the Perl code, or if the Perl expression returns 0 or undef,
+the command will be considered to have failed. For example, here is a
+simple command which creates a file C<foo> directly from Perl:
+
+ $env = new cons();
+ Command $env 'foo',
+ qq([perl] open(FOO,'>foo');print FOO "hi\\n"; close(FOO); 1);
+
+Note that when the command is executed, you are in the same package as
+when the F<Construct> or F<Conscript> file was read, so you can call
+Perl functions you've defined in the same F<Construct> or F<Conscript>
+file in which the C<Command> appears:
+
+ $env = new cons();
+ sub create_file {
+ my $file = shift;
+ open(FILE, ">$file");
+ print FILE "hi\n";
+ close(FILE);
+ return 1;
+ }
+ Command $env 'foo', "[perl] &create_file('%>')";
+
+The Perl string will be used to generate the signature for the derived
+file, so if you change the string, the file will be rebuilt. The contents
+of any subroutines you call, however, are not part of the signature,
+so if you modify a called subroutine such as C<create_file> above,
+the target will I<not> be rebuilt. Caveat user.
+
+=head2 Perl code references [EXPERIMENTAL]
+
+Cons supports the ability to create a derived file by directly executing
+a Perl code reference. This feature is considered EXPERIMENTAL and
+subject to change in the future.
+
+A code reference may either be a named subroutine referenced by the
+usual C<\&> syntax:
+
+ sub build_output {
+ my($env, $target, @sources) = @_;
+ print "build_output building $target\n";
+ open(OUT, ">$target");
+ foreach $src (@sources) {
+ if (! open(IN, "<$src")) {
+ print STDERR "cannot open '$src': $!\n";
+ return undef;
+ }
+ print OUT, <IN>;
+ }
+ close(OUT);
+ return 1;
+ }
+ Command $env 'output', \&build_output;
+
+or the code reference may be an anonymous subroutine:
+
+ Command $env 'output', sub {
+ my($env, $target, @sources) = @_;
+ print "building $target\n";
+ open(FILE, ">$target");
+ print FILE "hello\n";
+ close(FILE);
+ return 1;
+ };
+
+To build the target file, the referenced subroutine is passed, in order:
+the construction environment used to generate the target; the path
+name of the target itself; and the path names of all the source files
+necessary to build the target file.
+
+The code reference is expected to generate the target file, of course,
+but may manipulate the source and target files in any way it chooses.
+The code reference must return a false value (C<undef> or C<0>) if
+the build of the file failed. Any true value indicates a successful
+build of the target.
+
+Building target files using code references is considered EXPERIMENTAL
+due to the following current limitations:
+
+=over 4
+
+Cons does I<not> print anything to indicate the code reference is being
+called to build the file. The only way to give the user any indication
+is to have the code reference explicitly print some sort of "building"
+message, as in the above examples.
+
+Cons does not generate any signatures for code references, so if the
+code in the reference changes, the target will I<not> be rebuilt.
+
+Cons has no public method to allow a code reference to extract
+construction variables. This would be good to allow generalization of
+code references based on the current construction environment, but would
+also complicate the problem of generating meaningful signatures for code
+references.
+
+=back
+
+Support for building targets via code references has been released in
+this version to encourage experimentation and the seeking of possible
+solutions to the above limitations.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ b = Builder(action = 'build < $SOURCE > $TARGET')
+ </programlisting>
+
+ <para>
+
+ Is equivalent to:
+
+ </para>
+
+ <programlisting>
+ b = Builder(action = Action('build < $SOURCE > $TARGET'))
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Command Strings as Actions</title>
+
+ <section>
+ <title>Suppressing Command-Line Printing</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Ignoring Exit Status</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Argument Lists as Actions</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Python Functions as Actions</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Modifying How an Action is Printed</title>
+
+ <section>
+ <title>XXX: the &strfunction; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>XXX: the &cmdstr; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Making an Action Depend on Variable Contents: the &varlist; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>chdir=1</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Batch Building of Multiple Targets from Separate Sources: the &batch_key; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Manipulating the Exit Status of an Action: the &exitstatfunc; keyword argument</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <!--
+
+ ???
+
+ <section>
+ <title>presub=</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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
+ <filename>/usr/bin</filename> directory hierarchy,
+ but also copy it into a local <filename>install/bin</filename>
+ directory from which a package might be built:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ 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
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+ This produces the following:
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q /</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="test_stuff.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="res.rc">
+ res.rc
+ </file>
+ </scons_example>
+
+ <para>
+ This produces the following on Linux:
+ </para>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+ And the following on Windows:
+ </para>
+
+ <scons_output example="ex2" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+ 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.
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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
+ <filename>/usr/bin</filename> directory hierarchy,
+ but also copy it into a local <filename>install/bin</filename>
+ directory from which a package might be built:
+
+ </para>
+
+ <programlisting>
+ 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
+ </programlisting>
+
+ <para>
+ This produces the following:
+ </para>
+
+ <screen>
+ % <userinput>scons -Q /</userinput>
+ 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"
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+ This produces the following on Linux:
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o test_stuff.o -c test_stuff.c
+ cc -o tests/test_stuff test_stuff.o
+ </screen>
+
+ <para>
+ And the following on Windows:
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+ 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.
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ We've already seen how you can use the &Alias;
+ function to create a target named <literal>install</literal>:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Install('__ROOT__/usr/bin', hello)
+ env.Alias('install', '__ROOT__/usr/bin')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ You can then use this alias on the command line
+ to tell &SCons; more naturally that you want to install files:
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q install</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ 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])
+ </file>
+ <file name="foo.c">
+ int main() { printf("foo.c\n"); }
+ </file>
+ <file name="bar.c">
+ void bar() { printf("bar.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This example defines separate <literal>install</literal>,
+ <literal>install-bin</literal>,
+ and <literal>install-lib</literal> aliases,
+ allowing you finer control over what gets installed:
+
+ </para>
+
+ <scons_output example="ex2" os="posix">
+ <scons_output_command>scons -Q install-bin</scons_output_command>
+ <scons_output_command>scons -Q install-lib</scons_output_command>
+ <scons_output_command>scons -Q -c __ROOT__/</scons_output_command>
+ <scons_output_command>scons -Q install</scons_output_command>
+ </scons_output>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ We've already seen how you can use the &Alias;
+ function to create a target named <literal>install</literal>:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Install('/usr/bin', hello)
+ env.Alias('install', '/usr/bin')
+ </programlisting>
+
+ <para>
+
+ You can then use this alias on the command line
+ to tell &SCons; more naturally that you want to install files:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q install</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ Install file: "hello" as "/usr/bin/hello"
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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])
+ </programlisting>
+
+ <para>
+
+ This example defines separate <literal>install</literal>,
+ <literal>install-bin</literal>,
+ and <literal>install-lib</literal> aliases,
+ allowing you finer control over what gets installed:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q install-bin</userinput>
+ cc -o foo.o -c foo.c
+ cc -o foo foo.o
+ Install file: "foo" as "/usr/bin/foo"
+ % <userinput>scons -Q install-lib</userinput>
+ 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"
+ % <userinput>scons -Q -c /</userinput>
+ Removed foo.o
+ Removed foo
+ Removed /usr/bin/foo
+ Removed bar.o
+ Removed libbar.a
+ Removed /usr/lib/libbar.a
+ % <userinput>scons -Q install</userinput>
+ 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"
+ </screen>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Differences Between &Ant; and &SCons;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Advantages of &SCons; Over &Ant;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Differences Between &Ant; and &SCons;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Advantages of &SCons; Over &Ant;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <!--
+
+ <para>
+
+ Lastly, this chapter also contains a section that
+ provides a brief overview of the Python programming language,
+ which is the language used to implement &SCons;,
+ and which forms the basis of the &SCons; configuration files.
+ Becoming familiar with some Python concepts will make it easier
+ to understand many of the examples in this User's Guide.
+ Nevertheless, it <emphasis>is</emphasis> possible
+ to configure simple &SCons; builds without knowing Python,
+ so you can skip this section if you
+ want to dive in and pick up things
+ by example- -or, of course, if you are
+ already familiar with Python.
+
+ </para>
+
+ -->
+
+ <section>
+ <title>Installing Python</title>
+
+ <para>
+
+ 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
+ <userinput>python -V</userinput>
+ (capital 'V')
+ or
+ <userinput>python --version</userinput>
+ at your system's command-line prompt.
+
+ </para>
+
+ <screen>
+ $ <userinput>python -V</userinput>
+ Python 2.5.1
+ </screen>
+
+ <para>
+
+ And on a Windows system with Python installed:
+
+ </para>
+
+ <screen>
+ C:\><userinput>python -V</userinput>
+ Python 2.5.1
+ </screen>
+
+ <para>
+
+ 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;.
+
+ </para>
+
+ <para>
+
+ (Note that the <option>-V</option> option
+ was added to Python version 2.0,
+ so if your system only has an earlier version available
+ you may see an
+ <literal>"Unknown option: -V"</literal>
+ error message.)
+
+ </para>
+
+ <para>
+
+ The standard location for information
+ about downloading and installing Python is
+ <ulink url="http://www.python.org/download/">http://www.python.org/download/</ulink>.
+ See that page for information about
+ how to download and install Python on your system.
+
+ </para>
+
+ <para>
+
+ &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;.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; From Pre-Built Packages</title>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <section>
+ <title>Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems</title>
+
+ <para>
+
+ &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
+ <ulink url="http://www.rpmfind.net/">http://www.rpmfind.net/</ulink> or
+ <ulink url="http://rpm.pbone.net/">http://rpm.pbone.net/</ulink>.
+
+ </para>
+
+ <para>
+
+ If your distribution supports installation via
+ <application>yum</application>,
+ you should be able to install &SCons; by running:
+
+ </para>
+
+ <screen>
+ # <userinput>yum install scons</userinput>
+ </screen>
+
+ <para>
+
+ 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 <filename>/usr/bin</filename>,
+ and the SCons library modules in
+ <filename>/usr/lib/scons</filename>.
+
+ </para>
+
+ <para>
+
+ To install from the command line, simply download the
+ appropriate <filename>.rpm</filename> file,
+ and then run:
+
+ </para>
+
+ <screen>
+ # <userinput>rpm -Uvh scons-1.2.0.d20091224-1.noarch.rpm</userinput>
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; on Debian Linux Systems</title>
+
+ <para>
+
+ Debian Linux systems use a different package management
+ format that also makes it very easy to install &SCons;.
+
+ </para>
+
+ <para>
+
+ If your system is connected to the Internet,
+ you can install the latest official Debian package
+ by running:
+
+ </para>
+
+ <screen>
+ # <userinput>apt-get install scons</userinput>
+ </screen>
+
+ <!--
+
+ <para>
+
+ Alternatively,
+ you can download the Debian package built
+ by the &SCons; project
+ and install it manually by running:
+
+ </para>
+
+ <screen>
+ # <userinput>db-XXX scons-*.deb</userinput>
+ </screen>
+
+ -->
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; on Windows Systems</title>
+
+ <para>
+
+ &SCons; provides a Windows installer
+ that makes installation extremely easy.
+ Download the <filename>scons-1.2.0.d20091224.win32.exe</filename>
+ file from the &SCons; download page at
+ <ulink url="http://www.scons.org/download.php">http://www.scons.org/download.php</ulink>.
+ 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.
+
+ <!--
+ Things are a little more complicated
+ if you are using the Cygwin version of Python.
+ This is because Cygwin
+ tries to make a Windows system look more
+ POSIX-like (or UNIX-like or Linux-like, if you prefer)
+ by having the Cygwin utilities,
+ including Cygwin Python,
+ interpret file name arguments on the command line
+ using the forward-slash (<filename>/</filename>)
+ as the directory separator,
+ instead of the normal Windows behavior of the
+ backslash (<filename>\</filename>) as the directory separator.
+ -->
+
+ </para>
+
+ <!--
+
+ <section>
+ <title>Installing &SCons; on Windows Systems Without Cygwin Python</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; on Windows Systems With Cygwin Python</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ XXX - don't have the kinks worked out on how to
+ get these to display properly in all formats,
+ so comment them out for now.
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-1.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-2.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-3.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-4.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ -->
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Building and Installing &SCons; on Any System</title>
+
+ <para>
+
+ 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 <filename>distutils</filename> package.
+
+ </para>
+
+ <para>
+
+ The first step is to download either the
+ <filename>scons-1.2.0.d20091224.tar.gz</filename>
+ or <filename>scons-1.2.0.d20091224.zip</filename>,
+ which are available from the SCons download page at
+ <ulink url="http://www.scons.org/download.html">http://www.scons.org/download.html</ulink>.
+
+ </para>
+
+ <para>
+
+ Unpack the archive you downloaded,
+ using a utility like <application>tar</application>
+ on Linux or UNIX,
+ or <application>WinZip</application> on Windows.
+ This will create a directory called
+ <filename>scons-1.2.0.d20091224</filename>,
+ usually in your local directory.
+ Then change your working directory to that directory
+ and install &SCons; by executing the following commands:
+
+ </para>
+
+ <screen>
+ # <userinput>cd scons-1.2.0.d20091224</userinput>
+ # <userinput>python setup.py install</userinput>
+ </screen>
+
+ <para>
+
+ This will build &SCons;,
+ install the &scons; script
+ in the default system scripts directory
+ (<filename>/usr/local/bin</filename> or
+ <filename>C:\Python25\Scripts</filename>),
+ and will install the &SCons; build engine
+ in an appropriate stand-alone library directory
+ (<filename>/usr/local/lib/scons</filename> or
+ <filename>C:\Python25\scons</filename>).
+ Because these are system directories,
+ you may need root (on Linux or UNIX) or Administrator (on Windows)
+ privileges to install &SCons; like this.
+
+ </para>
+
+ <!--
+
+ <section>
+ <title>Building and Installing &SCons; in the Standard Python Library Directories</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Building and Installing Multiple Versions of &SCons; Side-by-Side</title>
+
+ <para>
+
+ The &SCons; <filename>setup.py</filename> 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.
+
+ </para>
+
+ <para>
+
+ To install &SCons; in a version-specific location,
+ add the <option>--version-lib</option> option
+ when you call <filename>setup.py</filename>:
+
+ </para>
+
+ <screen>
+ # <userinput>python setup.py install --version-lib</userinput>
+ </screen>
+
+ <para>
+
+ This will install the &SCons; build engine
+ in the
+ <filename>/usr/lib/scons-1.2.0.d20091224</filename>
+ or
+ <filename>C:\Python25\scons-1.2.0.d20091224</filename>
+ directory, for example.
+
+ </para>
+
+ <para>
+
+ If you use the <option>--version-lib</option> option
+ the first time you install &SCons;,
+ you do not need to specify it each time you install
+ a new version.
+ The &SCons; <filename>setup.py</filename> 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>--standalone-lib</option> option.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; in Other Locations</title>
+
+ <para>
+
+ You can install &SCons; in locations other than
+ the default by specifying the <option>--prefix=</option> option:
+
+ </para>
+
+ <screen>
+ # <userinput>python setup.py install --prefix=/opt/scons</userinput>
+ </screen>
+
+ <para>
+
+ This would
+ install the <application>scons</application> script in
+ <filename>/opt/scons/bin</filename>
+ and the build engine in
+ <filename>/opt/scons/lib/scons</filename>,
+
+ </para>
+
+ <para>
+
+ Note that you can specify both the <option>--prefix=</option>
+ and the <option>--version-lib</option> options
+ at the same type,
+ in which case <filename>setup.py</filename>
+ will install the build engine
+ in a version-specific directory
+ relative to the specified prefix.
+ Adding <option>--version-lib</option> to the
+ above example would install the build engine in
+ <filename>/opt/scons/lib/scons-1.2.0.d20091224</filename>.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building and Installing &SCons; Without Administrative Privileges</title>
+
+ <para>
+
+ If you don't have the right privileges to install &SCons;
+ in a system location,
+ simply use the <literal>--prefix=</literal> option
+ to install it in a location of your choosing.
+ For example,
+ to install &SCons; in appropriate locations
+ relative to the user's <literal>$HOME</literal> directory,
+ the &scons; script in
+ <filename>$HOME/bin</filename>
+ and the build engine in
+ <filename>$HOME/lib/scons</filename>,
+ simply type:
+
+ </para>
+
+ <screen>
+ $ <userinput>python setup.py install --prefix=$HOME</userinput>
+ </screen>
+
+ <para>
+
+ You may, of course, specify any other location you prefer,
+ and may use the <option>--version-lib</option> option
+ if you would like to install version-specific directories
+ relative to the specified prefix.
+
+ </para>
+
+ <para>
+
+ 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
+ (<filename>$HOME/bin</filename> in the above example)
+ must be configured in your &PATH; variable
+ before the directory containing
+ the system-installed version
+ of the &scons; script.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Python Basics</title>
+
+ <para>
+
+ This section will provide a brief overview of
+ the Python programming language.
+ Skip this section if you are already familiar with Python
+ (or you're really intent on diving into &SCons;
+ and just picking up things as you go).
+
+ </para>
+
+ <para>
+
+ Python has a lot of good
+ documentation freely available on-line
+ to help you get started.
+ The standard tutorial is available at XXX.
+
+
+ </para>
+
+ <para>
+
+ Python is very easy to pick up.
+
+ </para>
+
+ <para>
+
+ Python variables must be assigned to before they can be referenced.
+
+ </para>
+
+ <para>
+
+ Assignment is like most programming languages:
+
+ x = 1 + 2
+ z = 3 * x
+
+ </para>
+
+ <para>
+
+ Function calls look like most language function calls:
+
+ a = f(g)
+
+ </para>
+
+ <para>
+
+ Define functions like so:
+
+ def func(arg1, arg2):
+ return arg1 * arg 2
+
+ The number of parameters
+
+ </para>
+
+ <para>
+
+ Strings can be enclosed in single quotes or double quotes,
+ backslashes are used to escape characters,
+ triple-quote syntax lets you include quotes and newlines,
+ raw strings begin with 'r'.
+
+ </para>
+
+ <para>
+
+ Lists are enclosed in square brackets,
+ list items are separated by commas.
+ List references use square brackets and integer index values,
+ slice notation lets you select, delete or replace a range.
+
+ </para>
+
+ <para>
+
+ Dictionaries (hashes) are enclosed in curly brackets,
+ : separates keys from values,
+ , separates items.
+ Dictionary values are referenced using square brackets.
+
+ </para>
+
+ <para>
+
+ Access class attributes (including methods) using a '.'.
+
+ </para>
+
+ <para>
+
+ if: statements look like
+
+ elif: statements look like
+
+ else: statements look like
+
+ </para>
+
+ <para>
+
+ for: statements look like
+
+ while: statements look like
+
+ break statements look like
+
+ continue statements look like
+
+ </para>
+
+ <para>
+
+ pass
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <!--
+
+ <para>
+
+ Lastly, this chapter also contains a section that
+ provides a brief overview of the Python programming language,
+ which is the language used to implement &SCons;,
+ and which forms the basis of the &SCons; configuration files.
+ Becoming familiar with some Python concepts will make it easier
+ to understand many of the examples in this User's Guide.
+ Nevertheless, it <emphasis>is</emphasis> possible
+ to configure simple &SCons; builds without knowing Python,
+ so you can skip this section if you
+ want to dive in and pick up things
+ by example- -or, of course, if you are
+ already familiar with Python.
+
+ </para>
+
+ -->
+
+ <section>
+ <title>Installing Python</title>
+
+ <para>
+
+ 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
+ <userinput>python -V</userinput>
+ (capital 'V')
+ or
+ <userinput>python --version</userinput>
+ at your system's command-line prompt.
+
+ </para>
+
+ <screen>
+ $ <userinput>python -V</userinput>
+ Python 2.5.1
+ </screen>
+
+ <para>
+
+ And on a Windows system with Python installed:
+
+ </para>
+
+ <screen>
+ C:\><userinput>python -V</userinput>
+ Python 2.5.1
+ </screen>
+
+ <para>
+
+ 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;.
+
+ </para>
+
+ <para>
+
+ (Note that the <option>-V</option> option
+ was added to Python version 2.0,
+ so if your system only has an earlier version available
+ you may see an
+ <literal>"Unknown option: -V"</literal>
+ error message.)
+
+ </para>
+
+ <para>
+
+ The standard location for information
+ about downloading and installing Python is
+ <ulink url="http://www.python.org/download/">http://www.python.org/download/</ulink>.
+ See that page for information about
+ how to download and install Python on your system.
+
+ </para>
+
+ <para>
+
+ &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;.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; From Pre-Built Packages</title>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <section>
+ <title>Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems</title>
+
+ <para>
+
+ &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
+ <ulink url="http://www.rpmfind.net/">http://www.rpmfind.net/</ulink> or
+ <ulink url="http://rpm.pbone.net/">http://rpm.pbone.net/</ulink>.
+
+ </para>
+
+ <para>
+
+ If your distribution supports installation via
+ <application>yum</application>,
+ you should be able to install &SCons; by running:
+
+ </para>
+
+ <screen>
+ # <userinput>yum install scons</userinput>
+ </screen>
+
+ <para>
+
+ 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 <filename>/usr/bin</filename>,
+ and the SCons library modules in
+ <filename>/usr/lib/scons</filename>.
+
+ </para>
+
+ <para>
+
+ To install from the command line, simply download the
+ appropriate <filename>.rpm</filename> file,
+ and then run:
+
+ </para>
+
+ <screen>
+ # <userinput>rpm -Uvh scons-1.2.0.d20091224-1.noarch.rpm</userinput>
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; on Debian Linux Systems</title>
+
+ <para>
+
+ Debian Linux systems use a different package management
+ format that also makes it very easy to install &SCons;.
+
+ </para>
+
+ <para>
+
+ If your system is connected to the Internet,
+ you can install the latest official Debian package
+ by running:
+
+ </para>
+
+ <screen>
+ # <userinput>apt-get install scons</userinput>
+ </screen>
+
+ <!--
+
+ <para>
+
+ Alternatively,
+ you can download the Debian package built
+ by the &SCons; project
+ and install it manually by running:
+
+ </para>
+
+ <screen>
+ # <userinput>db-XXX scons-*.deb</userinput>
+ </screen>
+
+ -->
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; on Windows Systems</title>
+
+ <para>
+
+ &SCons; provides a Windows installer
+ that makes installation extremely easy.
+ Download the <filename>scons-1.2.0.d20091224.win32.exe</filename>
+ file from the &SCons; download page at
+ <ulink url="http://www.scons.org/download.php">http://www.scons.org/download.php</ulink>.
+ 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.
+
+ <!--
+ Things are a little more complicated
+ if you are using the Cygwin version of Python.
+ This is because Cygwin
+ tries to make a Windows system look more
+ POSIX-like (or UNIX-like or Linux-like, if you prefer)
+ by having the Cygwin utilities,
+ including Cygwin Python,
+ interpret file name arguments on the command line
+ using the forward-slash (<filename>/</filename>)
+ as the directory separator,
+ instead of the normal Windows behavior of the
+ backslash (<filename>\</filename>) as the directory separator.
+ -->
+
+ </para>
+
+ <!--
+
+ <section>
+ <title>Installing &SCons; on Windows Systems Without Cygwin Python</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; on Windows Systems With Cygwin Python</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ XXX - don't have the kinks worked out on how to
+ get these to display properly in all formats,
+ so comment them out for now.
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-1.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-2.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-3.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="SCons-win32-install-4.jpg" format="jpg" align="center">
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+
+ -->
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Building and Installing &SCons; on Any System</title>
+
+ <para>
+
+ 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 <filename>distutils</filename> package.
+
+ </para>
+
+ <para>
+
+ The first step is to download either the
+ <filename>scons-1.2.0.d20091224.tar.gz</filename>
+ or <filename>scons-1.2.0.d20091224.zip</filename>,
+ which are available from the SCons download page at
+ <ulink url="http://www.scons.org/download.html">http://www.scons.org/download.html</ulink>.
+
+ </para>
+
+ <para>
+
+ Unpack the archive you downloaded,
+ using a utility like <application>tar</application>
+ on Linux or UNIX,
+ or <application>WinZip</application> on Windows.
+ This will create a directory called
+ <filename>scons-1.2.0.d20091224</filename>,
+ usually in your local directory.
+ Then change your working directory to that directory
+ and install &SCons; by executing the following commands:
+
+ </para>
+
+ <screen>
+ # <userinput>cd scons-1.2.0.d20091224</userinput>
+ # <userinput>python setup.py install</userinput>
+ </screen>
+
+ <para>
+
+ This will build &SCons;,
+ install the &scons; script
+ in the default system scripts directory
+ (<filename>/usr/local/bin</filename> or
+ <filename>C:\Python25\Scripts</filename>),
+ and will install the &SCons; build engine
+ in an appropriate stand-alone library directory
+ (<filename>/usr/local/lib/scons</filename> or
+ <filename>C:\Python25\scons</filename>).
+ Because these are system directories,
+ you may need root (on Linux or UNIX) or Administrator (on Windows)
+ privileges to install &SCons; like this.
+
+ </para>
+
+ <!--
+
+ <section>
+ <title>Building and Installing &SCons; in the Standard Python Library Directories</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Building and Installing Multiple Versions of &SCons; Side-by-Side</title>
+
+ <para>
+
+ The &SCons; <filename>setup.py</filename> 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.
+
+ </para>
+
+ <para>
+
+ To install &SCons; in a version-specific location,
+ add the <option>--version-lib</option> option
+ when you call <filename>setup.py</filename>:
+
+ </para>
+
+ <screen>
+ # <userinput>python setup.py install --version-lib</userinput>
+ </screen>
+
+ <para>
+
+ This will install the &SCons; build engine
+ in the
+ <filename>/usr/lib/scons-1.2.0.d20091224</filename>
+ or
+ <filename>C:\Python25\scons-1.2.0.d20091224</filename>
+ directory, for example.
+
+ </para>
+
+ <para>
+
+ If you use the <option>--version-lib</option> option
+ the first time you install &SCons;,
+ you do not need to specify it each time you install
+ a new version.
+ The &SCons; <filename>setup.py</filename> 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>--standalone-lib</option> option.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Installing &SCons; in Other Locations</title>
+
+ <para>
+
+ You can install &SCons; in locations other than
+ the default by specifying the <option>--prefix=</option> option:
+
+ </para>
+
+ <screen>
+ # <userinput>python setup.py install --prefix=/opt/scons</userinput>
+ </screen>
+
+ <para>
+
+ This would
+ install the <application>scons</application> script in
+ <filename>/opt/scons/bin</filename>
+ and the build engine in
+ <filename>/opt/scons/lib/scons</filename>,
+
+ </para>
+
+ <para>
+
+ Note that you can specify both the <option>--prefix=</option>
+ and the <option>--version-lib</option> options
+ at the same type,
+ in which case <filename>setup.py</filename>
+ will install the build engine
+ in a version-specific directory
+ relative to the specified prefix.
+ Adding <option>--version-lib</option> to the
+ above example would install the build engine in
+ <filename>/opt/scons/lib/scons-1.2.0.d20091224</filename>.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building and Installing &SCons; Without Administrative Privileges</title>
+
+ <para>
+
+ If you don't have the right privileges to install &SCons;
+ in a system location,
+ simply use the <literal>--prefix=</literal> option
+ to install it in a location of your choosing.
+ For example,
+ to install &SCons; in appropriate locations
+ relative to the user's <literal>$HOME</literal> directory,
+ the &scons; script in
+ <filename>$HOME/bin</filename>
+ and the build engine in
+ <filename>$HOME/lib/scons</filename>,
+ simply type:
+
+ </para>
+
+ <screen>
+ $ <userinput>python setup.py install --prefix=$HOME</userinput>
+ </screen>
+
+ <para>
+
+ You may, of course, specify any other location you prefer,
+ and may use the <option>--version-lib</option> option
+ if you would like to install version-specific directories
+ relative to the specified prefix.
+
+ </para>
+
+ <para>
+
+ 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
+ (<filename>$HOME/bin</filename> in the above example)
+ must be configured in your &PATH; variable
+ before the directory containing
+ the system-installed version
+ of the &scons; script.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Python Basics</title>
+
+ <para>
+
+ This section will provide a brief overview of
+ the Python programming language.
+ Skip this section if you are already familiar with Python
+ (or you're really intent on diving into &SCons;
+ and just picking up things as you go).
+
+ </para>
+
+ <para>
+
+ Python has a lot of good
+ documentation freely available on-line
+ to help you get started.
+ The standard tutorial is available at XXX.
+
+
+ </para>
+
+ <para>
+
+ Python is very easy to pick up.
+
+ </para>
+
+ <para>
+
+ Python variables must be assigned to before they can be referenced.
+
+ </para>
+
+ <para>
+
+ Assignment is like most programming languages:
+
+ x = 1 + 2
+ z = 3 * x
+
+ </para>
+
+ <para>
+
+ Function calls look like most language function calls:
+
+ a = f(g)
+
+ </para>
+
+ <para>
+
+ Define functions like so:
+
+ def func(arg1, arg2):
+ return arg1 * arg 2
+
+ The number of parameters
+
+ </para>
+
+ <para>
+
+ Strings can be enclosed in single quotes or double quotes,
+ backslashes are used to escape characters,
+ triple-quote syntax lets you include quotes and newlines,
+ raw strings begin with 'r'.
+
+ </para>
+
+ <para>
+
+ Lists are enclosed in square brackets,
+ list items are separated by commas.
+ List references use square brackets and integer index values,
+ slice notation lets you select, delete or replace a range.
+
+ </para>
+
+ <para>
+
+ Dictionaries (hashes) are enclosed in curly brackets,
+ : separates keys from values,
+ , separates items.
+ Dictionary values are referenced using square brackets.
+
+ </para>
+
+ <para>
+
+ Access class attributes (including methods) using a '.'.
+
+ </para>
+
+ <para>
+
+ if: statements look like
+
+ elif: statements look like
+
+ else: statements look like
+
+ </para>
+
+ <para>
+
+ for: statements look like
+
+ while: statements look like
+
+ break statements look like
+
+ continue statements look like
+
+ </para>
+
+ <para>
+
+ pass
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <section>
+ <title>Programs: the &Program; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the executable program name to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ Program('prog', 'file1.o')
+ </programlisting>
+
+ <para>
+
+ Will create the &prog;
+ executable on a POSIX system,
+ the &prog_exe; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx')
+ env.Program('prog', ['file1.o', 'file2.o'])
+ </programlisting>
+
+ <para>
+
+ Will create a program named
+ <filename>myprog.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <para>
+
+ If you omit the &target;,
+ the base of the first input
+ file name specified
+ becomes the base of the target
+ program created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ Program(['hello.c', 'goodbye.c'])
+ </programlisting>
+
+ <para>
+
+ Will create the &hello;
+ executable on a POSIX system,
+ the &hello_exe; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="libs">
+ <file name="SConstruct" printme="1">
+ env = Environment(LIBS = ['foo1', 'foo2'],
+ LIBPATH = ['/usr/dir1', 'dir2'])
+ env.Program(['hello.c', 'goodbye.c'])
+ </file>
+ <file name="hello.c">
+ int hello() { printf("Hello, world!\n"); }
+ </file>
+ <file name="goodbye.c">
+ int goodbye() { printf("Goodbye, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Will execute as follows on a POSIX system:
+
+ </para>
+
+ <scons_output example="libs" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And execute as follows on a Windows system:
+
+ </para>
+
+ <scons_output example="libs" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Object-File Builders</title>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <section>
+ <title>The &StaticObject; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the static object file to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ StaticObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ object file on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx')
+ env.StaticObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>myfile.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <para>
+
+ If you omit the &target;,
+ 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:
+
+ </para>
+
+ <programlisting>
+ StaticObject('file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ executable on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &SharedObject; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the shared object file to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ SharedObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ object file on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx')
+ env.SharedObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>myfile.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <para>
+
+ If you omit the &target;,
+ 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:
+
+ </para>
+
+ <programlisting>
+ SharedObject('file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ executable on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &Object; Builder</title>
+
+ <para>
+
+ The &b-link-Object; Builder is a synonym for &b-link-StaticObject;
+ and is completely equivalent.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Library Builders</title>
+
+ <para>
+
+ &SCons; provides separate Builder objects
+ to create static and shared libraries.
+
+ </para>
+
+ <section>
+ <title>The &StaticLibrary; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the static library to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ StaticLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx')
+ env.StaticLibrary('lib', ['file1.o', 'file2.o'])
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>mylib.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <programlisting>
+ StaticLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ If you omit the &target;,
+ 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:
+
+ </para>
+
+ <programlisting>
+ StaticLibrary(['file.c', 'another.c'])
+ </programlisting>
+
+ <para>
+
+ Will create the &libfile_a;
+ library on a POSIX system,
+ the &file_lib; library on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &SharedLibrary; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the shared library to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ SharedLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx')
+ env.SharedLibrary('shared', ['file1.o', 'file2.o'])
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>myshared.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <programlisting>
+ SharedLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ If you omit the &target;,
+ the base of the first input
+ file name specified
+ becomes the base of the name of the shared library to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ SharedLibrary(['file.c', 'another.c'])
+ </programlisting>
+
+ <para>
+
+ Will create the &libfile_so;
+ library on a POSIX system,
+ the &file_dll; library on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &Library; Builder</title>
+
+ <para>
+
+ The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary;
+ and is completely equivalent.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Pre-Compiled Headers: the &PCH; Builder</title>
+
+ <para>
+
+ XXX PCH()
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Microsoft Visual C++ Resource Files: the &RES; Builder</title>
+
+ <para>
+
+ XXX RES()
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Source Files</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>The &CFile; Builder</title>
+
+ <para>
+
+ XXX CFile()
+
+ </para>
+
+ <programlisting>
+ XXX CFile() programlisting
+ </programlisting>
+
+ <screen>
+ XXX CFile() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &CXXFile; Builder</title>
+
+ <para>
+
+ XXX CXXFILE()
+
+ </para>
+
+ <programlisting>
+ XXX CXXFILE() programlisting
+ </programlisting>
+
+ <screen>
+ XXX CXXFILE() screen
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Documents</title>
+
+ <para>
+
+ &SCons; provides a number of Builder objects
+ for creating different types of documents.
+
+ </para>
+
+ <section>
+ <title>The &DVI; Builder</title>
+
+ <para>
+
+ XXX DVI() para
+
+ </para>
+
+ <programlisting>
+ XXX DVI() programlisting
+ </programlisting>
+
+ <screen>
+ XXX DVI() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &PDF; Builder</title>
+
+ <para>
+
+ XXX PDF() para
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &PostScript; Builder</title>
+
+ <para>
+
+ XXX PostScript() para
+
+ </para>
+
+ <programlisting>
+ XXX PostScript() programlisting
+ </programlisting>
+
+ <screen>
+ XXX PostScript() screen
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Archives</title>
+
+ <para>
+
+ &SCons; provides Builder objects
+ for creating two different types of archive files.
+
+ </para>
+
+ <section>
+ <title>The &Tar; Builder</title>
+
+ <para>
+
+ The &b-link-Tar; Builder object uses the &tar;
+ utility to create archives of files
+ and/or directory trees:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Tar('out1.tar', ['file1', 'file2'])
+ env.Tar('out2', 'directory')
+ </file>
+ <file name="file1">
+ file1
+ </file>
+ <file name="file2">
+ file2
+ </file>
+ <file name="directory/file3">
+ directory/file3
+ </file>
+ </scons_example>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ One common requirement when creating a &tar; archive
+ is to create a compressed archive using the
+ <option>-z</option> 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 <option>-c</option> 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:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ env = Environment(TARFLAGS = '-c -z')
+ env.Tar('out.tar.gz', 'directory')
+ </file>
+ <file name="directory/file">
+ directory/file
+ </file>
+ </scons_example>
+
+ <scons_output example="ex2" os="posix">
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct" printme="1">
+ env = Environment(TARFLAGS = '-c -z',
+ TARSUFFIX = '.tgz')
+ env.Tar('out', 'directory')
+ </file>
+ <file name="directory/file">
+ directory/file
+ </file>
+ </scons_example>
+
+ <scons_output example="ex3" os="posix">
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>The &Zip; Builder</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Zip('out', ['file1', 'file2'])
+ </file>
+ <file name="file1">
+ file1
+ </file>
+ <file name="file2">
+ file2
+ </file>
+ </scons_example>
+
+ <para>
+
+ Your output will reflect the fact
+ that an internal Python function
+ is being used to create the output ZIP archive:
+
+ </para>
+
+ <scons_output example="ex4" os="posix">
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If you're using Python version 1.5.2 to run &SCons;,
+ then &SCons; will try to use an external
+ &zip; program as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ zip /home/my/project/zip.out file1 file2
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Java</title>
+
+ <para>
+
+ &SCons; provides Builder objects
+ for creating various types of Java output files.
+
+ </para>
+
+ <section>
+ <title>Building Class Files: the &Java; Builder</title>
+
+ <para>
+
+ The &b-link-Java; builder takes one or more input
+ <filename>.java</filename> files
+ and turns them into one or more
+ <filename>.class</filename> files
+ Unlike most builders, however,
+ the &Java; builder takes
+ target and source <emphasis>directories</emphasis>,
+ not files, as input.
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Java(target = 'classes', source = 'src')
+ </programlisting>
+
+ <para>
+
+ The &Java; builder will then
+ search the specified source directory
+ tree for all <filename>.java</filename> files,
+ and pass any out-of-date
+
+ </para>
+
+ <screen>
+ XXX Java() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &Jar; Builder</title>
+
+ <para>
+
+ XXX The &Jar; builder object
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Java(target = 'classes', source = 'src')
+ env.Jar(target = '', source = 'classes')
+ </programlisting>
+
+ <screen>
+ XXX Jar() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Building C header and stub files: the &JavaH; Builder</title>
+
+ <para>
+
+ XXX JavaH() para
+
+ </para>
+
+ <programlisting>
+ XXX JavaH() programlisting
+ </programlisting>
+
+ <screen>
+ XXX JavaH() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Building RMI stub and skeleton class files: the &RMIC; Builder</title>
+
+ <para>
+
+ XXX RMIC() para
+
+ </para>
+
+ <programlisting>
+ XXX RMIC() programlisting
+ </programlisting>
+
+ <screen>
+ XXX RMIC() screen
+ </screen>
+
+ </section>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <section>
+ <title>Programs: the &Program; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the executable program name to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ Program('prog', 'file1.o')
+ </programlisting>
+
+ <para>
+
+ Will create the &prog;
+ executable on a POSIX system,
+ the &prog_exe; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx')
+ env.Program('prog', ['file1.o', 'file2.o'])
+ </programlisting>
+
+ <para>
+
+ Will create a program named
+ <filename>myprog.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <para>
+
+ If you omit the &target;,
+ the base of the first input
+ file name specified
+ becomes the base of the target
+ program created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ Program(['hello.c', 'goodbye.c'])
+ </programlisting>
+
+ <para>
+
+ Will create the &hello;
+ executable on a POSIX system,
+ the &hello_exe; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(LIBS = ['foo1', 'foo2'],
+ LIBPATH = ['/usr/dir1', 'dir2'])
+ env.Program(['hello.c', 'goodbye.c'])
+ </programlisting>
+
+ <para>
+
+ Will execute as follows on a POSIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ And execute as follows on a Windows system:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Object-File Builders</title>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <section>
+ <title>The &StaticObject; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the static object file to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ StaticObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ object file on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx')
+ env.StaticObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>myfile.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <para>
+
+ If you omit the &target;,
+ 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:
+
+ </para>
+
+ <programlisting>
+ StaticObject('file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ executable on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &SharedObject; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the shared object file to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ SharedObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ object file on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx')
+ env.SharedObject('file', 'file.c')
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>myfile.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <para>
+
+ If you omit the &target;,
+ 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:
+
+ </para>
+
+ <programlisting>
+ SharedObject('file.c')
+ </programlisting>
+
+ <para>
+
+ Will create the &file_o;
+ executable on a POSIX system,
+ the &file_obj; executable on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &Object; Builder</title>
+
+ <para>
+
+ The &b-link-Object; Builder is a synonym for &b-link-StaticObject;
+ and is completely equivalent.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Library Builders</title>
+
+ <para>
+
+ &SCons; provides separate Builder objects
+ to create static and shared libraries.
+
+ </para>
+
+ <section>
+ <title>The &StaticLibrary; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the static library to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ StaticLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx')
+ env.StaticLibrary('lib', ['file1.o', 'file2.o'])
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>mylib.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <programlisting>
+ StaticLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ If you omit the &target;,
+ 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:
+
+ </para>
+
+ <programlisting>
+ StaticLibrary(['file.c', 'another.c'])
+ </programlisting>
+
+ <para>
+
+ Will create the &libfile_a;
+ library on a POSIX system,
+ the &file_lib; library on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &SharedLibrary; Builder</title>
+
+ <para>
+
+ 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 &target; argument is the
+ name of the shared library to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ SharedLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx')
+ env.SharedLibrary('shared', ['file1.o', 'file2.o'])
+ </programlisting>
+
+ <para>
+
+ Will create an object file named
+ <filename>myshared.xxx</filename>
+ regardless of the system on which it is run.
+
+ </para>
+
+ <programlisting>
+ SharedLibrary('foo', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ If you omit the &target;,
+ the base of the first input
+ file name specified
+ becomes the base of the name of the shared library to be created.
+ For example:
+
+ </para>
+
+ <programlisting>
+ SharedLibrary(['file.c', 'another.c'])
+ </programlisting>
+
+ <para>
+
+ Will create the &libfile_so;
+ library on a POSIX system,
+ the &file_dll; library on a Windows system.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &Library; Builder</title>
+
+ <para>
+
+ The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary;
+ and is completely equivalent.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Pre-Compiled Headers: the &PCH; Builder</title>
+
+ <para>
+
+ XXX PCH()
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Microsoft Visual C++ Resource Files: the &RES; Builder</title>
+
+ <para>
+
+ XXX RES()
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Source Files</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>The &CFile; Builder</title>
+
+ <para>
+
+ XXX CFile()
+
+ </para>
+
+ <programlisting>
+ XXX CFile() programlisting
+ </programlisting>
+
+ <screen>
+ XXX CFile() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &CXXFile; Builder</title>
+
+ <para>
+
+ XXX CXXFILE()
+
+ </para>
+
+ <programlisting>
+ XXX CXXFILE() programlisting
+ </programlisting>
+
+ <screen>
+ XXX CXXFILE() screen
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Documents</title>
+
+ <para>
+
+ &SCons; provides a number of Builder objects
+ for creating different types of documents.
+
+ </para>
+
+ <section>
+ <title>The &DVI; Builder</title>
+
+ <para>
+
+ XXX DVI() para
+
+ </para>
+
+ <programlisting>
+ XXX DVI() programlisting
+ </programlisting>
+
+ <screen>
+ XXX DVI() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &PDF; Builder</title>
+
+ <para>
+
+ XXX PDF() para
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &PostScript; Builder</title>
+
+ <para>
+
+ XXX PostScript() para
+
+ </para>
+
+ <programlisting>
+ XXX PostScript() programlisting
+ </programlisting>
+
+ <screen>
+ XXX PostScript() screen
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Archives</title>
+
+ <para>
+
+ &SCons; provides Builder objects
+ for creating two different types of archive files.
+
+ </para>
+
+ <section>
+ <title>The &Tar; Builder</title>
+
+ <para>
+
+ The &b-link-Tar; Builder object uses the &tar;
+ utility to create archives of files
+ and/or directory trees:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Tar('out1.tar', ['file1', 'file2'])
+ env.Tar('out2', 'directory')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ tar -c -f out1.tar file1 file2
+ tar -c -f out2.tar directory
+ </screen>
+
+ <para>
+
+ One common requirement when creating a &tar; archive
+ is to create a compressed archive using the
+ <option>-z</option> 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 <option>-c</option> 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(TARFLAGS = '-c -z')
+ env.Tar('out.tar.gz', 'directory')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ tar -c -z -f out.tar.gz directory
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(TARFLAGS = '-c -z',
+ TARSUFFIX = '.tgz')
+ env.Tar('out', 'directory')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ tar -c -z -f out.tgz directory
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &Zip; Builder</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Zip('out', ['file1', 'file2'])
+ </programlisting>
+
+ <para>
+
+ Your output will reflect the fact
+ that an internal Python function
+ is being used to create the output ZIP archive:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ zip(["out.zip"], ["file1", "file2"])
+ </screen>
+
+ <para>
+
+ If you're using Python version 1.5.2 to run &SCons;,
+ then &SCons; will try to use an external
+ &zip; program as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ zip /home/my/project/zip.out file1 file2
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Java</title>
+
+ <para>
+
+ &SCons; provides Builder objects
+ for creating various types of Java output files.
+
+ </para>
+
+ <section>
+ <title>Building Class Files: the &Java; Builder</title>
+
+ <para>
+
+ The &b-link-Java; builder takes one or more input
+ <filename>.java</filename> files
+ and turns them into one or more
+ <filename>.class</filename> files
+ Unlike most builders, however,
+ the &Java; builder takes
+ target and source <emphasis>directories</emphasis>,
+ not files, as input.
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Java(target = 'classes', source = 'src')
+ </programlisting>
+
+ <para>
+
+ The &Java; builder will then
+ search the specified source directory
+ tree for all <filename>.java</filename> files,
+ and pass any out-of-date
+
+ </para>
+
+ <screen>
+ XXX Java() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &Jar; Builder</title>
+
+ <para>
+
+ XXX The &Jar; builder object
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Java(target = 'classes', source = 'src')
+ env.Jar(target = '', source = 'classes')
+ </programlisting>
+
+ <screen>
+ XXX Jar() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Building C header and stub files: the &JavaH; Builder</title>
+
+ <para>
+
+ XXX JavaH() para
+
+ </para>
+
+ <programlisting>
+ XXX JavaH() programlisting
+ </programlisting>
+
+ <screen>
+ XXX JavaH() screen
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Building RMI stub and skeleton class files: the &RMIC; Builder</title>
+
+ <para>
+
+ XXX RMIC() para
+
+ </para>
+
+ <programlisting>
+ XXX RMIC() programlisting
+ </programlisting>
+
+ <screen>
+ XXX RMIC() screen
+ </screen>
+
+ </section>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <!--
+
+ =head2 The C<Command> method
+
+
+ The C<Command> method is called as follows:
+
+ Command $env <target>, <inputs>, <build action>;
+
+ The target is made dependent upon the list of input files specified, and the
+ inputs must be built successfully or Cons will not attempt to build the
+ target.
+
+ To specify a command with multiple targets, you can specify a reference to a
+ list of targets. In Perl, a list reference can be created by enclosing a
+ list in square brackets. Hence the following command:
+
+ Command $env ['foo.h', 'foo.c'], 'foo.template', q(
+ gen %1
+ );
+
+ could be used in a case where the command C<gen> creates two files, both
+ F<foo.h> and F<foo.c>.
+
+ -->
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET")
+ </file>
+ <file name="foo.in">
+ foo.in
+ </file>
+ </scons_example>
+
+ <para>
+
+ When executed,
+ &SCons; runs the specified command,
+ substituting &cv-link-SOURCE; and &cv-link-TARGET;
+ as expected:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ This is often more convenient than
+ creating a &Builder; object
+ and adding it to the &cv-link-BUILDERS; variable
+ of a &consenv;
+
+ </para>
+
+ <para>
+
+ Note that the action you specify to the
+ &Command; &Builder; can be any legal &SCons; &Action;,
+ such as a Python function:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ def build(target, source, env):
+ # Whatever it takes to build
+ return None
+ env.Command('foo.out', 'foo.in', build)
+ </file>
+ <file name="foo.in">
+ foo.in
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which executes as follows:
+
+ </para>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct" printme="1">
+ env.Command('${SOURCE.basename}.out', 'foo.in', build)
+ </file>
+ </scons_example>
+
+
+ <para>
+
+ which does the same thing as the previous example, but allows you
+ to avoid repeating yourself.
+
+ </para>
+
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <!--
+
+ =head2 The C<Command> method
+
+
+ The C<Command> method is called as follows:
+
+ Command $env <target>, <inputs>, <build action>;
+
+ The target is made dependent upon the list of input files specified, and the
+ inputs must be built successfully or Cons will not attempt to build the
+ target.
+
+ To specify a command with multiple targets, you can specify a reference to a
+ list of targets. In Perl, a list reference can be created by enclosing a
+ list in square brackets. Hence the following command:
+
+ Command $env ['foo.h', 'foo.c'], 'foo.template', q(
+ gen %1
+ );
+
+ could be used in a case where the command C<gen> creates two files, both
+ F<foo.h> and F<foo.c>.
+
+ -->
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Command('foo.out', 'foo.in', "sed 's/x/y/' &lt; $SOURCE &gt; $TARGET")
+ </programlisting>
+
+ <para>
+
+ When executed,
+ &SCons; runs the specified command,
+ substituting &cv-link-SOURCE; and &cv-link-TARGET;
+ as expected:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ sed 's/x/y/' &lt; foo.in &gt; foo.out
+ </screen>
+
+ <para>
+
+ This is often more convenient than
+ creating a &Builder; object
+ and adding it to the &cv-link-BUILDERS; variable
+ of a &consenv;
+
+ </para>
+
+ <para>
+
+ Note that the action you specify to the
+ &Command; &Builder; can be any legal &SCons; &Action;,
+ such as a Python function:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ def build(target, source, env):
+ # Whatever it takes to build
+ return None
+ env.Command('foo.out', 'foo.in', build)
+ </programlisting>
+
+ <para>
+
+ Which executes as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ build(["foo.out"], ["foo.in"])
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env.Command('${SOURCE.basename}.out', 'foo.in', build)
+ </programlisting>
+
+
+ <para>
+
+ which does the same thing as the previous example, but allows you
+ to avoid repeating yourself.
+
+ </para>
+
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head2 Adding new methods
+
+For slightly more demanding changes, you may wish to add new methods to the
+C<cons> package. Here's an example of a very simple extension,
+C<InstallScript>, which installs a tcl script in a requested location, but
+edits the script first to reflect a platform-dependent path that needs to be
+installed in the script:
+
+ # cons::InstallScript - Create a platform dependent version of a shell
+ # script by replacing string ``#!your-path-here'' with platform specific
+ # path $BIN_DIR.
+
+ sub cons::InstallScript {
+ my ($env, $dst, $src) = @_;
+ Command $env $dst, $src, qq(
+ sed s+your-path-here+$BIN_DIR+ %< > %>
+ chmod oug+x %>
+ );
+ }
+
+Notice that this method is defined directly in the C<cons> package (by
+prefixing the name with C<cons::>). A change made in this manner will be
+globally visible to all environments, and could be called as in the
+following example:
+
+ InstallScript $env "$BIN/foo", "foo.tcl";
+
+For a small improvement in generality, the C<BINDIR> variable could be
+passed in as an argument or taken from the construction environment-,-as
+C<%BINDIR>.
+
+
+=head2 Overriding methods
+
+Instead of adding the method to the C<cons> name space, you could define a
+new package which inherits existing methods from the C<cons> package and
+overrides or adds others. This can be done using Perl's inheritance
+mechanisms.
+
+The following example defines a new package C<cons::switch> which
+overrides the standard C<Library> method. The overridden method builds
+linked library modules, rather than library archives. A new
+constructor is provided. Environments created with this constructor
+will have the new library method; others won't.
+
+ package cons::switch;
+ BEGIN {@ISA = 'cons'}
+
+ sub new {
+ shift;
+ bless new cons(@_);
+ }
+
+ sub Library {
+ my($env) = shift;
+ my($lib) = shift;
+ my(@objs) = Objects $env @_;
+ Command $env $lib, @objs, q(
+ %LD -r %LDFLAGS %< -o %>
+ );
+ }
+
+This functionality could be invoked as in the following example:
+
+ $env = new cons::switch(@overrides);
+ ...
+ Library $env 'lib.o', 'foo.c', 'bar.c';
+
+-->
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <section>
+ <title>Writing Builders That Execute External Commands</title>
+
+ <para>
+
+ 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
+ <literal>foobuild</literal>,
+ creating that &Builder; might look like:
+
+ </para>
+
+ <programlisting>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ </programlisting>
+
+ <para>
+
+ All the above line does is create a free-standing
+ &Builder; object.
+ The next section will show us how to actually use it.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Attaching a Builder to a &ConsEnv;</title>
+
+ <para>
+
+ 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
+ <function>Foo</function>,
+ our &SConstruct; file might look like:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct">
+ 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>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ </sconstruct>
+
+ <para>
+
+ With the &Builder; attached to our &consenv;
+ with the name <function>Foo</function>,
+ we can now actually call it like so:
+
+ </para>
+
+ <programlisting>
+ env.Foo('file.foo', 'file.input')
+ </programlisting>
+
+ <para>
+
+ Then when we run &SCons; it looks like:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <!--
+ The ToolSurrogate stuff that's used to capture output initializes
+ SCons.Defaults.ConstructionEnvironment with its own list of TOOLS.
+ In this next example, we want to show the user that when they
+ set the BUILDERS explicitly, the call to env.Program() generates
+ an AttributeError. This won't happen with all of the default
+ ToolSurrogates in the default construction environment. To make the
+ AttributeError show up, we have to overwite the default construction
+ environment's TOOLS variable so Program() builder doesn't show up.
+
+ We do this by executing a slightly different SConstruct file than the
+ one we print in the guide, with two extra statements at the front
+ that overwrite the TOOLS variable as described. Note that we have
+ to jam those statements on to the first line to keep the line number
+ in the generated error consistent with what the user will see in the
+ User's Guide.
+ -->
+ <scons_example name="ex2">
+ <file name="SConstruct">
+ import SCons.Defaults; SCons.Defaults.ConstructionEnvironment['TOOLS'] = {}; bld = Builder(action = 'foobuild &lt; $SOURCE &gt; $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </file>
+ <file name="SConstruct.printme" printme="1">
+ bld = Builder(action = 'foobuild &lt; $SOURCE &gt; $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct">
+ 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>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ env = Environment()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env.Append(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </sconstruct>
+
+ <para>
+
+ Or you can explicitly set the appropriately-named
+ key in the &cv-BUILDERS; dictionary:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env['BUILDERS']['Foo'] = bld
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </sconstruct>
+
+ <para>
+
+ Either way, the same &consenv;
+ can then use both the newly-defined
+ <function>Foo</function> &Builder;
+ and the default &b-link-Program; &Builder;:
+
+ </para>
+
+ <scons_output example="ex3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Letting &SCons; Handle The File Suffixes</title>
+
+ <para>
+
+ 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 <literal>Foo</literal>
+ &Builder; to build the <literal>file.foo</literal>
+ target file from the <literal>file.input</literal> source file,
+ you can give the <literal>.foo</literal>
+ and <literal>.input</literal> suffixes to the &Builder;,
+ making for more compact and readable calls to
+ the <literal>Foo</literal> &Builder;:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct">
+ 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')
+ </file>
+ <file name="file1.input">
+ file1.input
+ </file>
+ <file name="file2.input">
+ file2.input
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file1')
+ env.Foo('file2')
+ </sconstruct>
+
+ <scons_output example="ex4">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can also supply a <literal>prefix</literal> keyword argument
+ if it's appropriate to have &SCons; append a prefix
+ to the beginning of target file names.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builders That Execute Python Functions</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ def build_function(target, source, env):
+ # Code to build "target" from "source"
+ return None
+ </programlisting>
+
+ <para>
+
+ The arguments of a &buildfunc; are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>target</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>source</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ The builder function must
+ return a <literal>0</literal> or <literal>None</literal> 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,
+
+ </para>
+
+ <para>
+
+ 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
+ <literal>action</literal>
+ argument:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ 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>
+ <file name="file.input">
+ file.input
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ex5">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Builders That Create Actions Using a &Generator;</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ def generate_actions(source, target, env, for_signature):
+ return 'foobuild < %s > %s' % (target[0], source[0])
+ </programlisting>
+
+ <para>
+
+ The arguments of a &generator; are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>source</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>target</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>for_signature</term>
+
+ <listitem>
+ <para>
+
+ A flag that specifies whether the
+ generator is being called to contribute to a build signature,
+ as opposed to actually executing the command.
+
+ <!-- XXX NEED MORE HERE, describe generators use in signatures -->
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ Once you've defined a &generator;,
+ you create a &Builder; to use it
+ by specifying the generator keyword argument
+ instead of <literal>action</literal>.
+
+ </para>
+
+ <scons_example name="ex6">
+ <file name="SConstruct">
+ 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>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ 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')
+ </sconstruct>
+
+ <scons_output example="ex6">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that it's illegal to specify both an
+ <literal>action</literal>
+ and a
+ <literal>generator</literal>
+ for a &Builder;.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builders That Modify the Target or Source Lists Using an &Emitter;</title>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ For example, suppose you want to define a Builder
+ that always calls a <filename>foobuild</filename> program,
+ and you want to automatically add
+ a new target file named
+ <filename>new_target</filename>
+ and a new source file named
+ <filename>new_source</filename>
+ whenever it's called.
+ The &SConstruct; file might look like this:
+
+ </para>
+
+ <scons_example name="ex7">
+ <file name="SConstruct">
+ 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>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="new_source">
+ new_source
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ 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')
+ </sconstruct>
+
+ <para>
+
+ And would yield the following output:
+
+ </para>
+
+ <scons_output example="ex7">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="MY_EMITTER">
+
+ <file name="SConstruct" printme="1">
+ 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()
+ </file>
+ <file name="file1.input">
+ file1.input
+ </file>
+ <file name="file2.input">
+ file2.input
+ </file>
+ <file name="modify1.in">
+ modify1.input
+ </file>
+ <file name="modify2.in">
+ modify2.input
+ </file>
+ <file name="my_command" chmod="0755">
+ cat
+ </file>
+ </file>
+
+ </scons_example>
+
+ <sconstruct>
+ 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')
+ </file>
+ </sconstruct>
+
+ <para>
+
+ In this example, the <filename>modify1.in</filename>
+ and <filename>modify2.in</filename> files
+ get added to the source lists
+ of the different commands:
+
+ </para>
+
+ <scons_output example="MY_EMITTER">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>target_factor=, source_factory=</title>
+
+ </section>
+
+ <section>
+ <title>target_scanner=, source_scanner=</title>
+
+ </section>
+
+ <section>
+ <title>multi=</title>
+
+ </section>
+
+ <section>
+ <title>single_source=</title>
+
+ </section>
+
+ <section>
+ <title>src_builder=</title>
+
+ </section>
+
+ <section>
+ <title>ensure_suffix=</title>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Where To Put Your Custom Builders and Tools</title>
+
+ <para>
+
+ The <filename>site_scons</filename> 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.
+
+ </para>
+
+ <para>
+
+ 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
+ <filename>site_scons</filename> 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().
+
+ </para>
+
+ <para>
+
+ A single-function Tool can just be included in your
+ <filename>site_scons/site_init.py</filename> file where it will be
+ parsed and made available for use. For instance, you could have a
+ <filename>site_scons/site_init.py</filename> file like this:
+
+ </para>
+
+ <scons_example name="site1">
+ <file name="site_scons/site_init.py" printme=1>
+ 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
+ </file>
+ <file name="SConstruct">
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </file>
+ <file name="src">
+ hi there
+ </file>
+ </scons_example>
+
+ <para>
+
+ and a &SConstruct; like this:
+
+ </para>
+
+ <sconstruct>
+ # Use TOOL_ADD_HEADER from site_scons/site_init.py
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </sconstruct>
+
+ <para>
+
+ The <function>TOOL_ADD_HEADER</function> tool method will be
+ called to add the <function>AddHeader</function> tool to the
+ environment.
+
+ </para>
+
+ <!--
+ <scons_output example="site1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+ Similarly, a more full-fledged tool with
+ <function>exists()</function> and <function>generate()</function>
+ methods can be installed in
+ <filename>site_scons/site_tools/toolname.py</filename>. Since
+ <filename>site_scons/site_tools</filename> 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.
+ </para>
+
+ <para>
+ Many people have a library of utility Python functions they'd like
+ to include in &SConscript;s; just put that module in
+ <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+ choice. For instance you can do something like this in
+ <filename>site_scons/my_utils.py</filename> to add build_id and MakeWorkDir functions:
+ </para>
+
+ <scons_example name="site2">
+ <file name="site_scons/my_utils.py" printme=1>
+ 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))
+ </file>
+ <file name="SConscript">
+ import my_utils
+ MakeWorkDir('/tmp/work')
+ print "build_id=" + my_utils.build_id()
+ </file>
+ </scons_example>
+
+ <para>
+
+ And then in your &SConscript; or any sub-&SConscript; anywhere in
+ your build, you can import <filename>my_utils</filename> and use it:
+
+ </para>
+
+ <sconstruct>
+ import my_utils
+ print "build_id=" + my_utils.build_id()
+ my_utils.MakeWorkDir('/tmp/work')
+ </sconstruct>
+
+ <para>
+ Note that although you can put this library in
+ <filename>site_scons/site_init.py</filename>,
+ it is no better there than <filename>site_scons/my_utils.py</filename>
+ 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
+ </para>
+ <sconstruct>
+ from SCons.Script import *
+ </sconstruct>
+
+ <para>
+ This is true in modules in <filename>site_scons</filename> such as
+ <filename>site_scons/site_init.py</filename> as well.
+ </para>
+
+ <para>
+
+ If you have a machine-wide site dir you'd like to use instead of
+ <filename>./site_scons</filename>, use the
+ <literal>--site-dir</literal> option to point to your dir.
+ <filename>site_init.py</filename> and
+ <filename>site_tools</filename> will be located under that dir.
+ To avoid using a <filename>site_scons</filename> dir at all, even
+ if it exists, use the <literal>--no-site-dir</literal> option.
+
+ </para>
+
+ </section>
+
+
+ <!--
+
+ <section>
+ <title>Builders That Use Other Builders</title>
+
+ <para>
+
+ XXX para
+
+ </para>
+
+ <scons_example name="ex8">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ #env.SourceCode('.', env.BitKeeper('my_command'))
+ env.Program('hello.c')
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex8">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head2 Adding new methods
+
+For slightly more demanding changes, you may wish to add new methods to the
+C<cons> package. Here's an example of a very simple extension,
+C<InstallScript>, which installs a tcl script in a requested location, but
+edits the script first to reflect a platform-dependent path that needs to be
+installed in the script:
+
+ # cons::InstallScript - Create a platform dependent version of a shell
+ # script by replacing string ``#!your-path-here'' with platform specific
+ # path $BIN_DIR.
+
+ sub cons::InstallScript {
+ my ($env, $dst, $src) = @_;
+ Command $env $dst, $src, qq(
+ sed s+your-path-here+$BIN_DIR+ %< > %>
+ chmod oug+x %>
+ );
+ }
+
+Notice that this method is defined directly in the C<cons> package (by
+prefixing the name with C<cons::>). A change made in this manner will be
+globally visible to all environments, and could be called as in the
+following example:
+
+ InstallScript $env "$BIN/foo", "foo.tcl";
+
+For a small improvement in generality, the C<BINDIR> variable could be
+passed in as an argument or taken from the construction environment-,-as
+C<%BINDIR>.
+
+
+=head2 Overriding methods
+
+Instead of adding the method to the C<cons> name space, you could define a
+new package which inherits existing methods from the C<cons> package and
+overrides or adds others. This can be done using Perl's inheritance
+mechanisms.
+
+The following example defines a new package C<cons::switch> which
+overrides the standard C<Library> method. The overridden method builds
+linked library modules, rather than library archives. A new
+constructor is provided. Environments created with this constructor
+will have the new library method; others won't.
+
+ package cons::switch;
+ BEGIN {@ISA = 'cons'}
+
+ sub new {
+ shift;
+ bless new cons(@_);
+ }
+
+ sub Library {
+ my($env) = shift;
+ my($lib) = shift;
+ my(@objs) = Objects $env @_;
+ Command $env $lib, @objs, q(
+ %LD -r %LDFLAGS %< -o %>
+ );
+ }
+
+This functionality could be invoked as in the following example:
+
+ $env = new cons::switch(@overrides);
+ ...
+ Library $env 'lib.o', 'foo.c', 'bar.c';
+
+-->
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <section>
+ <title>Writing Builders That Execute External Commands</title>
+
+ <para>
+
+ 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
+ <literal>foobuild</literal>,
+ creating that &Builder; might look like:
+
+ </para>
+
+ <programlisting>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ </programlisting>
+
+ <para>
+
+ All the above line does is create a free-standing
+ &Builder; object.
+ The next section will show us how to actually use it.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Attaching a Builder to a &ConsEnv;</title>
+
+ <para>
+
+ 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
+ <function>Foo</function>,
+ our &SConstruct; file might look like:
+
+ </para>
+
+
+
+ <programlisting>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ </programlisting>
+
+ <para>
+
+ With the &Builder; attached to our &consenv;
+ with the name <function>Foo</function>,
+ we can now actually call it like so:
+
+ </para>
+
+ <programlisting>
+ env.Foo('file.foo', 'file.input')
+ </programlisting>
+
+ <para>
+
+ Then when we run &SCons; it looks like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ foobuild &lt; file.input &gt; file.foo
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <!--
+ The ToolSurrogate stuff that's used to capture output initializes
+ SCons.Defaults.ConstructionEnvironment with its own list of TOOLS.
+ In this next example, we want to show the user that when they
+ set the BUILDERS explicitly, the call to env.Program() generates
+ an AttributeError. This won't happen with all of the default
+ ToolSurrogates in the default construction environment. To make the
+ AttributeError show up, we have to overwite the default construction
+ environment's TOOLS variable so Program() builder doesn't show up.
+
+ We do this by executing a slightly different SConstruct file than the
+ one we print in the guide, with two extra statements at the front
+ that overwrite the TOOLS variable as described. Note that we have
+ to jam those statements on to the first line to keep the line number
+ in the generated error consistent with what the user will see in the
+ User's Guide.
+ -->
+ <programlisting>
+ bld = Builder(action = 'foobuild &lt; $SOURCE &gt; $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ AttributeError: SConsEnvironment instance has no attribute 'Program':
+ File "/home/my/project/SConstruct", line 4:
+ env.Program('hello.c')
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+
+
+ <programlisting>
+ env = Environment()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env.Append(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </programlisting>
+
+ <para>
+
+ Or you can explicitly set the appropriately-named
+ key in the &cv-BUILDERS; dictionary:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env['BUILDERS']['Foo'] = bld
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </programlisting>
+
+ <para>
+
+ Either way, the same &consenv;
+ can then use both the newly-defined
+ <function>Foo</function> &Builder;
+ and the default &b-link-Program; &Builder;:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ foobuild &lt; file.input &gt; file.foo
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Letting &SCons; Handle The File Suffixes</title>
+
+ <para>
+
+ 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 <literal>Foo</literal>
+ &Builder; to build the <literal>file.foo</literal>
+ target file from the <literal>file.input</literal> source file,
+ you can give the <literal>.foo</literal>
+ and <literal>.input</literal> suffixes to the &Builder;,
+ making for more compact and readable calls to
+ the <literal>Foo</literal> &Builder;:
+
+ </para>
+
+
+
+ <programlisting>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file1')
+ env.Foo('file2')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ foobuild &lt; file1.input &gt; file1.foo
+ foobuild &lt; file2.input &gt; file2.foo
+ </screen>
+
+ <para>
+
+ You can also supply a <literal>prefix</literal> keyword argument
+ if it's appropriate to have &SCons; append a prefix
+ to the beginning of target file names.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builders That Execute Python Functions</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ def build_function(target, source, env):
+ # Code to build "target" from "source"
+ return None
+ </programlisting>
+
+ <para>
+
+ The arguments of a &buildfunc; are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>target</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>source</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ The builder function must
+ return a <literal>0</literal> or <literal>None</literal> 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,
+
+ </para>
+
+ <para>
+
+ 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
+ <literal>action</literal>
+ argument:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ build_function(["file.foo"], ["file.input"])
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Builders That Create Actions Using a &Generator;</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ def generate_actions(source, target, env, for_signature):
+ return 'foobuild < %s > %s' % (target[0], source[0])
+ </programlisting>
+
+ <para>
+
+ The arguments of a &generator; are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>source</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>target</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>for_signature</term>
+
+ <listitem>
+ <para>
+
+ A flag that specifies whether the
+ generator is being called to contribute to a build signature,
+ as opposed to actually executing the command.
+
+ <!-- XXX NEED MORE HERE, describe generators use in signatures -->
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ Once you've defined a &generator;,
+ you create a &Builder; to use it
+ by specifying the generator keyword argument
+ instead of <literal>action</literal>.
+
+ </para>
+
+
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ foobuild &lt; file.input &gt; file.foo
+ </screen>
+
+ <para>
+
+ Note that it's illegal to specify both an
+ <literal>action</literal>
+ and a
+ <literal>generator</literal>
+ for a &Builder;.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builders That Modify the Target or Source Lists Using an &Emitter;</title>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ For example, suppose you want to define a Builder
+ that always calls a <filename>foobuild</filename> program,
+ and you want to automatically add
+ a new target file named
+ <filename>new_target</filename>
+ and a new source file named
+ <filename>new_source</filename>
+ whenever it's called.
+ The &SConstruct; file might look like this:
+
+ </para>
+
+
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ And would yield the following output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ foobuild file.foo new_target - file.input new_source
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ bld = Builder(action = 'my_command $SOURCES &gt; $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()
+
+
+ </programlisting>
+
+ <programlisting>
+ 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')
+
+ </programlisting>
+
+ <para>
+
+ In this example, the <filename>modify1.in</filename>
+ and <filename>modify2.in</filename> files
+ get added to the source lists
+ of the different commands:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ my_command file1.input modify1.in &gt; file1.foo
+ my_command file2.input modify2.in &gt; file2.foo
+ </screen>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>target_factor=, source_factory=</title>
+
+ </section>
+
+ <section>
+ <title>target_scanner=, source_scanner=</title>
+
+ </section>
+
+ <section>
+ <title>multi=</title>
+
+ </section>
+
+ <section>
+ <title>single_source=</title>
+
+ </section>
+
+ <section>
+ <title>src_builder=</title>
+
+ </section>
+
+ <section>
+ <title>ensure_suffix=</title>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Where To Put Your Custom Builders and Tools</title>
+
+ <para>
+
+ The <filename>site_scons</filename> 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.
+
+ </para>
+
+ <para>
+
+ 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
+ <filename>site_scons</filename> 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().
+
+ </para>
+
+ <para>
+
+ A single-function Tool can just be included in your
+ <filename>site_scons/site_init.py</filename> file where it will be
+ parsed and made available for use. For instance, you could have a
+ <filename>site_scons/site_init.py</filename> file like this:
+
+ </para>
+
+ <programlisting>
+ def TOOL_ADD_HEADER(env):
+ """A Tool to add a header from $HEADER to the source file"""
+ add_header = Builder(action=['echo "$HEADER" &gt; $TARGET',
+ 'cat $SOURCE &gt;&gt; $TARGET'])
+ env.Append(BUILDERS = {'AddHeader' : add_header})
+ env['HEADER'] = '' # set default value
+ </programlisting>
+
+ <para>
+
+ and a &SConstruct; like this:
+
+ </para>
+
+ <programlisting>
+ # Use TOOL_ADD_HEADER from site_scons/site_init.py
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </programlisting>
+
+ <para>
+
+ The <function>TOOL_ADD_HEADER</function> tool method will be
+ called to add the <function>AddHeader</function> tool to the
+ environment.
+
+ </para>
+
+ <!--
+ <scons_output example="site1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+ Similarly, a more full-fledged tool with
+ <function>exists()</function> and <function>generate()</function>
+ methods can be installed in
+ <filename>site_scons/site_tools/toolname.py</filename>. Since
+ <filename>site_scons/site_tools</filename> 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.
+ </para>
+
+ <para>
+ Many people have a library of utility Python functions they'd like
+ to include in &SConscript;s; just put that module in
+ <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+ choice. For instance you can do something like this in
+ <filename>site_scons/my_utils.py</filename> to add build_id and MakeWorkDir functions:
+ </para>
+
+ <programlisting>
+ 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))
+ </programlisting>
+
+ <para>
+
+ And then in your &SConscript; or any sub-&SConscript; anywhere in
+ your build, you can import <filename>my_utils</filename> and use it:
+
+ </para>
+
+ <programlisting>
+ import my_utils
+ print "build_id=" + my_utils.build_id()
+ my_utils.MakeWorkDir('/tmp/work')
+ </programlisting>
+
+ <para>
+ Note that although you can put this library in
+ <filename>site_scons/site_init.py</filename>,
+ it is no better there than <filename>site_scons/my_utils.py</filename>
+ 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
+ </para>
+ <programlisting>
+ from SCons.Script import *
+ </programlisting>
+
+ <para>
+ This is true in modules in <filename>site_scons</filename> such as
+ <filename>site_scons/site_init.py</filename> as well.
+ </para>
+
+ <para>
+
+ If you have a machine-wide site dir you'd like to use instead of
+ <filename>./site_scons</filename>, use the
+ <literal>--site-dir</literal> option to point to your dir.
+ <filename>site_init.py</filename> and
+ <filename>site_tools</filename> will be located under that dir.
+ To avoid using a <filename>site_scons</filename> dir at all, even
+ if it exists, use the <literal>--no-site-dir</literal> option.
+
+ </para>
+
+ </section>
+
+
+ <!--
+
+ <section>
+ <title>Builders That Use Other Builders</title>
+
+ <para>
+
+ XXX para
+
+ </para>
+
+ <scons_example name="ex8">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ #env.SourceCode('.', env.BitKeeper('my_command'))
+ env.Program('hello.c')
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex8">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="Command">
+
+<refmeta>
+<refentrytitle>Command</refentrytitle>
+</refmeta>
+
+<methodsynopsis>
+ <methodname>env.Command</methodname>
+ <methodparam>foo</methodparam>
+ <methodparam>bar</methodparam>
+</methodsynopsis>
+
+</refentry>
+-->
+
+<para>
+
+This appendix contains descriptions of all of the
+Builders that are <emphasis>potentially</emphasis>
+available "out of the box" in this version of SCons.
+
+</para>
+
+<variablelist>
+
+&builders-gen;
+
+</variablelist>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="Command">
+
+<refmeta>
+<refentrytitle>Command</refentrytitle>
+</refmeta>
+
+<methodsynopsis>
+ <methodname>env.Command</methodname>
+ <methodparam>foo</methodparam>
+ <methodparam>bar</methodparam>
+</methodsynopsis>
+
+</refentry>
+-->
+
+<para>
+
+This appendix contains descriptions of all of the
+Builders that are <emphasis>potentially</emphasis>
+available "out of the box" in this version of SCons.
+
+</para>
+
+<variablelist>
+
+&builders-gen;
+
+</variablelist>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Specifying the Shared Cache Directory</title>
+
+ <para>
+
+ To enable sharing of derived files,
+ use the &CacheDir; function
+ in any &SConscript; file:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct">
+ env = Environment()
+ env.Program('hello.c')
+ CacheDir('cache')
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ <directory name="cache">
+ </directory>
+ <file name="not_used" printme="1">
+ CacheDir('/usr/local/build_cache')
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+ <footnote>
+ <para>
+ Actually, the MD5 signature is used as the name of the file
+ in the shared cache directory in which the contents are stored.
+ </para>
+ </footnote>
+ 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:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <xref linkend="chap-depends"></xref>
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Keeping Build Output Consistent</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ If, however, you use the <literal>--cache-show</literal> option,
+ &SCons; will print the command line that it
+ <emphasis>would</emphasis> 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:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q --cache-show</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Not Using the Shared Cache for Specific Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex-NoCache">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ obj = env.Object('hello.c')
+ env.Program('hello.c')
+ CacheDir('cache')
+ NoCache('hello.o')
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ <directory name="cache">
+ </directory>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <!--
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ Retrieved `hello' from cache
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Disabling the Shared Cache</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ In these cases, you can specify
+ the <literal>--cache-disable</literal>
+ command-line option to tell &SCons;
+ to not retrieve already-built files from the
+ shared cache directory:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q --cache-disable</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Populating a Shared Cache With Already-Built Files</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ In this case, you can use the
+ the <literal>--cache-force</literal> 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:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q --cache-disable</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q --cache-disable</scons_output_command>
+ <scons_output_command>scons -Q --cache-force</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice how the above sample run
+ demonstrates that the <literal>--cache-disable</literal>
+ option avoids putting the built
+ <filename>hello.o</filename>
+ and
+ <filename>hello</filename> files in the cache,
+ but after using the <literal>--cache-force</literal> option,
+ the files have been put in the cache
+ for the next invocation to retrieve.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Minimizing Cache Contention: the <literal>--random</literal> Option</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex-random">
+ <file name="SConstruct" printme="1">
+ Program('prog',
+ ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c'])
+ </file>
+ <file name="f1.c">f1.c</file>
+ <file name="f2.c">f2.c</file>
+ <file name="f3.c">f3.c</file>
+ <file name="f4.c">f4.c</file>
+ <file name="f5.c">f5.c</file>
+ <file name="f6.c">f6.c</file>
+ </scons_example>
+
+ <para>
+
+ &SCons; will normally build the input object files
+ on which the program depends in their normal, sorted order:
+
+ </para>
+
+ <scons_output example="ex-random">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But if two such builds take place simultaneously,
+ they may each look in the cache at nearly the same
+ time and both decide that <filename>f1.o</filename>
+ must be rebuilt and pushed into the shared cache directory,
+ then both decide that <filename>f2.o</filename>
+ must be rebuilt (and pushed into the shared cache directory),
+ then both decide that <filename>f3.o</filename>
+ 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.
+
+ </para>
+
+ <para>
+
+ To alleviate such contention for the cache,
+ you can use the <literal>--random</literal> command-line option
+ to tell &SCons; to build dependencies
+ in a random order:
+
+ </para>
+
+ <!--
+
+ The following <screen> output was generated by this:
+
+ <scons_output example="ex-random">
+ <scons_output_command>scons -Q - -random</scons_output_command>
+ </scons_output>
+
+ We captured it directly here to guarantee a "random" order,
+ guarding against the potential for - -random to happen
+ to return things in the original sorted order.
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q --random</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Multiple builds using the <literal>--random</literal> 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.
+
+ </para>
+
+ <para>
+
+ Note, of course,
+ the <literal>--random</literal> 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.
+
+ </para>
+
+ <para>
+
+ If you want to make sure dependencies will be built
+ in a random order without having to specify
+ the <literal>--random</literal> on very command line,
+ you can use the &SetOption; function to
+ set the <literal>random</literal> option
+ within any &SConscript; file:
+
+ </para>
+
+ <scons_example name="ex-random">
+ <file name="SConstruct" printme="1">
+ SetOption('random', 1)
+ Program('prog',
+ ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c'])
+ </file>
+ <file name="f1.c">f1.c</file>
+ <file name="f2.c">f2.c</file>
+ <file name="f3.c">f3.c</file>
+ <file name="f4.c">f4.c</file>
+ <file name="f5.c">f5.c</file>
+ <file name="f6.c">f6.c</file>
+ </scons_example>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Troubleshooting Shared Caching: the &cache-debug; Option</title>
+
+ <para>
+
+ XXX describe the - - cache-debug option
+ XXX maybe point to the troubleshooting appendix?
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+
+ <para>
+
+ XXX describe CacheDir management: monitoring, deleting, etc.
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Specifying the Shared Cache Directory</title>
+
+ <para>
+
+ To enable sharing of derived files,
+ use the &CacheDir; function
+ in any &SConscript; file:
+
+ </para>
+
+ <programlisting>
+ CacheDir('/usr/local/build_cache')
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+ <footnote>
+ <para>
+ Actually, the MD5 signature is used as the name of the file
+ in the shared cache directory in which the contents are stored.
+ </para>
+ </footnote>
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q</userinput>
+ Retrieved `hello.o' from cache
+ Retrieved `hello' from cache
+ </screen>
+
+ <para>
+
+ 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 <xref linkend="chap-depends"></xref>
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Keeping Build Output Consistent</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ If, however, you use the <literal>--cache-show</literal> option,
+ &SCons; will print the command line that it
+ <emphasis>would</emphasis> 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q --cache-show</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Not Using the Shared Cache for Specific Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ obj = env.Object('hello.c')
+ env.Program('hello.c')
+ CacheDir('cache')
+ NoCache('hello.o')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <!--
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ Retrieved `hello' from cache
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Disabling the Shared Cache</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ In these cases, you can specify
+ the <literal>--cache-disable</literal>
+ command-line option to tell &SCons;
+ to not retrieve already-built files from the
+ shared cache directory:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q</userinput>
+ Retrieved `hello.o' from cache
+ Retrieved `hello' from cache
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q --cache-disable</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Populating a Shared Cache With Already-Built Files</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ In this case, you can use the
+ the <literal>--cache-force</literal> 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --cache-disable</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q -c</userinput>
+ Removed hello.o
+ Removed hello
+ % <userinput>scons -Q --cache-disable</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q --cache-force</userinput>
+ scons: `.' is up to date.
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Notice how the above sample run
+ demonstrates that the <literal>--cache-disable</literal>
+ option avoids putting the built
+ <filename>hello.o</filename>
+ and
+ <filename>hello</filename> files in the cache,
+ but after using the <literal>--cache-force</literal> option,
+ the files have been put in the cache
+ for the next invocation to retrieve.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Minimizing Cache Contention: the <literal>--random</literal> Option</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('prog',
+ ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c'])
+ </programlisting>
+
+ <para>
+
+ &SCons; will normally build the input object files
+ on which the program depends in their normal, sorted order:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ But if two such builds take place simultaneously,
+ they may each look in the cache at nearly the same
+ time and both decide that <filename>f1.o</filename>
+ must be rebuilt and pushed into the shared cache directory,
+ then both decide that <filename>f2.o</filename>
+ must be rebuilt (and pushed into the shared cache directory),
+ then both decide that <filename>f3.o</filename>
+ 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.
+
+ </para>
+
+ <para>
+
+ To alleviate such contention for the cache,
+ you can use the <literal>--random</literal> command-line option
+ to tell &SCons; to build dependencies
+ in a random order:
+
+ </para>
+
+ <!--
+
+ The following <screen> output was generated by this:
+
+ <scons_output example="ex-random">
+ <scons_output_command>scons -Q - -random</scons_output_command>
+ </scons_output>
+
+ We captured it directly here to guarantee a "random" order,
+ guarding against the potential for - -random to happen
+ to return things in the original sorted order.
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q --random</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Multiple builds using the <literal>--random</literal> 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.
+
+ </para>
+
+ <para>
+
+ Note, of course,
+ the <literal>--random</literal> 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.
+
+ </para>
+
+ <para>
+
+ If you want to make sure dependencies will be built
+ in a random order without having to specify
+ the <literal>--random</literal> on very command line,
+ you can use the &SetOption; function to
+ set the <literal>random</literal> option
+ within any &SConscript; file:
+
+ </para>
+
+ <programlisting>
+ 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'])
+ </programlisting>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Troubleshooting Shared Caching: the &cache-debug; Option</title>
+
+ <para>
+
+ XXX describe the - - cache-debug option
+ XXX maybe point to the troubleshooting appendix?
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+
+ <para>
+
+ XXX describe CacheDir management: monitoring, deleting, etc.
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Options</term>
+
+ <listitem>
+ <para>
+
+ Command-line options always begin with
+ one or two <literal>-</literal> (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 <xref linkend="sect-command-line-options"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Variables</term>
+
+ <listitem>
+ <para>
+
+ Any command-line argument containing an <literal>=</literal>
+ (equal sign) is considered a variable setting with the form
+ <varname>variable</varname>=<varname>value</varname>
+ &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 <xref linkend="sect-command-line-variables"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Targets</term>
+
+ <listitem>
+ <para>
+
+ 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 <xref linkend="sect-command-line-targets"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <section id="sect-command-line-options">
+ <title>Command-Line Options</title>
+
+ <para>
+
+ &SCons; has many <emphasis>command-line options</emphasis>
+ that control its behavior.
+ A &SCons; <emphasis>command-line option</emphasis>
+ always begins with one or two <literal>-</literal> (hyphen)
+ characters.
+
+ </para>
+
+ <section>
+ <title>Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable</title>
+
+ <para>
+
+ 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 <literal>-j 2</literal>
+ to have &SCons; run up to two build commands in parallel.
+ To avoid having to type <literal>-j 2</literal> 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.
+
+ </para>
+
+ <para>
+
+ If, for example,
+ you're using a POSIX shell that's
+ compatible with the Bourne shell,
+ and you always want &SCons; to use the
+ <literal>-Q</literal> option,
+ you can set the &SCONSFLAGS;
+ environment as follows:
+
+ </para>
+
+ <scons_example name="SCONSFLAGS">
+ <file name="SConstruct">
+ 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')
+ </file>
+ <file name="foo.in">
+ foo.in
+ </file>
+ </scons_example>
+
+ <scons_output example="SCONSFLAGS">
+ <scons_output_command>scons</scons_output_command>
+ <scons_output_command>export SCONSFLAGS="-Q"</scons_output_command>
+ <scons_output_command environment="SCONSFLAGS=-Q">scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Users of &csh;-style shells on POSIX systems
+ can set the &SCONSFLAGS; environment as follows:
+
+ </para>
+
+ <screen>
+ $ <userinput>setenv SCONSFLAGS "-Q"</userinput>
+ </screen>
+
+ <para>
+
+ Windows users may typically want to set the
+ &SCONSFLAGS; in the appropriate tab of the
+ <literal>System Properties</literal> window.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Getting Values Set by Command-Line Options: the &GetOption; Function</title>
+
+ <para>
+
+ &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 <literal>-h</literal> or <literal>--help</literal> 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.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis> specified
+ the <literal>-h</literal> or <literal>--help</literal> option,
+ like so:
+
+ </para>
+
+ <sconstruct)
+ if not GetOption('help'):
+ SConscript('src/SConscript', export='env')
+ </sconstruct>
+
+ <para>
+
+ 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
+ <xref linkend="sect-command-line-option-strings"></xref> section,
+ below.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Setting Values of Command-Line Options: the &SetOption; Function</title>
+
+ <para>
+
+ 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
+ <xref linkend="sect-command-line-option-strings"></xref> section,
+ below.
+
+ </para>
+
+ <para>
+
+ One use of the &SetOption; function is to
+ specify a value for the <literal>-j</literal>
+ or <literal>--jobs</literal> 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 <literal>-j</literal> option is
+ somewhat system-dependent.
+ One rough guideline is that the more processors
+ your system has,
+ the higher you want to set the
+ <literal>-j</literal> value,
+ in order to take advantage of the number of CPUs.
+
+ </para>
+
+ <para>
+
+ For example, suppose the administrators
+ of your development systems
+ have standardized on setting a
+ <varname>NUM_CPU</varname> 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:
+
+ </para>
+
+ <scons_example name="SetOption">
+ <file name="SConstruct" printme="1">
+ import os
+ num_cpu = int(os.environ.get('NUM_CPU', 2))
+ SetOption('num_jobs', num_cpu)
+ print "running with -j", GetOption('num_jobs')
+ </file>
+ <file name="foo.in">
+ foo.in
+ </file>
+ </scons_example>
+
+ <para>
+
+ The above snippet of code
+ sets the value of the <literal>--jobs</literal> option
+ to the value specified in the
+ <varname>$NUM_CPU</varname> 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 <literal>--jobs</literal>
+ value is <literal>num_jobs</literal>
+ for historical reasons.)
+ The code in this example prints the <literal>num_jobs</literal>
+ value for illustrative purposes.
+ It uses a default value of <literal>2</literal>
+ to provide some minimal parallelism even on
+ single-processor systems:
+
+ </para>
+
+ <scons_output example="SetOption">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But if the <varname>$NUM_CPU</varname>
+ environment variable is set,
+ then we use that for the default number of jobs:
+
+ </para>
+
+ <scons_output example="SetOption">
+ <scons_output_command>export NUM_CPU="4"</scons_output_command>
+ <scons_output_command environment="NUM_CPU=4">scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But any explicit
+ <literal>-j</literal> or <literal>--jobs</literal>
+ value the user specifies an the command line is used first,
+ regardless of whether or not
+ the <varname>$NUM_CPU</varname> environment
+ variable is set:
+
+ </para>
+
+ <scons_output example="SetOption">
+ <scons_output_command>scons -Q -j 7</scons_output_command>
+ <scons_output_command>export NUM_CPU="4"</scons_output_command>
+ <scons_output_command environment="NUM_CPU=4">scons -Q -j 3</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section id="sect-command-line-option-strings">
+ <title>Strings for Getting or Setting Values of &SCons; Command-Line Options</title>
+
+ <para>
+
+ 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: <literal>--</literal>),
+ after replacing any remaining hyphen characters
+ with underscores.
+
+ </para>
+
+ <para>
+
+ The full list of strings and the variables they
+ correspond to is as follows:
+
+ </para>
+
+ <informaltable>
+ <tgroup cols="2" align="left">
+
+ <thead>
+
+ <row>
+ <entry>String for &GetOption; and &SetOption;</entry>
+ <entry>Command-Line Option(s)</entry>
+ </row>
+
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry><literal>cache_debug</literal></entry>
+ <entry><option>--cache-debug</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>cache_disable</literal></entry>
+ <entry><option>--cache-disable</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>cache_force</literal></entry>
+ <entry><option>--cache-force</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>cache_show</literal></entry>
+ <entry><option>--cache-show</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>clean</literal></entry>
+ <entry><option>-c</option>,
+ <option>--clean</option>,
+ <option>--remove</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>config</literal></entry>
+ <entry><option>--config</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>directory</literal></entry>
+ <entry><option>-C</option>,
+ <option>--directory</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>diskcheck</literal></entry>
+ <entry><option>--diskcheck</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>duplicate</literal></entry>
+ <entry><option>--duplicate</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>file</literal></entry>
+ <entry><option>-f</option>,
+ <option>--file</option>,
+ <option>--makefile </option>,
+ <option>--sconstruct</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>help</literal></entry>
+ <entry><option>-h</option>,
+ <option>--help</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>ignore_errors</literal></entry>
+ <entry><option>--ignore-errors</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>implicit_cache</literal></entry>
+ <entry><option>--implicit-cache</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>implicit_deps_changed</literal></entry>
+ <entry><option>--implicit-deps-changed</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>implicit_deps_unchanged</literal></entry>
+ <entry><option>--implicit-deps-unchanged</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>interactive</literal></entry>
+ <entry><option>--interact</option>,
+ <option>--interactive</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>keep_going</literal></entry>
+ <entry><option>-k</option>,
+ <option>--keep-going</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>max_drift</literal></entry>
+ <entry><option>--max-drift</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>no_exec</literal></entry>
+ <entry><option>-n</option>,
+ <option>--no-exec</option>,
+ <option>--just-print</option>,
+ <option>--dry-run</option>,
+ <option>--recon</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>no_site_dir</literal></entry>
+ <entry><option>--no-site-dir</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>num_jobs</literal></entry>
+ <entry><option>-j</option>,
+ <option>--jobs</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>profile_file</literal></entry>
+ <entry><option>--profile</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>question</literal></entry>
+ <entry><option>-q</option>,
+ <option>--question</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>random</literal></entry>
+ <entry><option>--random</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>repository</literal></entry>
+ <entry><option>-Y</option>,
+ <option>--repository</option>,
+ <option>--srcdir</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>silent</literal></entry>
+ <entry><option>-s</option>,
+ <option>--silent</option>,
+ <option>--quiet</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>site_dir</literal></entry>
+ <entry><option>--site-dir</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>stack_size</literal></entry>
+ <entry><option>--stack-size</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>taskmastertrace_file</literal></entry>
+ <entry><option>--taskmastertrace</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>warn</literal></entry>
+ <entry><option>--warn</option> <option>--warning</option></entry>
+ </row>
+
+ </tbody>
+
+ </tgroup>
+ </informaltable>
+
+ </section>
+
+ <section>
+ <title>Adding Custom Command-Line Options: the &AddOption; Function</title>
+
+ <para>
+
+ &SCons; also allows you to define your own
+ command-line options with the &AddOption; function.
+ The &AddOption; function takes the same arguments
+ as the <function>optparse.add_option</function> function
+ from the standard Python library.
+ <footnote>
+ <para>
+ The &AddOption; function is,
+ in fact, implemented using a subclass
+ of the <classname>optparse.OptionParser</classname>.
+ </para>
+ </footnote>
+ 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.)
+
+ </para>
+
+ <para>
+
+ One useful example of using this functionality
+ is to provide a <option>--prefix</option> for users:
+
+ </para>
+
+ <scons_example name="AddOption">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ <file name="foo.in">
+ foo.in
+ </file>
+ </scons_example>
+
+ <para>
+
+ The above code uses the &GetOption; function
+ to set the <varname>$PREFIX</varname>
+ construction variable to any
+ value that the user specifies with a command-line
+ option of <literal>--prefix</literal>.
+ Because <varname>$PREFIX</varname>
+ will expand to a null string if it's not initialized,
+ running &SCons; without the
+ option of <literal>--prefix</literal>
+ will install the file in the
+ <filename>/usr/bin/</filename> directory:
+
+ </para>
+
+ <scons_output example="AddOption">
+ <scons_output_command>scons -Q -n</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But specifying <literal>--prefix=/tmp/install</literal>
+ on the command line causes the file to be installed in the
+ <filename>/tmp/install/usr/bin/</filename> directory:
+
+ </para>
+
+ <scons_output example="AddOption">
+ <scons_output_command>scons -Q -n --prefix=/tmp/install</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ </section>
+
+ <section id="sect-command-line-variables">
+ <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables</title>
+
+ <para>
+
+ You may want to control various aspects
+ of your build by allowing the user
+ to specify <varname>variable</varname>=<varname>value</varname>
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q debug=1</userinput>
+ </screen>
+
+ <para>
+
+ &SCons; provides an &ARGUMENTS; dictionary
+ that stores all of the
+ <varname>variable</varname>=<varname>value</varname>
+ 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 <emphasis>always</emphasis>
+ specify a variable,
+ you probably want to use
+ the Python
+ <literal>ARGUMENTS.get()</literal> function,
+ which allows you to specify a default value
+ to be used if there is no specification
+ on the command line.)
+
+ </para>
+
+ <para>
+
+ The following code sets the &cv-link-CCFLAGS; construction
+ variable in response to the <varname>debug</varname>
+ flag being set in the &ARGUMENTS; dictionary:
+
+ </para>
+
+ <scons_example name="ARGUMENTS">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ debug = ARGUMENTS.get('debug', 0)
+ if int(debug):
+ env.Append(CCFLAGS = '-g')
+ env.Program('prog.c')
+ </file>
+ <file name="prog.c">
+ prog.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ This results in the <varname>-g</varname>
+ compiler option being used when
+ <literal>debug=1</literal>
+ is used on the command line:
+
+ </para>
+
+ <scons_output example="ARGUMENTS">
+ <scons_output_command>scons -Q debug=0</scons_output_command>
+ <scons_output_command>scons -Q debug=0</scons_output_command>
+ <scons_output_command>scons -Q debug=1</scons_output_command>
+ <scons_output_command>scons -Q debug=1</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <literal>debug</literal>
+ argument has changed.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ To accomodate these requirements,
+ &SCons; provides an &ARGLIST; variable
+ that gives you direct access to
+ <varname>variable</varname>=<varname>value</varname>
+ 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
+ <varname>define=</varname>
+ settings on the command line:
+
+ </para>
+
+ <scons_example name="ARGLIST">
+ <file name="SConstruct" printme="1">
+ cppdefines = []
+ for key, value in ARGLIST:
+ if key == 'define':
+ cppdefines.append(value)
+ env = Environment(CPPDEFINES = cppdefines)
+ env.Object('prog.c')
+ </file>
+ <file name="prog.c">
+ prog.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Yields the following output:
+
+ </para>
+
+ <scons_output example="ARGLIST">
+ <scons_output_command>scons -Q define=FOO</scons_output_command>
+ <scons_output_command>scons -Q define=FOO define=BAR</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that the &ARGLIST; and &ARGUMENTS;
+ variables do not interfere with each other,
+ but merely provide slightly different views
+ into how the user specified
+ <varname>variable</varname>=<varname>value</varname>
+ 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).
+
+ </para>
+
+ <section>
+ <title>Controlling Command-Line Build Variables</title>
+
+ <para>
+
+ Being able to use a command-line build variable like
+ <literal>debug=1</literal> 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.
+
+ </para>
+
+ <para>
+
+ 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 <literal>-D</literal> 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:
+
+ </para>
+
+ <scons_example name="Variables1">
+ <file name="SConstruct" printme="1">
+ 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'])
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="bar.c">
+ bar.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ This &SConstruct; file first creates a &Variables; object
+ (the <literal>vars = Variables()</literal> 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 <literal>0</literal>
+ (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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="Variables1">
+ <scons_output_command>scons -Q RELEASE=1</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Providing Help for Command-Line Build Variables</title>
+
+ <para>
+
+ 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 <literal>scons -h</literal>.
+ 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:
+
+ </para>
+
+ <scons_example name="Variables_Help">
+ <file name="SConstruct" printme="1">
+ vars = Variables('custom.py')
+ vars.Add('RELEASE', 'Set to 1 to build for release', 0)
+ env = Environment(variables = vars)
+ Help(vars.GenerateHelpText(env))
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; will now display some useful text
+ when the <literal>-h</literal> option is used:
+
+ </para>
+
+ <scons_output example="Variables_Help">
+ <scons_output_command>scons -Q -h</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice that the help output shows the default value,
+ and the current actual value of the build variable.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Reading Build Variables From a File</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Variables_custom_py_1">
+ <file name="SConstruct" printme="1">
+ 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))
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="bar.c">
+ bar.c
+ </file>
+ <file name="custom.py">
+ RELEASE = 1
+ </file>
+ </scons_example>
+
+ <para>
+
+ This then allows the user to control the &RELEASE;
+ variable by setting it in the &custom_py; file:
+
+ </para>
+
+ <scons_example_file example="Variables_custom_py_1" name="custom.py"></scons_example_file>
+
+ <para>
+
+ Note that this file is actually executed
+ like a Python script.
+ Now when we run &SCons;:
+
+ </para>
+
+ <scons_output example="Variables_custom_py_1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And if we change the contents of &custom_py; to:
+
+ </para>
+
+ <scons_example name="Variables_custom_py_2">
+ <file name="SConstruct">
+ 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))
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="bar.c">
+ bar.c
+ </file>
+ <file name="custom.py" printme="1">
+ RELEASE = 0
+ </file>
+ </scons_example>
+
+ <para>
+
+ The object files are rebuilt appropriately
+ with the new variable:
+
+ </para>
+
+ <scons_output example="Variables_custom_py_2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Pre-Defined Build Variable Functions</title>
+
+ <para>
+
+ &SCons; provides a number of functions
+ that provide ready-made behaviors
+ for various types of command-line build variables.
+
+ </para>
+
+ <section>
+ <title>True/False Values: the &BoolVariable; Build Variable Function</title>
+
+ <para>
+
+ 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;.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="BoolVariable">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ With this build variable,
+ the &RELEASE; variable can now be enabled by
+ setting it to the value <literal>yes</literal>
+ or <literal>t</literal>:
+
+ </para>
+
+ <scons_output example="BoolVariable">
+ <scons_output_command>scons -Q RELEASE=yes foo.o</scons_output_command>
+ </scons_output>
+
+ <scons_output example="BoolVariable">
+ <scons_output_command>scons -Q RELEASE=t foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Other values that equate to &true; include
+ <literal>y</literal>,
+ <literal>1</literal>,
+ <literal>on</literal>
+ and
+ <literal>all</literal>.
+
+ </para>
+
+ <para>
+
+ Conversely, &RELEASE; may now be given a &false;
+ value by setting it to
+ <literal>no</literal>
+ or
+ <literal>f</literal>:
+
+ </para>
+
+ <scons_output example="BoolVariable">
+ <scons_output_command>scons -Q RELEASE=no foo.o</scons_output_command>
+ </scons_output>
+
+ <scons_output example="BoolVariable">
+ <scons_output_command>scons -Q RELEASE=f foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Other values that equate to &false; include
+ <literal>n</literal>,
+ <literal>0</literal>,
+ <literal>off</literal>
+ and
+ <literal>none</literal>.
+
+ </para>
+
+ <para>
+
+ Lastly, if a user tries to specify
+ any other value,
+ &SCons; supplies an appropriate error message:
+
+ </para>
+
+ <scons_output example="BoolVariable">
+ <scons_output_command>scons -Q RELEASE=bad_value foo.o</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Single Value From a List: the &EnumVariable; Build Variable Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="EnumVariable">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ The user can now explicity set the &COLOR; build variable
+ to any of the specified allowed values:
+
+ </para>
+
+ <scons_output example="EnumVariable">
+ <scons_output_command>scons -Q COLOR=red foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=blue foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=green foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But, almost more importantly,
+ an attempt to set &COLOR;
+ to a value that's not in the list
+ generates an error message:
+
+ </para>
+
+ <scons_output example="EnumVariable">
+ <scons_output_command>scons -Q COLOR=magenta foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <literal>navy</literal> as a synonym for
+ <literal>blue</literal>.
+ We do this by adding a &map; dictionary
+ that will map its key values
+ to the desired legal value:
+
+ </para>
+
+ <scons_example name="EnumVariable_map">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ As desired, the user can then use
+ <literal>navy</literal> on the command line,
+ and &SCons; will translate it into <literal>blue</literal>
+ when it comes time to use the &COLOR;
+ variable to build a target:
+
+ </para>
+
+ <scons_output example="EnumVariable_map">
+ <scons_output_command>scons -Q COLOR=navy foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ By default, when using the &EnumVariable; function,
+ arguments that differ
+ from the legal values
+ only in case
+ are treated as illegal values:
+
+ </para>
+
+ <scons_output example="EnumVariable">
+ <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=BLUE foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The &EnumVariable; function can take an additional
+ &ignorecase; keyword argument that,
+ when set to <literal>1</literal>,
+ tells &SCons; to allow case differences
+ when the values are specified:
+
+ </para>
+
+ <scons_example name="EnumVariable_ic1">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which yields the output:
+
+ </para>
+
+ <scons_output example="EnumVariable_ic1">
+ <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=BLUE foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=green foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice that an &ignorecase; value of <literal>1</literal>
+ 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 <literal>2</literal>:
+
+ </para>
+
+ <scons_example name="EnumVariable_ic2">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Now &SCons; will use values of
+ <literal>red</literal>,
+ <literal>green</literal> or
+ <literal>blue</literal>
+ regardless of how the user spells
+ those values on the command line:
+
+ </para>
+
+ <scons_output example="EnumVariable_ic2">
+ <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLOR=GREEN foo.o</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Multiple Values From a List: the &ListVariable; Build Variable Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ListVariable">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ListVariable">
+ <scons_output_command>scons -Q COLORS=red,blue foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLORS=blue,green,red foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ListVariable">
+ <scons_output_command>scons -Q COLORS=all foo.o</scons_output_command>
+ <scons_output_command>scons -Q COLORS=none foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And, of course, an illegal value
+ still generates an error message:
+
+ </para>
+
+ <scons_output example="ListVariable">
+ <scons_output_command>scons -Q COLORS=magenta foo.o</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Path Names: the &PathVariable; Build Variable Function</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <scons_example name="PathVariable">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="__ROOT__/etc/my_config">
+ /opt/location
+ </file>
+ <file name="__ROOT__/usr/local/etc/other_config">
+ /opt/location
+ </file>
+ </scons_example>
+
+ <para>
+
+ This then allows the user to
+ override the &CONFIG; build variable
+ on the command line as necessary:
+
+ </para>
+
+ <scons_output example="PathVariable">
+ <scons_output_command>scons -Q foo.o</scons_output_command>
+ <scons_output_command>scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ By default, &PathVariable; checks to make sure
+ that the specified path exists and generates an error if it
+ doesn't:
+
+ </para>
+
+ <scons_output example="PathVariable">
+ <scons_output_command>scons -Q CONFIG=__ROOT__/does/not/exist foo.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <scons_example name="PathIsFile">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="__ROOT__/etc/my_config">
+ /opt/location
+ </file>
+ </scons_example>
+
+ <para>
+
+ Conversely, to ensure that any specified paths are
+ directories and not files,
+ use the &PathVariable_PathIsDir; method:
+
+ </para>
+
+ <scons_example name="PathIsDir">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="__ROOT__/var/my_dbdir">
+ /opt/location
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="PathIsDirCreate">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="__ROOT__/var/my_dbdir">
+ /opt/location
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="PathAccept">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ </section>
+
+ <section>
+ <title>Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function</title>
+
+ <para>
+
+ 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 <literal>yes</literal> or <literal>no</literal> keywords,
+ in addition to allow them
+ to supply an explicit path name.
+ &SCons; supports the &PackageVariable;
+ function to support this:
+
+ </para>
+
+ <scons_example name="PackageVariable">
+ <file name="SConstruct" printme="1">
+ vars = Variables('custom.py')
+ vars.Add(PackageVariable('PACKAGE',
+ 'Location package',
+ '__ROOT__/opt/location'))
+ env = Environment(variables = vars,
+ CPPDEFINES={'PACKAGE' : '"$PACKAGE"'})
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="__ROOT__/opt/location">
+ /opt/location
+ </file>
+ <file name="__ROOT__/usr/local/location">
+ /opt/location
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="PackageVariable">
+ <scons_output_command>scons -Q foo.o</scons_output_command>
+ <scons_output_command>scons -Q PACKAGE=__ROOT__/usr/local/location foo.o</scons_output_command>
+ <scons_output_command>scons -Q PACKAGE=yes foo.o</scons_output_command>
+ <scons_output_command>scons -Q PACKAGE=no foo.o</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Adding Multiple Command-Line Build Variables at Once</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="AddVariables_1">
+ <file name="SConstruct" printme="1">
+ 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),
+ )
+ </file>
+ </scons_example>
+
+ <para>
+ </para>
+
+ </section>
+
+ <section>
+ <title>Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function</title>
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis> 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:
+
+ </para>
+
+ <scons_example name="UnknownVariables">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ The &UnknownVariables; method returns a dictionary
+ containing the keywords and values
+ of any variables the user specified on the command line
+ that are <emphasis>not</emphasis>
+ 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;:
+
+ </para>
+
+ <scons_output example="UnknownVariables">
+ <scons_output_command>scons -Q NOT_KNOWN=foo</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ Note that you must delay the call of &UnknownVariables;
+ until after you have applied the &Variables; object
+ to a construction environment
+ with the <literal>variables=</literal>
+ keyword argument of an &Environment; call.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section id="sect-command-line-targets">
+ <title>Command-Line Targets</title>
+
+ <section>
+ <title>Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <scons_example name="COMMAND_LINE_TARGETS">
+ <file name="SConstruct" printme="1">
+ if 'bar' in COMMAND_LINE_TARGETS:
+ print "Don't forget to copy `bar' to the archive!"
+ Default(Program('foo.c'))
+ Program('bar.c')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ <file name="bar.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="COMMAND_LINE_TARGETS">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q bar</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling the Default Targets: the &Default; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Default1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Program('goodbye.c')
+ Default(hello)
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ <file name="goodbye.c">
+ goodbye.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ This &SConstruct; file knows how to build two programs,
+ &hello; and &goodbye;,
+ but only builds the
+ &hello; program by default:
+
+ </para>
+
+ <scons_output example="Default1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q goodbye</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that, even when you use the &Default;
+ function in your &SConstruct; file,
+ you can still explicitly specify the current directory
+ (<literal>.</literal>) on the command line
+ to tell &SCons; to build
+ everything in (or below) the current directory:
+
+ </para>
+
+ <scons_output example="Default1">
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Default2">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ prog1 = env.Program('prog1.c')
+ Default(prog1)
+ prog2 = env.Program('prog2.c')
+ prog3 = env.Program('prog3.c')
+ Default(prog3)
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ <file name="prog3.c">
+ prog3.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Or you can specify more than one target
+ in a single call to the &Default; function:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ prog1 = env.Program('prog1.c')
+ prog2 = env.Program('prog2.c')
+ prog3 = env.Program('prog3.c')
+ Default(prog1, prog3)
+ </programlisting>
+
+ <para>
+
+ Either of these last two examples
+ will build only the
+ <application>prog1</application>
+ and
+ <application>prog3</application>
+ programs by default:
+
+ </para>
+
+ <scons_output example="Default2">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can list a directory as
+ an argument to &Default;:
+
+ </para>
+
+ <scons_example name="Default3">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Program(['prog1/main.c', 'prog1/foo.c'])
+ env.Program(['prog2/main.c', 'prog2/bar.c'])
+ Default('prog1')
+ </file>
+ <directory name="prog1"></directory>
+ <directory name="prog2"></directory>
+ <file name="prog1/main.c">
+ int main() { printf("prog1/main.c\n"); }
+ </file>
+ <file name="prog1/foo.c">
+ int foo() { printf("prog1/foo.c\n"); }
+ </file>
+ <file name="prog2/main.c">
+ int main() { printf("prog2/main.c\n"); }
+ </file>
+ <file name="prog2/bar.c">
+ int bar() { printf("prog2/bar.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ In which case only the target(s) in that
+ directory will be built by default:
+
+ </para>
+
+ <scons_output example="Default3">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Lastly, if for some reason you don't want
+ any targets built by default,
+ you can use the Python <literal>None</literal>
+ variable:
+
+ </para>
+
+ <scons_example name="Default4">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ prog1 = env.Program('prog1.c')
+ prog2 = env.Program('prog2.c')
+ Default(None)
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which would produce build output like:
+
+ </para>
+
+ <scons_output example="Default4">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q .</scons_output_command>
+ </scons_output>
+
+ <section>
+ <title>Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable</title>
+
+ <para>
+
+ &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 <function>map</function> function
+ to run the list through <function>str</function>:
+
+ </para>
+
+ <scons_example name="DEFAULT_TARGETS_1">
+ <file name="SConstruct" printme="1">
+ prog1 = Program('prog1.c')
+ Default(prog1)
+ print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS)
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ (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 <literal>-Q</literal> flag when we run &SCons;:)
+
+ </para>
+
+ <scons_output example="DEFAULT_TARGETS_1">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="DEFAULT_TARGETS_2">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which yields the output:
+
+ </para>
+
+ <scons_output example="DEFAULT_TARGETS_2">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ if COMMAND_LINE_TARGETS:
+ targets = COMMAND_LINE_TARGETS
+ else:
+ targets = DEFAULT_TARGETS
+ </sconstruct>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="BUILD_TARGETS_1">
+ <file name="SConstruct" printme="1">
+ prog1 = Program('prog1.c')
+ Program('prog2.c')
+ Default(prog1)
+ print "BUILD_TARGETS is", map(str, BUILD_TARGETS)
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Notice how the value of &BUILD_TARGETS;
+ changes depending on whether a target is
+ specified on the command line:
+
+ </para>
+
+ <scons_output example="BUILD_TARGETS_1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q prog2</scons_output_command>
+ <scons_output_command>scons -Q -c .</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Options</term>
+
+ <listitem>
+ <para>
+
+ Command-line options always begin with
+ one or two <literal>-</literal> (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 <xref linkend="sect-command-line-options"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Variables</term>
+
+ <listitem>
+ <para>
+
+ Any command-line argument containing an <literal>=</literal>
+ (equal sign) is considered a variable setting with the form
+ <varname>variable</varname>=<varname>value</varname>
+ &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 <xref linkend="sect-command-line-variables"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Targets</term>
+
+ <listitem>
+ <para>
+
+ 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 <xref linkend="sect-command-line-targets"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <section id="sect-command-line-options">
+ <title>Command-Line Options</title>
+
+ <para>
+
+ &SCons; has many <emphasis>command-line options</emphasis>
+ that control its behavior.
+ A &SCons; <emphasis>command-line option</emphasis>
+ always begins with one or two <literal>-</literal> (hyphen)
+ characters.
+
+ </para>
+
+ <section>
+ <title>Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable</title>
+
+ <para>
+
+ 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 <literal>-j 2</literal>
+ to have &SCons; run up to two build commands in parallel.
+ To avoid having to type <literal>-j 2</literal> 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.
+
+ </para>
+
+ <para>
+
+ If, for example,
+ you're using a POSIX shell that's
+ compatible with the Bourne shell,
+ and you always want &SCons; to use the
+ <literal>-Q</literal> option,
+ you can set the &SCONSFLAGS;
+ environment as follows:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript files ...
+ scons: done reading SConscript files.
+ scons: Building targets ...
+ ... [build output] ...
+ scons: done building targets.
+ % <userinput>export SCONSFLAGS="-Q"</userinput>
+ % <userinput>scons</userinput>
+ ... [build output] ...
+ </screen>
+
+ <para>
+
+ Users of &csh;-style shells on POSIX systems
+ can set the &SCONSFLAGS; environment as follows:
+
+ </para>
+
+ <screen>
+ $ <userinput>setenv SCONSFLAGS "-Q"</userinput>
+ </screen>
+
+ <para>
+
+ Windows users may typically want to set the
+ &SCONSFLAGS; in the appropriate tab of the
+ <literal>System Properties</literal> window.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Getting Values Set by Command-Line Options: the &GetOption; Function</title>
+
+ <para>
+
+ &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 <literal>-h</literal> or <literal>--help</literal> 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.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis> specified
+ the <literal>-h</literal> or <literal>--help</literal> option,
+ like so:
+
+ </para>
+
+ <programlisting></programlisting>
+
+ <para>
+
+ 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
+ <xref linkend="sect-command-line-option-strings"></xref> section,
+ below.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Setting Values of Command-Line Options: the &SetOption; Function</title>
+
+ <para>
+
+ 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
+ <xref linkend="sect-command-line-option-strings"></xref> section,
+ below.
+
+ </para>
+
+ <para>
+
+ One use of the &SetOption; function is to
+ specify a value for the <literal>-j</literal>
+ or <literal>--jobs</literal> 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 <literal>-j</literal> option is
+ somewhat system-dependent.
+ One rough guideline is that the more processors
+ your system has,
+ the higher you want to set the
+ <literal>-j</literal> value,
+ in order to take advantage of the number of CPUs.
+
+ </para>
+
+ <para>
+
+ For example, suppose the administrators
+ of your development systems
+ have standardized on setting a
+ <varname>NUM_CPU</varname> 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:
+
+ </para>
+
+ <programlisting>
+ import os
+ num_cpu = int(os.environ.get('NUM_CPU', 2))
+ SetOption('num_jobs', num_cpu)
+ print "running with -j", GetOption('num_jobs')
+ </programlisting>
+
+ <para>
+
+ The above snippet of code
+ sets the value of the <literal>--jobs</literal> option
+ to the value specified in the
+ <varname>$NUM_CPU</varname> 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 <literal>--jobs</literal>
+ value is <literal>num_jobs</literal>
+ for historical reasons.)
+ The code in this example prints the <literal>num_jobs</literal>
+ value for illustrative purposes.
+ It uses a default value of <literal>2</literal>
+ to provide some minimal parallelism even on
+ single-processor systems:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ running with -j 2
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ But if the <varname>$NUM_CPU</varname>
+ environment variable is set,
+ then we use that for the default number of jobs:
+
+ </para>
+
+ <screen>
+ % <userinput>export NUM_CPU="4"</userinput>
+ % <userinput>scons -Q</userinput>
+ running with -j 4
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ But any explicit
+ <literal>-j</literal> or <literal>--jobs</literal>
+ value the user specifies an the command line is used first,
+ regardless of whether or not
+ the <varname>$NUM_CPU</varname> environment
+ variable is set:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -j 7</userinput>
+ running with -j 7
+ scons: `.' is up to date.
+ % <userinput>export NUM_CPU="4"</userinput>
+ % <userinput>scons -Q -j 3</userinput>
+ running with -j 3
+ scons: `.' is up to date.
+ </screen>
+
+ </section>
+
+ <section id="sect-command-line-option-strings">
+ <title>Strings for Getting or Setting Values of &SCons; Command-Line Options</title>
+
+ <para>
+
+ 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: <literal>--</literal>),
+ after replacing any remaining hyphen characters
+ with underscores.
+
+ </para>
+
+ <para>
+
+ The full list of strings and the variables they
+ correspond to is as follows:
+
+ </para>
+
+ <informaltable>
+ <tgroup cols="2" align="left">
+
+ <thead>
+
+ <row>
+ <entry>String for &GetOption; and &SetOption;</entry>
+ <entry>Command-Line Option(s)</entry>
+ </row>
+
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry><literal>cache_debug</literal></entry>
+ <entry><option>--cache-debug</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>cache_disable</literal></entry>
+ <entry><option>--cache-disable</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>cache_force</literal></entry>
+ <entry><option>--cache-force</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>cache_show</literal></entry>
+ <entry><option>--cache-show</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>clean</literal></entry>
+ <entry><option>-c</option>,
+ <option>--clean</option>,
+ <option>--remove</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>config</literal></entry>
+ <entry><option>--config</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>directory</literal></entry>
+ <entry><option>-C</option>,
+ <option>--directory</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>diskcheck</literal></entry>
+ <entry><option>--diskcheck</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>duplicate</literal></entry>
+ <entry><option>--duplicate</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>file</literal></entry>
+ <entry><option>-f</option>,
+ <option>--file</option>,
+ <option>--makefile </option>,
+ <option>--sconstruct</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>help</literal></entry>
+ <entry><option>-h</option>,
+ <option>--help</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>ignore_errors</literal></entry>
+ <entry><option>--ignore-errors</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>implicit_cache</literal></entry>
+ <entry><option>--implicit-cache</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>implicit_deps_changed</literal></entry>
+ <entry><option>--implicit-deps-changed</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>implicit_deps_unchanged</literal></entry>
+ <entry><option>--implicit-deps-unchanged</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>interactive</literal></entry>
+ <entry><option>--interact</option>,
+ <option>--interactive</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>keep_going</literal></entry>
+ <entry><option>-k</option>,
+ <option>--keep-going</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>max_drift</literal></entry>
+ <entry><option>--max-drift</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>no_exec</literal></entry>
+ <entry><option>-n</option>,
+ <option>--no-exec</option>,
+ <option>--just-print</option>,
+ <option>--dry-run</option>,
+ <option>--recon</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>no_site_dir</literal></entry>
+ <entry><option>--no-site-dir</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>num_jobs</literal></entry>
+ <entry><option>-j</option>,
+ <option>--jobs</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>profile_file</literal></entry>
+ <entry><option>--profile</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>question</literal></entry>
+ <entry><option>-q</option>,
+ <option>--question</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>random</literal></entry>
+ <entry><option>--random</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>repository</literal></entry>
+ <entry><option>-Y</option>,
+ <option>--repository</option>,
+ <option>--srcdir</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>silent</literal></entry>
+ <entry><option>-s</option>,
+ <option>--silent</option>,
+ <option>--quiet</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>site_dir</literal></entry>
+ <entry><option>--site-dir</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>stack_size</literal></entry>
+ <entry><option>--stack-size</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>taskmastertrace_file</literal></entry>
+ <entry><option>--taskmastertrace</option></entry>
+ </row>
+
+ <row>
+ <entry><literal>warn</literal></entry>
+ <entry><option>--warn</option> <option>--warning</option></entry>
+ </row>
+
+ </tbody>
+
+ </tgroup>
+ </informaltable>
+
+ </section>
+
+ <section>
+ <title>Adding Custom Command-Line Options: the &AddOption; Function</title>
+
+ <para>
+
+ &SCons; also allows you to define your own
+ command-line options with the &AddOption; function.
+ The &AddOption; function takes the same arguments
+ as the <function>optparse.add_option</function> function
+ from the standard Python library.
+ <footnote>
+ <para>
+ The &AddOption; function is,
+ in fact, implemented using a subclass
+ of the <classname>optparse.OptionParser</classname>.
+ </para>
+ </footnote>
+ 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.)
+
+ </para>
+
+ <para>
+
+ One useful example of using this functionality
+ is to provide a <option>--prefix</option> for users:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ The above code uses the &GetOption; function
+ to set the <varname>$PREFIX</varname>
+ construction variable to any
+ value that the user specifies with a command-line
+ option of <literal>--prefix</literal>.
+ Because <varname>$PREFIX</varname>
+ will expand to a null string if it's not initialized,
+ running &SCons; without the
+ option of <literal>--prefix</literal>
+ will install the file in the
+ <filename>/usr/bin/</filename> directory:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -n</userinput>
+ Install file: "foo.in" as "/usr/bin/foo.in"
+ </screen>
+
+ <para>
+
+ But specifying <literal>--prefix=/tmp/install</literal>
+ on the command line causes the file to be installed in the
+ <filename>/tmp/install/usr/bin/</filename> directory:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -n --prefix=/tmp/install</userinput>
+ Install file: "foo.in" as "/tmp/install/usr/bin/foo.in"
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section id="sect-command-line-variables">
+ <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables</title>
+
+ <para>
+
+ You may want to control various aspects
+ of your build by allowing the user
+ to specify <varname>variable</varname>=<varname>value</varname>
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q debug=1</userinput>
+ </screen>
+
+ <para>
+
+ &SCons; provides an &ARGUMENTS; dictionary
+ that stores all of the
+ <varname>variable</varname>=<varname>value</varname>
+ 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 <emphasis>always</emphasis>
+ specify a variable,
+ you probably want to use
+ the Python
+ <literal>ARGUMENTS.get()</literal> function,
+ which allows you to specify a default value
+ to be used if there is no specification
+ on the command line.)
+
+ </para>
+
+ <para>
+
+ The following code sets the &cv-link-CCFLAGS; construction
+ variable in response to the <varname>debug</varname>
+ flag being set in the &ARGUMENTS; dictionary:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ debug = ARGUMENTS.get('debug', 0)
+ if int(debug):
+ env.Append(CCFLAGS = '-g')
+ env.Program('prog.c')
+ </programlisting>
+
+ <para>
+
+ This results in the <varname>-g</varname>
+ compiler option being used when
+ <literal>debug=1</literal>
+ is used on the command line:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q debug=0</userinput>
+ cc -o prog.o -c prog.c
+ cc -o prog prog.o
+ % <userinput>scons -Q debug=0</userinput>
+ scons: `.' is up to date.
+ % <userinput>scons -Q debug=1</userinput>
+ scons: `.' is up to date.
+ % <userinput>scons -Q debug=1</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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 <literal>debug</literal>
+ argument has changed.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ To accomodate these requirements,
+ &SCons; provides an &ARGLIST; variable
+ that gives you direct access to
+ <varname>variable</varname>=<varname>value</varname>
+ 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
+ <varname>define=</varname>
+ settings on the command line:
+
+ </para>
+
+ <programlisting>
+ cppdefines = []
+ for key, value in ARGLIST:
+ if key == 'define':
+ cppdefines.append(value)
+ env = Environment(CPPDEFINES = cppdefines)
+ env.Object('prog.c')
+ </programlisting>
+
+ <para>
+
+ Yields the following output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q define=FOO</userinput>
+ cc -o prog.o -c -DFOO prog.c
+ % <userinput>scons -Q define=FOO define=BAR</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Note that the &ARGLIST; and &ARGUMENTS;
+ variables do not interfere with each other,
+ but merely provide slightly different views
+ into how the user specified
+ <varname>variable</varname>=<varname>value</varname>
+ 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).
+
+ </para>
+
+ <section>
+ <title>Controlling Command-Line Build Variables</title>
+
+ <para>
+
+ Being able to use a command-line build variable like
+ <literal>debug=1</literal> 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.
+
+ </para>
+
+ <para>
+
+ 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 <literal>-D</literal> 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:
+
+ </para>
+
+ <programlisting>
+ 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'])
+ </programlisting>
+
+ <para>
+
+ This &SConstruct; file first creates a &Variables; object
+ (the <literal>vars = Variables()</literal> 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 <literal>0</literal>
+ (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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q RELEASE=1</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Providing Help for Command-Line Build Variables</title>
+
+ <para>
+
+ 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 <literal>scons -h</literal>.
+ 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:
+
+ </para>
+
+ <programlisting>
+ vars = Variables('custom.py')
+ vars.Add('RELEASE', 'Set to 1 to build for release', 0)
+ env = Environment(variables = vars)
+ Help(vars.GenerateHelpText(env))
+ </programlisting>
+
+ <para>
+
+ &SCons; will now display some useful text
+ when the <literal>-h</literal> option is used:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -h</userinput>
+
+ RELEASE: Set to 1 to build for release
+ default: 0
+ actual: 0
+
+ Use scons -H for help about command-line options.
+ </screen>
+
+ <para>
+
+ Notice that the help output shows the default value,
+ and the current actual value of the build variable.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Reading Build Variables From a File</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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))
+ </programlisting>
+
+ <para>
+
+ This then allows the user to control the &RELEASE;
+ variable by setting it in the &custom_py; file:
+
+ </para>
+
+ <programlisting>
+ RELEASE = 1
+ </programlisting>
+
+ <para>
+
+ Note that this file is actually executed
+ like a Python script.
+ Now when we run &SCons;:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ And if we change the contents of &custom_py; to:
+
+ </para>
+
+ <programlisting>
+ RELEASE = 0
+ </programlisting>
+
+ <para>
+
+ The object files are rebuilt appropriately
+ with the new variable:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Pre-Defined Build Variable Functions</title>
+
+ <para>
+
+ &SCons; provides a number of functions
+ that provide ready-made behaviors
+ for various types of command-line build variables.
+
+ </para>
+
+ <section>
+ <title>True/False Values: the &BoolVariable; Build Variable Function</title>
+
+ <para>
+
+ 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;.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ With this build variable,
+ the &RELEASE; variable can now be enabled by
+ setting it to the value <literal>yes</literal>
+ or <literal>t</literal>:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q RELEASE=yes foo.o</userinput>
+ cc -o foo.o -c -DRELEASE_BUILD=True foo.c
+ </screen>
+
+ <screen>
+ % <userinput>scons -Q RELEASE=t foo.o</userinput>
+ cc -o foo.o -c -DRELEASE_BUILD=True foo.c
+ </screen>
+
+ <para>
+
+ Other values that equate to &true; include
+ <literal>y</literal>,
+ <literal>1</literal>,
+ <literal>on</literal>
+ and
+ <literal>all</literal>.
+
+ </para>
+
+ <para>
+
+ Conversely, &RELEASE; may now be given a &false;
+ value by setting it to
+ <literal>no</literal>
+ or
+ <literal>f</literal>:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q RELEASE=no foo.o</userinput>
+ cc -o foo.o -c -DRELEASE_BUILD=False foo.c
+ </screen>
+
+ <screen>
+ % <userinput>scons -Q RELEASE=f foo.o</userinput>
+ cc -o foo.o -c -DRELEASE_BUILD=False foo.c
+ </screen>
+
+ <para>
+
+ Other values that equate to &false; include
+ <literal>n</literal>,
+ <literal>0</literal>,
+ <literal>off</literal>
+ and
+ <literal>none</literal>.
+
+ </para>
+
+ <para>
+
+ Lastly, if a user tries to specify
+ any other value,
+ &SCons; supplies an appropriate error message:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q RELEASE=bad_value foo.o</userinput>
+
+ scons: *** Error converting option: RELEASE
+ Invalid value for boolean option: bad_value
+ File "/home/my/project/SConstruct", line 4, in ?
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Single Value From a List: the &EnumVariable; Build Variable Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ The user can now explicity set the &COLOR; build variable
+ to any of the specified allowed values:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLOR=red foo.o</userinput>
+ cc -o foo.o -c -DCOLOR="red" foo.c
+ % <userinput>scons -Q COLOR=blue foo.o</userinput>
+ scons: `foo.o' is up to date.
+ % <userinput>scons -Q COLOR=green foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ <para>
+
+ But, almost more importantly,
+ an attempt to set &COLOR;
+ to a value that's not in the list
+ generates an error message:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLOR=magenta foo.o</userinput>
+
+ scons: *** Invalid value for option COLOR: magenta
+ File "/home/my/project/SConstruct", line 5, in ?
+ </screen>
+
+ <para>
+
+ 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 <literal>navy</literal> as a synonym for
+ <literal>blue</literal>.
+ We do this by adding a &map; dictionary
+ that will map its key values
+ to the desired legal value:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ As desired, the user can then use
+ <literal>navy</literal> on the command line,
+ and &SCons; will translate it into <literal>blue</literal>
+ when it comes time to use the &COLOR;
+ variable to build a target:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLOR=navy foo.o</userinput>
+ cc -o foo.o -c -DCOLOR="blue" foo.c
+ </screen>
+
+ <para>
+
+ By default, when using the &EnumVariable; function,
+ arguments that differ
+ from the legal values
+ only in case
+ are treated as illegal values:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLOR=Red foo.o</userinput>
+
+ scons: *** Invalid value for option COLOR: Red
+ File "/home/my/project/SConstruct", line 5, in ?
+ % <userinput>scons -Q COLOR=BLUE foo.o</userinput>
+
+ scons: *** Invalid value for option COLOR: BLUE
+ File "/home/my/project/SConstruct", line 5, in ?
+ % <userinput>scons -Q COLOR=nAvY foo.o</userinput>
+
+ scons: *** Invalid value for option COLOR: nAvY
+ File "/home/my/project/SConstruct", line 5, in ?
+ </screen>
+
+ <para>
+
+ The &EnumVariable; function can take an additional
+ &ignorecase; keyword argument that,
+ when set to <literal>1</literal>,
+ tells &SCons; to allow case differences
+ when the values are specified:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ Which yields the output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLOR=Red foo.o</userinput>
+ cc -o foo.o -c -DCOLOR="Red" foo.c
+ % <userinput>scons -Q COLOR=BLUE foo.o</userinput>
+ scons: `foo.o' is up to date.
+ % <userinput>scons -Q COLOR=nAvY foo.o</userinput>
+ scons: `foo.o' is up to date.
+ % <userinput>scons -Q COLOR=green foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ <para>
+
+ Notice that an &ignorecase; value of <literal>1</literal>
+ 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 <literal>2</literal>:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ Now &SCons; will use values of
+ <literal>red</literal>,
+ <literal>green</literal> or
+ <literal>blue</literal>
+ regardless of how the user spells
+ those values on the command line:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLOR=Red foo.o</userinput>
+ cc -o foo.o -c -DCOLOR="red" foo.c
+ % <userinput>scons -Q COLOR=nAvY foo.o</userinput>
+ scons: `foo.o' is up to date.
+ % <userinput>scons -Q COLOR=GREEN foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Multiple Values From a List: the &ListVariable; Build Variable Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLORS=red,blue foo.o</userinput>
+ cc -o foo.o -c -DCOLORS="red blue" foo.c
+ % <userinput>scons -Q COLORS=blue,green,red foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLORS=all foo.o</userinput>
+ cc -o foo.o -c -DCOLORS="red green blue" foo.c
+ % <userinput>scons -Q COLORS=none foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ <para>
+
+ And, of course, an illegal value
+ still generates an error message:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q COLORS=magenta foo.o</userinput>
+
+ scons: *** Error converting option: COLORS
+ Invalid value(s) for option: magenta
+ File "/home/my/project/SConstruct", line 5, in ?
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Path Names: the &PathVariable; Build Variable Function</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ This then allows the user to
+ override the &CONFIG; build variable
+ on the command line as necessary:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q foo.o</userinput>
+ cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c
+ % <userinput>scons -Q CONFIG=/usr/local/etc/other_config foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ <para>
+
+ By default, &PathVariable; checks to make sure
+ that the specified path exists and generates an error if it
+ doesn't:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q CONFIG=/does/not/exist foo.o</userinput>
+
+ scons: *** Path for option CONFIG does not exist: /does/not/exist
+ File "/home/my/project/SConstruct", line 6, in ?
+ </screen>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ Conversely, to ensure that any specified paths are
+ directories and not files,
+ use the &PathVariable_PathIsDir; method:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function</title>
+
+ <para>
+
+ 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 <literal>yes</literal> or <literal>no</literal> keywords,
+ in addition to allow them
+ to supply an explicit path name.
+ &SCons; supports the &PackageVariable;
+ function to support this:
+
+ </para>
+
+ <programlisting>
+ vars = Variables('custom.py')
+ vars.Add(PackageVariable('PACKAGE',
+ 'Location package',
+ '/opt/location'))
+ env = Environment(variables = vars,
+ CPPDEFINES={'PACKAGE' : '"$PACKAGE"'})
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q foo.o</userinput>
+ cc -o foo.o -c -DPACKAGE="/opt/location" foo.c
+ % <userinput>scons -Q PACKAGE=/usr/local/location foo.o</userinput>
+ scons: `foo.o' is up to date.
+ % <userinput>scons -Q PACKAGE=yes foo.o</userinput>
+ scons: `foo.o' is up to date.
+ % <userinput>scons -Q PACKAGE=no foo.o</userinput>
+ scons: `foo.o' is up to date.
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Adding Multiple Command-Line Build Variables at Once</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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),
+ )
+ </programlisting>
+
+ <para>
+ </para>
+
+ </section>
+
+ <section>
+ <title>Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function</title>
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis> 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ The &UnknownVariables; method returns a dictionary
+ containing the keywords and values
+ of any variables the user specified on the command line
+ that are <emphasis>not</emphasis>
+ 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;:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q NOT_KNOWN=foo</userinput>
+ Unknown variables: ['NOT_KNOWN']
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ Note that you must delay the call of &UnknownVariables;
+ until after you have applied the &Variables; object
+ to a construction environment
+ with the <literal>variables=</literal>
+ keyword argument of an &Environment; call.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section id="sect-command-line-targets">
+ <title>Command-Line Targets</title>
+
+ <section>
+ <title>Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ if 'bar' in COMMAND_LINE_TARGETS:
+ print "Don't forget to copy `bar' to the archive!"
+ Default(Program('foo.c'))
+ Program('bar.c')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o foo.o -c foo.c
+ cc -o foo foo.o
+ % <userinput>scons -Q bar</userinput>
+ Don't forget to copy `bar' to the archive!
+ cc -o bar.o -c bar.c
+ cc -o bar bar.o
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling the Default Targets: the &Default; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Program('goodbye.c')
+ Default(hello)
+ </programlisting>
+
+ <para>
+
+ This &SConstruct; file knows how to build two programs,
+ &hello; and &goodbye;,
+ but only builds the
+ &hello; program by default:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q</userinput>
+ scons: `hello' is up to date.
+ % <userinput>scons -Q goodbye</userinput>
+ cc -o goodbye.o -c goodbye.c
+ cc -o goodbye goodbye.o
+ </screen>
+
+ <para>
+
+ Note that, even when you use the &Default;
+ function in your &SConstruct; file,
+ you can still explicitly specify the current directory
+ (<literal>.</literal>) on the command line
+ to tell &SCons; to build
+ everything in (or below) the current directory:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q .</userinput>
+ cc -o goodbye.o -c goodbye.c
+ cc -o goodbye goodbye.o
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ prog1 = env.Program('prog1.c')
+ Default(prog1)
+ prog2 = env.Program('prog2.c')
+ prog3 = env.Program('prog3.c')
+ Default(prog3)
+ </programlisting>
+
+ <para>
+
+ Or you can specify more than one target
+ in a single call to the &Default; function:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ prog1 = env.Program('prog1.c')
+ prog2 = env.Program('prog2.c')
+ prog3 = env.Program('prog3.c')
+ Default(prog1, prog3)
+ </programlisting>
+
+ <para>
+
+ Either of these last two examples
+ will build only the
+ <application>prog1</application>
+ and
+ <application>prog3</application>
+ programs by default:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o prog1.o -c prog1.c
+ cc -o prog1 prog1.o
+ cc -o prog3.o -c prog3.c
+ cc -o prog3 prog3.o
+ % <userinput>scons -Q .</userinput>
+ cc -o prog2.o -c prog2.c
+ cc -o prog2 prog2.o
+ </screen>
+
+ <para>
+
+ You can list a directory as
+ an argument to &Default;:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Program(['prog1/main.c', 'prog1/foo.c'])
+ env.Program(['prog2/main.c', 'prog2/bar.c'])
+ Default('prog1')
+ </programlisting>
+
+ <para>
+
+ In which case only the target(s) in that
+ directory will be built by default:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ % <userinput>scons -Q</userinput>
+ scons: `prog1' is up to date.
+ % <userinput>scons -Q .</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Lastly, if for some reason you don't want
+ any targets built by default,
+ you can use the Python <literal>None</literal>
+ variable:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ prog1 = env.Program('prog1.c')
+ prog2 = env.Program('prog2.c')
+ Default(None)
+ </programlisting>
+
+ <para>
+
+ Which would produce build output like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ scons: *** No targets specified and no Default() targets found. Stop.
+ % <userinput>scons -Q .</userinput>
+ cc -o prog1.o -c prog1.c
+ cc -o prog1 prog1.o
+ cc -o prog2.o -c prog2.c
+ cc -o prog2 prog2.o
+ </screen>
+
+ <section>
+ <title>Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable</title>
+
+ <para>
+
+ &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 <function>map</function> function
+ to run the list through <function>str</function>:
+
+ </para>
+
+ <programlisting>
+ prog1 = Program('prog1.c')
+ Default(prog1)
+ print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS)
+ </programlisting>
+
+ <para>
+
+ (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 <literal>-Q</literal> flag when we run &SCons;:)
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ Which yields the output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ if COMMAND_LINE_TARGETS:
+ targets = COMMAND_LINE_TARGETS
+ else:
+ targets = DEFAULT_TARGETS
+ </programlisting>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ prog1 = Program('prog1.c')
+ Program('prog2.c')
+ Default(prog1)
+ print "BUILD_TARGETS is", map(str, BUILD_TARGETS)
+ </programlisting>
+
+ <para>
+
+ Notice how the value of &BUILD_TARGETS;
+ changes depending on whether a target is
+ specified on the command line:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ BUILD_TARGETS is ['prog1']
+ cc -o prog1.o -c prog1.c
+ cc -o prog1 prog1.o
+ % <userinput>scons -Q prog2</userinput>
+ BUILD_TARGETS is ['prog2']
+ cc -o prog2.o -c prog2.c
+ cc -o prog2 prog2.o
+ % <userinput>scons -Q -c .</userinput>
+ BUILD_TARGETS is ['.']
+ Removed prog1.o
+ Removed prog1
+ Removed prog2.o
+ Removed prog2
+ </screen>
+
+ </section>
+
+ </section>
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<Cons> 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<Construction
+environments> 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<signatures> 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<sequencing> 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<export>, and, in a C environment, provided
+conventional sub-directories of this directory, such as F<include>, F<lib>,
+F<bin>, etc.
+
+These directories are defined by the top-level F<Construct> file. A simple
+F<Construct> file for a B<Hello, World!> 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<world> directory's F<Conscript> 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<hello> directory's F<Conscript> 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<Hello, World!> program with this directory structure, go to
+the top-level directory, and invoke C<cons> with the appropriate
+arguments. In the following example, we tell Cons to build the directory
+F<export>. 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<Conscript> 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<Construct> file (in this example), to specify the new locations
+of the F<Conscript> 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<export>
+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<signatures> 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<MD5> (B<Message Digest 5>) 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<content signature> 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<build signature> 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<Depends> 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<world.o> file will include:
+
+=over 4
+
+the signature of the F<world.c> file
+
+the signatures of any header files that Cons detects are included,
+directly or indirectly, by F<world.c>
+
+the text of the actual command line was used to generate F<world.o>
+
+=back
+
+Similarly, the build signature of the F<libworld.a> file will include
+all the signatures of its constituents (and hence, transitively, the
+signatures of B<their> 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<Construct> or F<Conscript> 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<Construct> or F<Conscript> 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<build/peach/world> 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<SourceSignature> 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<SourceSignature> 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<foo/*.c>
+would match all C source files in any subdirectory underneath the C<foo>
+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<content> 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<SourceSignature> arguments:
+
+ SourceSignature '/usr/repository/objects/*' => 'stored-content',
+ '/usr/repository/*' => 'content',
+ '*.y' => 'content',
+ '*' => 'stored-content';
+
+In this example, all source files under the F</usr/repository/objects>
+directory will use F<.consign> file content signatures, source files
+anywhere else underneath F</usr/repository> 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<SIGNATURE> 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<SIGNATURE> construction variable is a Perl array
+reference that holds one or more pairs of strings, like the arguments to
+the C<SourceSignature> 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<SourceSignature> 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<build> 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<SIGNATURE> 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<content> or C<stored-content> 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<build> 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<SIGNATURE> array
+reference:
+
+ $env = new cons(SIGNATURE => ['foo/*.o' => 'build',
+ '*.o' => 'content',
+ '*.a' => 'stored-content',
+ '*' => 'content']);
+
+In this example, all object files underneath the F<foo> 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<md5::debug>
+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<Override> commands, and you specify this on the command line, when you run
+C<cons>:
+
+ % cons -o over export
+
+will build the F<export> directory, with all derived files subject to the
+overrides present in the F<over> 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<Construct>
+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<Override> command,
+which looks like this:
+
+ Override <regexp>, <var1> => <value1>, <var2> => <value2>, ...;
+
+The regular expression I<regexp> 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<over> containing this command:
+
+ Override '\.o$', COPT => '-O', CDBG => '';
+
+then any C<cons> 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<Override> 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<Construct> and
+F<Conscript> 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<Module> method
+
+The C<Module> method is a combination of the C<Program> and C<Command>
+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 <module name>, <source or object files>, <construction command>;
+
+This command is useful in instances where you wish to create, for example,
+dynamically loaded modules, or statically linked code libraries.
+
+
+
+
+=head2 The C<RuleSet> method
+
+The C<RuleSet> 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<DefaultRules> method
+
+The C<DefaultRules> method sets the default construction variables that
+will be returned by the C<new> 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<RuleSet> 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<DefaultRules> 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<RuleSet>, 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<SourcePath> method
+
+The C<SourcePath> 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 <buildpath>;
+
+
+=head2 The C<ConsPath> method
+
+The C<ConsPath> method returns true if the supplied path is a derivable
+file, and returns undef (false) otherwise.
+It is invoked as follows:
+
+ $result = ConsPath <path>;
+
+
+=head2 The C<SplitPath> method
+
+The C<SplitPath> 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 <pathlist>;
+
+The C<SplitPath> 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<DirPath> method
+
+The C<DirPath> method returns the build path name(s) of a directory or
+list of directories. It is invoked as follows:
+
+ $cwd = DirPath <paths>;
+
+The most common use for the C<DirPath> method is:
+
+ $cwd = DirPath '.';
+
+to fetch the path to the current directory of a subsidiary F<Conscript>
+file.
+
+
+=head2 The C<FilePath> method
+
+The C<FilePath> method returns the build path name(s) of a file or
+list of files. It is invoked as follows:
+
+ $file = FilePath <path>;
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<blockquote>
+ <para>
+
+ SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
+
+ </para>
+</blockquote>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<blockquote>
+ <para>
+
+ SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
+
+ </para>
+</blockquote>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ So far we've seen how &SCons; handles one-time builds.
+ But one of the main functions of a build tool like &SCons;
+ is to rebuild only what is necessary
+ when source files change--or, put another way,
+ &SCons; should <emphasis>not</emphasis>
+ waste time rebuilding things that don't need to be rebuilt.
+ You can see this at work simply by re-invoking &SCons;
+ after building our simple &hello; example:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct">
+ Program('hello.c')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The second time it is executed,
+ &SCons; realizes that the &hello; program
+ is up-to-date with respect to the current &hello_c; source file,
+ and avoids rebuilding it.
+ You can see this more clearly by naming
+ the &hello; program explicitly on the command line:
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that &SCons; reports <literal>"...is up to date"</literal>
+ only for target files named explicitly on the command line,
+ to avoid cluttering the output.
+
+ </para>
+
+ <section>
+ <title>Deciding When an Input File Has Changed: the &Decider; Function</title>
+
+ <para>
+
+ Another aspect of avoiding unnecessary rebuilds
+ is the fundamental build tool behavior
+ of <emphasis>rebuilding</emphasis>
+ things when an input file changes,
+ so that the built software is up to date.
+ By default,
+ &SCons; keeps track of this through an
+ MD5 &signature;, or checksum, of the contents of each file,
+ although you can easily configure
+ &SCons; to use the
+ modification times (or time stamps)
+ instead.
+ You can even specify your own Python function
+ for deciding if an input file has changed.
+
+ </para>
+
+ <section>
+ <title>Using MD5 Signatures to Decide if a File Has Changed</title>
+
+ <para>
+
+ By default,
+ &SCons; keeps track of whether a file has changed
+ based on an MD5 checksum of the file's contents,
+ not the file's modification time.
+ This means that you may be surprised by the
+ default &SCons; behavior if you are used to the
+ &Make; convention of forcing
+ a rebuild by updating the file's modification time
+ (using the &touch; command, for example):
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>touch hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Even though the file's modification time has changed,
+ &SCons; realizes that the contents of the
+ &hello_c; file have <emphasis>not</emphasis> changed,
+ and therefore that the &hello; program
+ need not be rebuilt.
+ This avoids unnecessary rebuilds when,
+ for example, someone rewrites the
+ contents of a file without making a change.
+ But if the contents of the file really do change,
+ then &SCons; detects the change
+ and rebuilds the program as required:
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that you can, if you wish,
+ specify this default behavior
+ (MD5 signatures) explicitly
+ using the &Decider; function as follows:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ Decider('MD5')
+ </sconstruct>
+
+ <para>
+
+ You can also use the string <literal>'content'</literal>
+ as a synonym for <literal>'MD5'</literal>
+ when calling the &Decider; function.
+
+ </para>
+
+ <section>
+ <title>Ramifications of Using MD5 Signatures</title>
+
+ <para>
+
+ Using MD5 signatures to decide if an input file has changed
+ has one surprising benefit:
+ if a source file has been changed
+ in such a way that the contents of the
+ rebuilt target file(s)
+ will be exactly the same as the last time
+ the file was built,
+ then any "downstream" target files
+ that depend on the rebuilt-but-not-changed target
+ file actually need not be rebuilt.
+
+ </para>
+
+ <para>
+
+ So if, for example,
+ a user were to only change a comment in a &hello_c; file,
+ then the rebuilt &hello_o; file
+ would be exactly the same as the one previously built
+ (assuming the compiler doesn't put any build-specific
+ information in the object file).
+ &SCons; would then realize that it would not
+ need to rebuild the &hello; program as follows:
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE A COMMENT IN hello.c]" edit="STRIP CCCOM line">edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ In essence, &SCons;
+ "short-circuits" any dependent builds
+ when it realizes that a target file
+ has been rebuilt to exactly the same file as the last build.
+ This does take some extra processing time
+ to read the contents of the target (&hello_o;) file,
+ but often saves time when the rebuild that was avoided
+ would have been time-consuming and expensive.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Using Time Stamps to Decide If a File Has Changed</title>
+
+ <para>
+
+ If you prefer, you can
+ configure &SCons; to use the modification time
+ of a file, not the file contents,
+ when deciding if a target needs to be rebuilt.
+ &SCons; gives you two ways to use time stamps
+ to decide if an input file has changed
+ since the last time a target has been built.
+
+ </para>
+
+ <para>
+
+ The most familiar way to use time stamps
+ is the way &Make; does:
+ that is, have &SCons; decide
+ that a target must be rebuilt
+ if a source file's modification time is
+ <emphasis>newer</emphasis>
+ than the target file.
+ To do this, call the &Decider;
+ function as follows:
+
+ </para>
+
+ <scons_example name="newer">
+ <file name="SConstruct" printme="1">
+ Program('hello.c')
+ Decider('timestamp-newer')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This makes &SCons; act like &Make;
+ when a file's modification time is updated
+ (using the &touch; command, for example):
+
+ </para>
+
+ <scons_output example="newer" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>touch hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And, in fact, because this behavior is the same
+ as the behavior of &Make;,
+ you can also use the string <literal>'make'</literal>
+ as a synonym for <literal>'timestamp-newer'</literal>
+ when calling the &Decider; function:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ Decider('make')
+ </sconstruct>
+
+ <para>
+
+ One drawback to using times stamps exactly like &Make;
+ is that if an input file's modification time suddenly
+ becomes <emphasis>older</emphasis> than a target file,
+ the target file will not be rebuilt.
+ This can happen if an old copy of a source file is restored
+ from a backup archive, for example.
+ The contents of the restored file will likely be different
+ than they were the last time a dependent target was built,
+ but the target won't be rebuilt
+ because the modification time of the source file
+ is not newer than the target.
+
+ </para>
+
+ <para>
+
+ Because &SCons; actually stores information
+ about the source files' time stamps whenever a target is built,
+ it can handle this situation by checking for
+ an exact match of the source file time stamp,
+ instead of just whether or not the source file
+ is newer than the target file.
+ To do this, specify the argument
+ <literal>'timestamp-match'</literal>
+ when calling the &Decider; function:
+
+ </para>
+
+ <scons_example name="match">
+ <file name="SConstruct" printme="1">
+ Program('hello.c')
+ Decider('timestamp-match')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ When configured this way,
+ &SCons; will rebuild a target whenever
+ a source file's modification time has changed.
+ So if we use the <literal>touch -t</literal>
+ option to change the modification time of
+ &hello_c; to an old date (January 1, 1989),
+ &SCons; will still rebuild the target file:
+
+ </para>
+
+ <scons_output example="match" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>touch -t 198901010000 hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ In general, the only reason to prefer
+ <literal>timestamp-newer</literal>
+ instead of
+ <literal>timestamp-match</literal>,
+ would be if you have some specific reason
+ to require this &Make;-like behavior of
+ not rebuilding a target when an otherwise-modified
+ source file is older.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Deciding If a File Has Changed Using Both MD Signatures and Time Stamps</title>
+
+ <para>
+
+ As a performance enhancement,
+ &SCons; provides a way to use
+ MD5 checksums of file contents
+ but to read those contents
+ only when the file's timestamp has changed.
+ To do this, call the &Decider;
+ function with <literal>'MD5-timestamp'</literal>
+ argument as follows:
+
+ </para>
+
+ <scons_example name="MD5-timestamp">
+ <file name="SConstruct" printme="1">
+ Program('hello.c')
+ Decider('MD5-timestamp')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ So configured, &SCons will still behave like
+ it does when using <literal>Decider('MD5')</literal>:
+
+ </para>
+
+ <!--
+
+ We want to generate the output as follows,
+ but our "surrogate" system for generating the
+ output seems to get this wrong.
+ Just in-line the output for now.
+
+ <scons_output example="MD5-timestamp" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>touch hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>touch hello.c</userinput>
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit hello.c</userinput>
+ [CHANGE THE CONTENTS OF hello.c]
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ However, the second call to &SCons; in the above output,
+ when the build is up-to-date,
+ will have been performed by simply looking at the
+ modification time of the &hello_c; file,
+ not by opening it and performing
+ an MD5 checksum calcuation on its contents.
+ This can significantly speed up many up-to-date builds.
+
+ </para>
+
+ <para>
+
+ The only drawback to using
+ <literal>Decider('MD5-timestamp')</literal>
+ is that &SCons; will <emphasis>not</emphasis>
+ rebuild a target file if a source file was modified
+ within one second of the last time &SCons; built the file.
+ While most developers are programming,
+ this isn't a problem in practice,
+ since it's unlikely that someone will have built
+ and then thought quickly enough to make a substantive
+ change to a source file within one second.
+ Certain build scripts or
+ continuous integration tools may, however,
+ rely on the ability to apply changes to files
+ automatically and then rebuild as quickly as possible,
+ in which case use of
+ <literal>Decider('MD5-timestamp')</literal>
+ may not be appropriate.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Writing Your Own Custom &Decider; Function</title>
+
+ <para>
+
+ The different string values that we've passed to
+ the &Decider; function are essentially used by &SCons;
+ to pick one of several specific internal functions
+ that implement various ways of deciding if a dependency
+ (usually a source file)
+ has changed since a target file has been built.
+ As it turns out,
+ you can also supply your own function
+ to decide if a dependency has changed.
+
+ </para>
+
+ <para>
+
+ For example, suppose we have an input file
+ that contains a lot of data,
+ in some specific regular format,
+ that is used to rebuild a lot of different target files,
+ but each target file really only depends on
+ one particular section of the input file.
+ We'd like to have each target file depend on
+ only its section of the input file.
+ However, since the input file may contain a lot of data,
+ we want to open the input file only if its timestamp has changed.
+ This could done with a custom
+ &Decider; function that might look something like this:
+
+ </para>
+
+ <scons_example name="function">
+ <file name="SConstruct" printme="1">
+ Program('hello.c')
+ def decide_if_changed(dependency, target, prev_ni):
+ if self.get_timestamp() != prev_ni.timestamp:
+ dep = str(dependency)
+ tgt = str(target)
+ if specific_part_of_file_has_changed(dep, tgt):
+ return True
+ return False
+ Decider(decide_if_changed)
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Note that in the function definition,
+ the <varname>dependency</varname>
+ (input file) is the first argument,
+ and then the &target;.
+ Both of these are passed to the functions as
+ SCons &Node; objects,
+ which we convert to strings using the Python
+ <function>str()</function>.
+
+ </para>
+
+ <para>
+
+ The third argument, <varname>prev_ni</varname>,
+ is an object that holds the
+ signature or timestamp information
+ that was recorded about the dependency
+ the last time the target was built.
+ A <varname>prev_ni</varname> object can hold
+ different information,
+ depending on the type of thing that the
+ <varname>dependency</varname> argument represents.
+ For normal files,
+ the <varname>prev_ni</varname> object
+ has the following attributes:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>.csig</term>
+
+ <listitem>
+ <para>
+ The <emphasis>content signature</emphasis>,
+ or MD5 checksum, of the contents of the
+ <varname>dependency</varname>
+ file the list time the &target; was built.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>.size</term>
+
+ <listitem>
+ <para>
+ The size in bytes of the <varname>dependency</varname>
+ file the list time the target was built.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>.timestamp</term>
+
+ <listitem>
+ <para>
+ The modification time of the <varname>dependency</varname>
+ file the list time the &target; was built.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ Note that ignoring some of the arguments
+ in your custom &Decider; function
+ is a perfectly normal thing to do,
+ if they don't impact the way you want to
+ decide if the dependency file has changed.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Mixing Different Ways of Deciding If a File Has Changed</title>
+
+ <para>
+
+ The previous examples have all demonstrated calling
+ the global &Decider; function
+ to configure all dependency decisions that &SCons; makes.
+ Sometimes, however, you want to be able to configure
+ different decision-making for different targets.
+ When that's necessary, you can use the
+ <function>env.Decider</function>
+ method to affect only the configuration
+ decisions for targets built with a
+ specific construction environment.
+
+ </para>
+
+ <para>
+
+ For example, if we arbitrarily want to build
+ one program using MD5 checkums
+ and another using file modification times
+ from the same source
+ we might configure it this way:
+
+ </para>
+
+ <scons_example name="mixing">
+ <file name="SConstruct" printme="1">
+ env1 = Environment(CPPPATH = ['.'])
+ env2 = env1.Clone()
+ env2.Decider('timestamp-match')
+ env1.Program('prog-MD5', 'program1.c')
+ env2.Program('prog-timestamp', 'program2.c')
+ </file>
+ <file name="program1.c">
+ #include "inc.h"
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="program2.c">
+ #include "inc.h"
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="inc.h">
+ #define INC 1
+ </file>
+ </scons_example>
+
+ <para>
+
+ If both of the programs include the same
+ <filename>inc.h</filename> file,
+ then updating the modification time of
+ <filename>inc.h</filename>
+ (using the &touch; command)
+ will cause only <filename>prog-timestamp</filename>
+ to be rebuilt:
+
+ </para>
+
+ <scons_output example="mixing" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>touch inc.h</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Older Functions for Deciding When an Input File Has Changed</title>
+
+ <para>
+
+ &SCons; still supports two functions that used to be the
+ primary methods for configuring the
+ decision about whether or not an input file has changed.
+ 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.
+
+ </para>
+
+ <section>
+ <title>The &SourceSignatures; Function</title>
+
+ <para>
+
+ The &SourceSignatures; function is fairly straightforward,
+ and supports two different argument values
+ to configure whether source file changes should be decided
+ using MD5 signatures:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ SourceSignatures('MD5')
+ </sconstruct>
+
+ <para>
+
+ Or using time stamps:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ SourceSignatures('timestamp')
+ </sconstruct>
+
+ <para>
+
+ These are roughly equivalent to specifying
+ <function>Decider('MD5')</function>
+ or
+ <function>Decider('timestamp-match')</function>,
+ respectively,
+ although it only affects how SCons makes
+ decisions about dependencies on
+ <emphasis>source</emphasis> files--that is,
+ files that are not built from any other files.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &TargetSignatures; Function</title>
+
+ <para>
+
+ The &TargetSignatures; function
+ specifies how &SCons; decides
+ when a target file has changed
+ <emphasis>when it is used as a
+ dependency of (input to) another target</emphasis>--that is,
+ the &TargetSignatures; function configures
+ how the signatures of "intermediate" target files
+ are used when deciding if a "downstream" target file
+ must be rebuilt.
+ <footnote><para>
+ This easily-overlooked distinction between
+ how &SCons; decides if the target itself must be rebuilt
+ and how the target is then used to decide if a different
+ target must be rebuilt is one of the confusing
+ things that has led to the &TargetSignatures;
+ and &SourceSignatures; functions being
+ replaced by the simpler &Decider; function.
+ </para></footnote>
+
+ </para>
+
+ <para>
+
+ The &TargetSignatures; function supports the same
+ <literal>'MD5'</literal> and <literal>'timestamp'</literal>
+ argument values that are supported by the &SourceSignatures;,
+ with the same meanings, but applied to target files.
+ That is, in the example:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ TargetSignatures('MD5')
+ </sconstruct>
+
+ <para>
+
+ The MD5 checksum of the &hello_o; target file
+ will be used to decide if it has changed since the last
+ time the "downstream" &hello; target file was built.
+ And in the example:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ TargetSignatures('timestamp')
+ </sconstruct>
+
+ <para>
+
+ The modification time of the &hello_o; target file
+ will be used to decide if it has changed since the last
+ time the "downstream" &hello; target file was built.
+
+ </para>
+
+ <para>
+
+ The &TargetSignatures; function supports
+ two additional argument values:
+ <literal>'source'</literal> and <literal>'build'</literal>.
+ The <literal>'source'</literal> argument
+ specifies that decisions involving
+ whether target files have changed
+ since a previous build
+ should use the same behavior
+ for the decisions configured for source files
+ (using the &SourceSignatures; function).
+ So in the example:
+
+ </para>
+
+ <sconstruct>
+ Program('hello.c')
+ TargetSignatures('source')
+ SourceSignatures('timestamp')
+ </sconstruct>
+
+ <para>
+
+ All files, both targets and sources,
+ will use modification times
+ when deciding if an input file
+ has changed since the last
+ time a target was built.
+
+ </para>
+
+ <para>
+
+ Lastly, the <literal>'build'</literal> argument
+ specifies that &SCons; should examine
+ the build status of a target file
+ and always rebuild a "downstream" target
+ if the target file was itself rebuilt,
+ without re-examining the contents or timestamp
+ of the newly-built target file.
+ If the target file was not rebuilt during
+ this &scons; invocation,
+ then the target file will be examined
+ the same way as configured by
+ the &SourceSignature; call
+ to decide if it has changed.
+
+ </para>
+
+ <para>
+
+ This mimics the behavior of
+ <literal>build signatures</literal>
+ in earlier versions of &SCons;.
+ A &buildsignature; re-combined
+ signatures of all the input files
+ that went into making the target file,
+ so that the target file itself
+ did not need to have its contents read
+ to compute an MD5 signature.
+ This can improve performance for some configurations,
+ but is generally not as effective as using
+ <literal>Decider('MD5-timestamp')</literal>.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Implicit Dependencies: The &cv-CPPPATH; Construction Variable</title>
+
+ <para>
+
+ Now suppose that our "Hello, World!" program
+ actually has an <literal>#include</literal> line
+ to include the &hello_h; file in the compilation:
+
+ </para>
+
+ <scons_example name="include">
+ <file name="SConstruct">
+ Program('hello.c', CPPPATH = '.')
+ </file>
+ <file name="hello.c" printme="1">
+ #include &lt;hello.h&gt;
+ int
+ main()
+ {
+ printf("Hello, %s!\n", string);
+ }
+ </file>
+ <file name="hello.h">
+ #define string "world"
+ </file>
+ </scons_example>
+
+ <para>
+
+ And, for completeness, the &hello_h; file looks like this:
+
+ </para>
+
+ <scons_example_file example="include" name="hello.h">
+ </scons_example_file>
+
+ <para>
+
+ In this case, we want &SCons; to recognize that,
+ if the contents of the &hello_h; file change,
+ the &hello; program must be recompiled.
+ To do this, we need to modify the
+ &SConstruct; file like so:
+
+ </para>
+
+ <scons_example_file example="include" name="SConstruct">
+ </scons_example_file>
+
+ <para>
+
+ The &cv-link-CPPPATH; value
+ tells &SCons; to look in the current directory
+ (<literal>'.'</literal>)
+ for any files included by C source files
+ (<filename>.c</filename> or <filename>.h</filename> files).
+ With this assignment in the &SConstruct; file:
+
+ </para>
+
+ <scons_output example="include" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.h]">edit hello.h</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ First, notice that &SCons;
+ added the <literal>-I.</literal> argument
+ from the &cv-CPPPATH; variable
+ so that the compilation would find the
+ &hello_h; file in the local directory.
+
+ </para>
+
+ <para>
+
+ Second, realize that &SCons; knows that the &hello;
+ program must be rebuilt
+ because it scans the contents of
+ the &hello_c; file
+ for the <literal>#include</literal> lines that indicate
+ another file is being included in the compilation.
+ &SCons; records these as
+ <emphasis>implicit dependencies</emphasis>
+ of the target file,
+ Consequently,
+ when the &hello_h; file changes,
+ &SCons; realizes that the &hello_c; file includes it,
+ and rebuilds the resulting &hello; program
+ that depends on both the &hello_c; and &hello_h; files.
+
+ </para>
+
+ <para>
+
+ Like the &cv-link-LIBPATH; variable,
+ the &cv-CPPPATH; variable
+ may be a list of directories,
+ or a string separated by
+ the system-specific path separation character
+ (':' on POSIX/Linux, ';' on Windows).
+ Either way, &SCons; creates the
+ right command-line options
+ so that the following example:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ Program('hello.c', CPPPATH = ['include', '/home/project/inc'])
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Will look like this on POSIX or Linux:
+
+ </para>
+
+ <scons_output example="ex5" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And like this on Windows:
+
+ </para>
+
+ <scons_output example="ex5" os="win32">
+ <scons_output_command>scons -Q hello.exe</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Caching Implicit Dependencies</title>
+
+ <para>
+
+ Scanning each file for <literal>#include</literal> lines
+ does take some extra processing time.
+ When you're doing a full build of a large system,
+ the scanning time is usually a very small percentage
+ of the overall time spent on the build.
+ You're most likely to notice the scanning time,
+ however, when you <emphasis>rebuild</emphasis>
+ all or part of a large system:
+ &SCons; will likely take some extra time to "think about"
+ what must be built before it issues the
+ first build command
+ (or decides that everything is up to date
+ and nothing must be rebuilt).
+
+ <!--
+ Isn't this expensive? The answer is, it depends. If you do a full build of a
+ large system, the scanning time is insignificant. If you do a rebuild of a
+ large system, then Cons will spend a fair amount of time thinking about it
+ before it decides that nothing has to be done (although not necessarily more
+ time than make!). The good news is that Cons makes it very easy to
+ intelligently subset your build, when you are working on localized changes.
+ -->
+
+ </para>
+
+ <para>
+
+ In practice, having &SCons; scan files saves time
+ relative to the amount of potential time
+ lost to tracking down subtle problems
+ introduced by incorrect dependencies.
+ Nevertheless, the "waiting time"
+ while &SCons; scans files can annoy
+ individual developers waiting for their builds to finish.
+ Consequently, &SCons; lets you cache
+ the implicit dependencies
+ that its scanners find,
+ for use by later builds.
+ You can do this by specifying the
+ &implicit-cache; option on the command line:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q --implicit-cache hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If you don't want to specify &implicit-cache;
+ on the command line each time,
+ you can make it the default behavior for your build
+ by setting the &implicit_cache; option
+ in an &SConscript; file:
+
+ </para>
+
+ <sconstruct>
+ SetOption('implicit_cache', 1)
+ </sconstruct>
+
+ <para>
+
+ &SCons; does not cache implicit dependencies like this by default
+ because the &implicit-cache; causes &SCons; to simply use the implicit
+ dependencies stored during the last run, without any checking
+ for whether or not those dependencies are still correct.
+ Specifically, this means &implicit-cache; instructs &SCons;
+ to <emphasis>not</emphasis> rebuild "correctly" in the
+ following cases:
+
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ When &implicit-cache; is used, &SCons; will ignore any changes that
+ may have been made to search paths
+ (like &cv-CPPPATH; or &cv-LIBPATH;,).
+ This can lead to &SCons; not rebuilding a file if a change to
+ &cv-CPPPATH; would normally cause a different, same-named file from
+ a different directory to be used.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ When &implicit-cache; is used, &SCons; will not detect if a
+ same-named file has been added to a directory that is earlier in
+ the search path than the directory in which the file was found
+ last time.
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <section>
+ <title>The &implicit-deps-changed; Option</title>
+
+ <para>
+
+ When using cached implicit dependencies,
+ sometimes you want to "start fresh"
+ and have &SCons; re-scan the files
+ for which it previously cached the dependencies.
+ For example,
+ if you have recently installed a new version of
+ external code that you use for compilation,
+ the external header files will have changed
+ and the previously-cached implicit dependencies
+ will be out of date.
+ You can update them by
+ running &SCons; with the &implicit-deps-changed; option:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q --implicit-deps-changed hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ In this case, &SCons; will re-scan all of the implicit dependencies
+ and cache updated copies of the information.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &implicit-deps-unchanged; Option</title>
+
+ <para>
+
+ By default when caching dependencies,
+ &SCons; notices when a file has been modified
+ and re-scans the file for any updated
+ implicit dependency information.
+ Sometimes, however, you may want
+ to force &SCons; to use the cached implicit dependencies,
+ even if the source files changed.
+ This can speed up a build for example,
+ when you have changed your source files
+ but know that you haven't changed
+ any <literal>#include</literal> lines.
+ In this case,
+ you can use the &implicit-deps-unchanged; option:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q --implicit-deps-unchanged hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ In this case,
+ &SCons; will assume that the cached implicit
+ dependencies are correct and
+ will not bother to re-scan changed files.
+ For typical builds after small,
+ incremental changes to source files,
+ the savings may not be very big,
+ but sometimes every bit of
+ improved performance counts.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>XXX max drift</title>
+
+ XXX SetOption('max_drift')
+
+ </section>
+
+ -->
+
+ </section>
+
+ <section>
+ <title>Explicit Dependencies: the &Depends; Function</title>
+
+ <para>
+
+ Sometimes a file depends on another file
+ that is not detected by an &SCons; scanner.
+ For this situation,
+ &SCons; allows you to specific explicitly that one file
+ depends on another file,
+ and must be rebuilt whenever that file changes.
+ This is specified using the &Depends; method:
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c')
+ Depends(hello, 'other_file')
+ </programlisting>
+
+ <!-- XXX mention that you can use arrays for target and source? -->
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit other_file</userinput>
+ [CHANGE THE CONTENTS OF other_file]
+ % <userinput>scons -Q hello</userinput>
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ Note that the dependency
+ (the second argument to &Depends;)
+ may also be a list of Node objects
+ (for example, as returned by a call to a Builder):
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c')
+ goodbye = Program('goodbye.c')
+ Depends(hello, goodbye)
+ </programlisting>
+
+ <para>
+
+ in which case the dependency or dependencies
+ will be built before the target(s):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -c goodbye.c -o goodbye.o
+ cc -o goodbye goodbye.o
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Dependencies From External Files: the &ParseDepends;
+ Function</title>
+
+ <para>
+
+ &SCons; has built-in scanners for a number of languages. Sometimes
+ these scanners fail to extract certain implicit dependencies due
+ to limitations of the scanner implementation.
+
+ </para>
+
+ <para>
+
+ The following example illustrates a case where the built-in C
+ scanner is unable to extract the implicit dependency on a header
+ file.
+
+ </para>
+
+ <scons_example name="macroinc">
+ <file name="hello.c" printme="1">
+ #define FOO_HEADER &lt;foo.h&gt;
+ #include FOO_HEADER
+
+ int main() {
+ return FOO;
+ }
+ </file>
+ <file name="SConstruct">
+ Program('hello', 'hello.c', CPPPATH='.')
+ </file>
+ <file name="foo.h">
+ #define FOO 42
+ </file>
+ </scons_example>
+
+ <scons_output example="macroinc" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE CONTENTS OF foo.h]"
+ >edit foo.h</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Apparently, the scanner does not know about the header dependency.
+ Being not a full-fledged C preprocessor, the scanner does not
+ expand the macro.
+
+ </para>
+
+ <para>
+
+ In these cases, you may also use the compiler to extract the
+ implicit dependencies. &ParseDepends; can parse the contents of
+ the compiler output in the style of &Make;, and explicitly
+ establish all of the listed dependencies.
+
+ </para>
+
+ <para>
+
+ The following example uses &ParseDepends; to process a compiler
+ generated dependency file which is generated as a side effect
+ during compilation of the object file:
+
+ </para>
+
+ <!-- XXX The ParseDepends example below fakes proper working by a
+ priori specification of the dependency file. The produced hello.d
+ file is not found (or used) for unknown reasons. -->
+
+ <scons_example name="parsedep">
+ <file name="hello.c">
+ #define FOO_HEADER &lt;foo.h&gt;
+ #include FOO_HEADER
+
+ int main() {
+ return FOO;
+ }
+ </file>
+ <file name="SConstruct" printme="1">
+ obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.')
+ SideEffect('hello.d', obj)
+ ParseDepends('hello.d')
+ Program('hello', obj)
+ </file>
+ <file name="foo.h">
+ #define FOO 42
+ </file>
+ <file name="hello.d">
+ hello.o: hello.c foo.h
+ </file>
+ </scons_example>
+
+ <scons_output example="parsedep" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE CONTENTS OF foo.h]"
+ >edit foo.h</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Parsing dependencies from a compiler-generated
+ <filename>.d</filename> file has a chicken-and-egg problem, that
+ causes unnecessary rebuilds:
+
+ </para>
+
+ <scons_example name="parsedeprebuild">
+ <file name="hello.c">
+ #define FOO_HEADER &lt;foo.h&gt;
+ #include FOO_HEADER
+
+ int main() {
+ return FOO;
+ }
+ </file>
+ <file name="SConstruct">
+ obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.')
+ SideEffect('hello.d', obj)
+ ParseDepends('hello.d')
+ Program('hello', obj)
+ </file>
+ <file name="foo.h">
+ #define FOO 42
+ </file>
+ </scons_example>
+
+ <!--
+ <scons_output example="parsedeprebuild" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -MD -MF hello.d -I. hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q --debug=explain</userinput>
+ scons: rebuilding `hello.o' because `foo.h' is a new dependency
+ cc -o hello.o -c -MD -MF hello.d -I. hello.c
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ In the first pass, the dependency file is generated while the
+ object file is compiled. At that time, &SCons; does not know about
+ the dependency on <filename>foo.h</filename>. In the second pass,
+ the object file is regenerated because <filename>foo.h</filename>
+ is detected as a new dependency.
+
+ </para>
+
+ <para>
+
+ &ParseDepends; immediately reads the specified file at invocation
+ time and just returns if the file does not exist. A dependency
+ file generated during the build process is not automatically
+ parsed again. Hence, the compiler-extracted dependencies are not
+ stored in the signature database during the same build pass. This
+ limitation of &ParseDepends; leads to unnecessary recompilations.
+ Therefore, &ParseDepends; should only be used if scanners are not
+ available for the employed language or not powerful enough for the
+ specific task.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Ignoring Dependencies: the &Ignore; Function</title>
+
+ <para>
+
+ Sometimes it makes sense
+ to not rebuild a program,
+ even if a dependency file changes.
+ In this case,
+ you would tell &SCons; specifically
+ to ignore a dependency as follows:
+
+ </para>
+
+ <scons_example name="ignore">
+ <file name="SConstruct" printme="1">
+ hello = Program('hello.c')
+ Ignore(hello, 'hello.h')
+ </file>
+ <file name="hello.c">
+ #include "hello.h"
+ int main() { printf("Hello, %s!\n", string); }
+ </file>
+ <file name="hello.h">
+ #define string "world"
+ </file>
+ </scons_example>
+
+ <!-- XXX mention that you can use lists for target and source? -->
+
+ <!--
+ <scons_output example="ignore">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.h]">edit hello.h</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ XXX THIS EXAMPLE SHOULD BE UP-TO-DATE! XXX
+ </scons_output>
+ -->
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -c -o hello.o hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit hello.h</userinput>
+ [CHANGE THE CONTENTS OF hello.h]
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ Now, the above example is a little contrived,
+ because it's hard to imagine a real-world situation
+ where you wouldn't want to rebuild &hello;
+ if the &hello_h; file changed.
+ A more realistic example
+ might be if the &hello;
+ program is being built in a
+ directory that is shared between multiple systems
+ that have different copies of the
+ &stdio_h; include file.
+ In that case,
+ &SCons; would notice the differences between
+ the different systems' copies of &stdio_h;
+ and would rebuild &hello;
+ each time you change systems.
+ You could avoid these rebuilds as follows:
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c', CPPPATH=['/usr/include'])
+ Ignore(hello, '/usr/include/stdio.h')
+ </programlisting>
+
+ <para>
+ &Ignore; can also be used to prevent a generated file from being built
+ by default. This is due to the fact that directories depend on
+ their contents. So to ignore a generated file from the default build,
+ you specify that the directory should ignore the generated file.
+ Note that the file will still be built if the user specifically
+ requests the target on scons command line, or if the file is
+ a dependency of another file which is requested and/or is built
+ by default.
+ </para>
+
+ <scons_example name="ignore_explicit">
+ <file name="SConstruct" printme="1">
+ hello_obj=Object('hello.c')
+ hello = Program(hello_obj)
+ Ignore('.',[hello,hello_obj])
+ </file>
+ <file name="hello.c">
+ #include "stdio.h"
+ int main() { printf("Hello!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="ignore_explicit" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+ </section>
+
+ <section>
+ <title>Order-Only Dependencies: the &Requires; Function</title>
+
+ <para>
+
+ Occasionally,
+ it may be useful to specify that a certain
+ file or directory must, if necessary,
+ be built or created before some other target is built,
+ but that changes to that file or directory
+ do <emphasis>not</emphasis>
+ require that the target itself be rebuilt.
+ Such a relationship is called an
+ <emphasis>order-only dependency</emphasis>
+ because it only affects the order in which
+ things must be built--the dependency before the target--but
+ it is not a strict dependency relationship
+ because the target should not
+ change in response to changes in the dependent file.
+
+ </para>
+
+ <para>
+
+ For example, suppose that you want to create a file
+ every time you run a build
+ that identifies the time the build was performed,
+ the version number, etc.,
+ and which is included in every program that you build.
+ The version file's contents will change every build.
+ If you specify a normal dependency relationship,
+ then every program that depends on
+ that file would be rebuilt every time you ran &SCons;.
+ For example, we could use some Python code in
+ a &SConstruct; file to create a new <filename>version.c</filename> file
+ with a string containing the current date every time
+ we run &SCons;,
+ and then link a program with the resulting object file
+ by listing <filename>version.c</filename> in the sources:
+
+ </para>
+
+ <scons_example name="no-Requires">
+ <file name="SConstruct" printme="1">
+ import time
+
+ version_c_text = """
+ char *date = "%s";
+ """ % time.ctime(time.time())
+ open('version.c', 'w').write(version_c_text)
+
+ hello = Program(['hello.c', 'version.c'])
+ </file>
+ <file name="hello.c">
+ extern char *date;
+ int main() { printf("Hello, %s! I was built: %s\n", date); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ If we list <filename>version.c</filename> as an actual source file,
+ though, then <filename>version.o</filename>
+ will get rebuilt every time we run &SCons;
+ (because the &SConstruct; file itself changes
+ the contents of <filename>version.c</filename>)
+ and the <filename>hello</filename> executable
+ will get re-linked every time
+ (because the <filename>version.o</filename> file changes):
+
+ </para>
+
+ <!--
+
+ <scons_output example="no-Requires">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ gcc -o hello.o -c hello.c
+ gcc -o version.o -c version.c
+ gcc -o hello hello.o version.o
+ % <userinput>scons -Q</userinput>
+ gcc -o version.o -c version.c
+ gcc -o hello hello.o version.o
+ % <userinput>scons -Q</userinput>
+ gcc -o version.o -c version.c
+ gcc -o hello hello.o version.o
+ </screen>
+
+ <para>
+
+ One solution is to use the &Requires; function
+ to specify that the <filename>version.o</filename>
+ must be rebuilt before it is used by the link step,
+ but that changes to <filename>version.o</filename>
+ should not actually cause the <filename>hello</filename>
+ executable to be re-linked:
+
+ </para>
+
+ <scons_example name="Requires">
+ <file name="SConstruct" printme="1">
+ import time
+
+ version_c_text = """
+ char *date = "%s";
+ """ % time.ctime(time.time())
+ open('version.c', 'w').write(version_c_text)
+
+ version_obj = Object('version.c')
+
+ hello = Program('hello.c',
+ LINKFLAGS = str(version_obj[0]))
+
+ Requires(hello, version_obj)
+ </file>
+ <file name="hello.c">
+ extern char *date;
+ int main() { printf("Hello, %s! I was built: %s\n", date); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Notice that because we can no longer list <filename>version.c</filename>
+ as one of the sources for the <filename>hello</filename> program,
+ we have to find some other way to get it into the link command line.
+ For this example, we're cheating a bit and stuffing the
+ object file name (extracted from <literal>version_obj</literal>
+ list returned by the &b-Object; call)
+ into the &cv-link-LINKFLAGS; variable,
+ because &cv-LINKFLAGS; is already included
+ in the &cv-link-LINKCOM; command line.
+
+ </para>
+
+ <para>
+
+ With these changes,
+ we get the desired behavior of
+ re-building the <filename>version.o</filename> file,
+ and therefore re-linking the <filename>hello</filename> executable,
+ only when the <filename>hello.c</filename> has changed:
+
+ </para>
+
+ <scons_output example="Requires">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>The &AlwaysBuild; Function</title>
+
+ <para>
+
+ How &SCons; handles dependencies can also be affected
+ by the &AlwaysBuild; method.
+ When a file is passed to the &AlwaysBuild; method,
+ like so:
+
+ </para>
+
+ <scons_example name="AlwaysBuild">
+ <file name="SConstruct" printme="1">
+ hello = Program('hello.c')
+ AlwaysBuild(hello)
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, %s!\n", string); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then the specified target file (&hello; in our example)
+ will always be considered out-of-date and
+ rebuilt whenever that target file is evaluated
+ while walking the dependency graph:
+
+ </para>
+
+ <scons_output example="AlwaysBuild">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The &AlwaysBuild; function has a somewhat misleading name,
+ because it does not actually mean the target file will
+ be rebuilt every single time &SCons; is invoked.
+ Instead, it means that the target will, in fact,
+ be rebuilt whenever the target file is encountered
+ while evaluating the targets specified on
+ the command line (and their dependencies).
+ So specifying some other target on the command line,
+ a target that does <emphasis>not</emphasis>
+ itself depend on the &AlwaysBuild; target,
+ will still be rebuilt only if it's out-of-date
+ with respect to its dependencies:
+
+ </para>
+
+ <scons_output example="AlwaysBuild">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q hello.o</scons_output_command>
+ </scons_output>
+
+ <!--
+
+ XXX AlwaysBuild() and Alias Nodes
+
+ XXX AlwaysBuild() and Dir Nodes
+
+ XXX AlwaysBuild() with no sources
+
+ -->
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>The &Salt; Method</title>
+
+ <para>
+
+ XXX Salt() (are we going to implement this ?)
+
+ original Cons classic POD documentation:
+
+=head2 The C<Salt> method
+
+The C<Salt> method adds a constant value to the signature calculation
+for every derived file. It is invoked as follows:
+
+ Salt $string;
+
+Changing the Salt value will force a complete rebuild of every derived
+file. This can be used to force rebuilds in certain desired
+circumstances. For example,
+
+ Salt `uname -s`;
+
+Would force a complete rebuild of every derived file whenever the
+operating system on which the build is performed (as reported by C<uname
+-s>) changes.
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ So far we've seen how &SCons; handles one-time builds.
+ But one of the main functions of a build tool like &SCons;
+ is to rebuild only what is necessary
+ when source files change--or, put another way,
+ &SCons; should <emphasis>not</emphasis>
+ waste time rebuilding things that don't need to be rebuilt.
+ You can see this at work simply by re-invoking &SCons;
+ after building our simple &hello; example:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ The second time it is executed,
+ &SCons; realizes that the &hello; program
+ is up-to-date with respect to the current &hello_c; source file,
+ and avoids rebuilding it.
+ You can see this more clearly by naming
+ the &hello; program explicitly on the command line:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ Note that &SCons; reports <literal>"...is up to date"</literal>
+ only for target files named explicitly on the command line,
+ to avoid cluttering the output.
+
+ </para>
+
+ <section>
+ <title>Deciding When an Input File Has Changed: the &Decider; Function</title>
+
+ <para>
+
+ Another aspect of avoiding unnecessary rebuilds
+ is the fundamental build tool behavior
+ of <emphasis>rebuilding</emphasis>
+ things when an input file changes,
+ so that the built software is up to date.
+ By default,
+ &SCons; keeps track of this through an
+ MD5 &signature;, or checksum, of the contents of each file,
+ although you can easily configure
+ &SCons; to use the
+ modification times (or time stamps)
+ instead.
+ You can even specify your own Python function
+ for deciding if an input file has changed.
+
+ </para>
+
+ <section>
+ <title>Using MD5 Signatures to Decide if a File Has Changed</title>
+
+ <para>
+
+ By default,
+ &SCons; keeps track of whether a file has changed
+ based on an MD5 checksum of the file's contents,
+ not the file's modification time.
+ This means that you may be surprised by the
+ default &SCons; behavior if you are used to the
+ &Make; convention of forcing
+ a rebuild by updating the file's modification time
+ (using the &touch; command, for example):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>touch hello.c</userinput>
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ Even though the file's modification time has changed,
+ &SCons; realizes that the contents of the
+ &hello_c; file have <emphasis>not</emphasis> changed,
+ and therefore that the &hello; program
+ need not be rebuilt.
+ This avoids unnecessary rebuilds when,
+ for example, someone rewrites the
+ contents of a file without making a change.
+ But if the contents of the file really do change,
+ then &SCons; detects the change
+ and rebuilds the program as required:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>edit hello.c</userinput>
+ [CHANGE THE CONTENTS OF hello.c]
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ Note that you can, if you wish,
+ specify this default behavior
+ (MD5 signatures) explicitly
+ using the &Decider; function as follows:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ Decider('MD5')
+ </programlisting>
+
+ <para>
+
+ You can also use the string <literal>'content'</literal>
+ as a synonym for <literal>'MD5'</literal>
+ when calling the &Decider; function.
+
+ </para>
+
+ <section>
+ <title>Ramifications of Using MD5 Signatures</title>
+
+ <para>
+
+ Using MD5 signatures to decide if an input file has changed
+ has one surprising benefit:
+ if a source file has been changed
+ in such a way that the contents of the
+ rebuilt target file(s)
+ will be exactly the same as the last time
+ the file was built,
+ then any "downstream" target files
+ that depend on the rebuilt-but-not-changed target
+ file actually need not be rebuilt.
+
+ </para>
+
+ <para>
+
+ So if, for example,
+ a user were to only change a comment in a &hello_c; file,
+ then the rebuilt &hello_o; file
+ would be exactly the same as the one previously built
+ (assuming the compiler doesn't put any build-specific
+ information in the object file).
+ &SCons; would then realize that it would not
+ need to rebuild the &hello; program as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>edit hello.c</userinput>
+ [CHANGE A COMMENT IN hello.c]
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ In essence, &SCons;
+ "short-circuits" any dependent builds
+ when it realizes that a target file
+ has been rebuilt to exactly the same file as the last build.
+ This does take some extra processing time
+ to read the contents of the target (&hello_o;) file,
+ but often saves time when the rebuild that was avoided
+ would have been time-consuming and expensive.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Using Time Stamps to Decide If a File Has Changed</title>
+
+ <para>
+
+ If you prefer, you can
+ configure &SCons; to use the modification time
+ of a file, not the file contents,
+ when deciding if a target needs to be rebuilt.
+ &SCons; gives you two ways to use time stamps
+ to decide if an input file has changed
+ since the last time a target has been built.
+
+ </para>
+
+ <para>
+
+ The most familiar way to use time stamps
+ is the way &Make; does:
+ that is, have &SCons; decide
+ that a target must be rebuilt
+ if a source file's modification time is
+ <emphasis>newer</emphasis>
+ than the target file.
+ To do this, call the &Decider;
+ function as follows:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ Decider('timestamp-newer')
+ </programlisting>
+
+ <para>
+
+ This makes &SCons; act like &Make;
+ when a file's modification time is updated
+ (using the &touch; command, for example):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>touch hello.c</userinput>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ And, in fact, because this behavior is the same
+ as the behavior of &Make;,
+ you can also use the string <literal>'make'</literal>
+ as a synonym for <literal>'timestamp-newer'</literal>
+ when calling the &Decider; function:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ Decider('make')
+ </programlisting>
+
+ <para>
+
+ One drawback to using times stamps exactly like &Make;
+ is that if an input file's modification time suddenly
+ becomes <emphasis>older</emphasis> than a target file,
+ the target file will not be rebuilt.
+ This can happen if an old copy of a source file is restored
+ from a backup archive, for example.
+ The contents of the restored file will likely be different
+ than they were the last time a dependent target was built,
+ but the target won't be rebuilt
+ because the modification time of the source file
+ is not newer than the target.
+
+ </para>
+
+ <para>
+
+ Because &SCons; actually stores information
+ about the source files' time stamps whenever a target is built,
+ it can handle this situation by checking for
+ an exact match of the source file time stamp,
+ instead of just whether or not the source file
+ is newer than the target file.
+ To do this, specify the argument
+ <literal>'timestamp-match'</literal>
+ when calling the &Decider; function:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ Decider('timestamp-match')
+ </programlisting>
+
+ <para>
+
+ When configured this way,
+ &SCons; will rebuild a target whenever
+ a source file's modification time has changed.
+ So if we use the <literal>touch -t</literal>
+ option to change the modification time of
+ &hello_c; to an old date (January 1, 1989),
+ &SCons; will still rebuild the target file:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>touch -t 198901010000 hello.c</userinput>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ In general, the only reason to prefer
+ <literal>timestamp-newer</literal>
+ instead of
+ <literal>timestamp-match</literal>,
+ would be if you have some specific reason
+ to require this &Make;-like behavior of
+ not rebuilding a target when an otherwise-modified
+ source file is older.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Deciding If a File Has Changed Using Both MD Signatures and Time Stamps</title>
+
+ <para>
+
+ As a performance enhancement,
+ &SCons; provides a way to use
+ MD5 checksums of file contents
+ but to read those contents
+ only when the file's timestamp has changed.
+ To do this, call the &Decider;
+ function with <literal>'MD5-timestamp'</literal>
+ argument as follows:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ Decider('MD5-timestamp')
+ </programlisting>
+
+ <para>
+
+ So configured, &SCons; will still behave like
+ it does when using <literal>Decider('MD5')</literal>:
+
+ </para>
+
+ <!--
+
+ We want to generate the output as follows,
+ but our "surrogate" system for generating the
+ output seems to get this wrong.
+ Just in-line the output for now.
+
+ <scons_output example="MD5-timestamp" os="posix">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>touch hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>touch hello.c</userinput>
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit hello.c</userinput>
+ [CHANGE THE CONTENTS OF hello.c]
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ However, the second call to &SCons; in the above output,
+ when the build is up-to-date,
+ will have been performed by simply looking at the
+ modification time of the &hello_c; file,
+ not by opening it and performing
+ an MD5 checksum calcuation on its contents.
+ This can significantly speed up many up-to-date builds.
+
+ </para>
+
+ <para>
+
+ The only drawback to using
+ <literal>Decider('MD5-timestamp')</literal>
+ is that &SCons; will <emphasis>not</emphasis>
+ rebuild a target file if a source file was modified
+ within one second of the last time &SCons; built the file.
+ While most developers are programming,
+ this isn't a problem in practice,
+ since it's unlikely that someone will have built
+ and then thought quickly enough to make a substantive
+ change to a source file within one second.
+ Certain build scripts or
+ continuous integration tools may, however,
+ rely on the ability to apply changes to files
+ automatically and then rebuild as quickly as possible,
+ in which case use of
+ <literal>Decider('MD5-timestamp')</literal>
+ may not be appropriate.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Writing Your Own Custom &Decider; Function</title>
+
+ <para>
+
+ The different string values that we've passed to
+ the &Decider; function are essentially used by &SCons;
+ to pick one of several specific internal functions
+ that implement various ways of deciding if a dependency
+ (usually a source file)
+ has changed since a target file has been built.
+ As it turns out,
+ you can also supply your own function
+ to decide if a dependency has changed.
+
+ </para>
+
+ <para>
+
+ For example, suppose we have an input file
+ that contains a lot of data,
+ in some specific regular format,
+ that is used to rebuild a lot of different target files,
+ but each target file really only depends on
+ one particular section of the input file.
+ We'd like to have each target file depend on
+ only its section of the input file.
+ However, since the input file may contain a lot of data,
+ we want to open the input file only if its timestamp has changed.
+ This could done with a custom
+ &Decider; function that might look something like this:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ Note that in the function definition,
+ the <varname>dependency</varname>
+ (input file) is the first argument,
+ and then the &target;.
+ Both of these are passed to the functions as
+ SCons &Node; objects,
+ which we convert to strings using the Python
+ <function>str()</function>.
+
+ </para>
+
+ <para>
+
+ The third argument, <varname>prev_ni</varname>,
+ is an object that holds the
+ signature or timestamp information
+ that was recorded about the dependency
+ the last time the target was built.
+ A <varname>prev_ni</varname> object can hold
+ different information,
+ depending on the type of thing that the
+ <varname>dependency</varname> argument represents.
+ For normal files,
+ the <varname>prev_ni</varname> object
+ has the following attributes:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>.csig</term>
+
+ <listitem>
+ <para>
+ The <emphasis>content signature</emphasis>,
+ or MD5 checksum, of the contents of the
+ <varname>dependency</varname>
+ file the list time the &target; was built.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>.size</term>
+
+ <listitem>
+ <para>
+ The size in bytes of the <varname>dependency</varname>
+ file the list time the target was built.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>.timestamp</term>
+
+ <listitem>
+ <para>
+ The modification time of the <varname>dependency</varname>
+ file the list time the &target; was built.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ Note that ignoring some of the arguments
+ in your custom &Decider; function
+ is a perfectly normal thing to do,
+ if they don't impact the way you want to
+ decide if the dependency file has changed.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Mixing Different Ways of Deciding If a File Has Changed</title>
+
+ <para>
+
+ The previous examples have all demonstrated calling
+ the global &Decider; function
+ to configure all dependency decisions that &SCons; makes.
+ Sometimes, however, you want to be able to configure
+ different decision-making for different targets.
+ When that's necessary, you can use the
+ <function>env.Decider</function>
+ method to affect only the configuration
+ decisions for targets built with a
+ specific construction environment.
+
+ </para>
+
+ <para>
+
+ For example, if we arbitrarily want to build
+ one program using MD5 checkums
+ and another using file modification times
+ from the same source
+ we might configure it this way:
+
+ </para>
+
+ <programlisting>
+ env1 = Environment(CPPPATH = ['.'])
+ env2 = env1.Clone()
+ env2.Decider('timestamp-match')
+ env1.Program('prog-MD5', 'program1.c')
+ env2.Program('prog-timestamp', 'program2.c')
+ </programlisting>
+
+ <para>
+
+ If both of the programs include the same
+ <filename>inc.h</filename> file,
+ then updating the modification time of
+ <filename>inc.h</filename>
+ (using the &touch; command)
+ will cause only <filename>prog-timestamp</filename>
+ to be rebuilt:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ % <userinput>touch inc.h</userinput>
+ % <userinput>scons -Q</userinput>
+ cc -o program2.o -c -I. program2.c
+ cc -o prog-timestamp program2.o
+ </screen>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Older Functions for Deciding When an Input File Has Changed</title>
+
+ <para>
+
+ &SCons; still supports two functions that used to be the
+ primary methods for configuring the
+ decision about whether or not an input file has changed.
+ 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.
+
+ </para>
+
+ <section>
+ <title>The &SourceSignatures; Function</title>
+
+ <para>
+
+ The &SourceSignatures; function is fairly straightforward,
+ and supports two different argument values
+ to configure whether source file changes should be decided
+ using MD5 signatures:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ SourceSignatures('MD5')
+ </programlisting>
+
+ <para>
+
+ Or using time stamps:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ SourceSignatures('timestamp')
+ </programlisting>
+
+ <para>
+
+ These are roughly equivalent to specifying
+ <function>Decider('MD5')</function>
+ or
+ <function>Decider('timestamp-match')</function>,
+ respectively,
+ although it only affects how SCons makes
+ decisions about dependencies on
+ <emphasis>source</emphasis> files--that is,
+ files that are not built from any other files.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &TargetSignatures; Function</title>
+
+ <para>
+
+ The &TargetSignatures; function
+ specifies how &SCons; decides
+ when a target file has changed
+ <emphasis>when it is used as a
+ dependency of (input to) another target</emphasis>--that is,
+ the &TargetSignatures; function configures
+ how the signatures of "intermediate" target files
+ are used when deciding if a "downstream" target file
+ must be rebuilt.
+ <footnote><para>
+ This easily-overlooked distinction between
+ how &SCons; decides if the target itself must be rebuilt
+ and how the target is then used to decide if a different
+ target must be rebuilt is one of the confusing
+ things that has led to the &TargetSignatures;
+ and &SourceSignatures; functions being
+ replaced by the simpler &Decider; function.
+ </para></footnote>
+
+ </para>
+
+ <para>
+
+ The &TargetSignatures; function supports the same
+ <literal>'MD5'</literal> and <literal>'timestamp'</literal>
+ argument values that are supported by the &SourceSignatures;,
+ with the same meanings, but applied to target files.
+ That is, in the example:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ TargetSignatures('MD5')
+ </programlisting>
+
+ <para>
+
+ The MD5 checksum of the &hello_o; target file
+ will be used to decide if it has changed since the last
+ time the "downstream" &hello; target file was built.
+ And in the example:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ TargetSignatures('timestamp')
+ </programlisting>
+
+ <para>
+
+ The modification time of the &hello_o; target file
+ will be used to decide if it has changed since the last
+ time the "downstream" &hello; target file was built.
+
+ </para>
+
+ <para>
+
+ The &TargetSignatures; function supports
+ two additional argument values:
+ <literal>'source'</literal> and <literal>'build'</literal>.
+ The <literal>'source'</literal> argument
+ specifies that decisions involving
+ whether target files have changed
+ since a previous build
+ should use the same behavior
+ for the decisions configured for source files
+ (using the &SourceSignatures; function).
+ So in the example:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ TargetSignatures('source')
+ SourceSignatures('timestamp')
+ </programlisting>
+
+ <para>
+
+ All files, both targets and sources,
+ will use modification times
+ when deciding if an input file
+ has changed since the last
+ time a target was built.
+
+ </para>
+
+ <para>
+
+ Lastly, the <literal>'build'</literal> argument
+ specifies that &SCons; should examine
+ the build status of a target file
+ and always rebuild a "downstream" target
+ if the target file was itself rebuilt,
+ without re-examining the contents or timestamp
+ of the newly-built target file.
+ If the target file was not rebuilt during
+ this &scons; invocation,
+ then the target file will be examined
+ the same way as configured by
+ the &SourceSignature; call
+ to decide if it has changed.
+
+ </para>
+
+ <para>
+
+ This mimics the behavior of
+ <literal>build signatures</literal>
+ in earlier versions of &SCons;.
+ A &buildsignature; re-combined
+ signatures of all the input files
+ that went into making the target file,
+ so that the target file itself
+ did not need to have its contents read
+ to compute an MD5 signature.
+ This can improve performance for some configurations,
+ but is generally not as effective as using
+ <literal>Decider('MD5-timestamp')</literal>.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Implicit Dependencies: The &cv-CPPPATH; Construction Variable</title>
+
+ <para>
+
+ Now suppose that our "Hello, World!" program
+ actually has an <literal>#include</literal> line
+ to include the &hello_h; file in the compilation:
+
+ </para>
+
+ <programlisting>
+ #include &lt;hello.h&gt;
+ int
+ main()
+ {
+ printf("Hello, %s!\n", string);
+ }
+ </programlisting>
+
+ <para>
+
+ And, for completeness, the &hello_h; file looks like this:
+
+ </para>
+
+
+ <programlisting>
+ #define string "world"
+ </programlisting>
+
+ <para>
+
+ In this case, we want &SCons; to recognize that,
+ if the contents of the &hello_h; file change,
+ the &hello; program must be recompiled.
+ To do this, we need to modify the
+ &SConstruct; file like so:
+
+ </para>
+
+
+ <programlisting>
+ Program('hello.c', CPPPATH = '.')
+ </programlisting>
+
+ <para>
+
+ The &cv-link-CPPPATH; value
+ tells &SCons; to look in the current directory
+ (<literal>'.'</literal>)
+ for any files included by C source files
+ (<filename>.c</filename> or <filename>.h</filename> files).
+ With this assignment in the &SConstruct; file:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c -I. hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit hello.h</userinput>
+ [CHANGE THE CONTENTS OF hello.h]
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c -I. hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ First, notice that &SCons;
+ added the <literal>-I.</literal> argument
+ from the &cv-CPPPATH; variable
+ so that the compilation would find the
+ &hello_h; file in the local directory.
+
+ </para>
+
+ <para>
+
+ Second, realize that &SCons; knows that the &hello;
+ program must be rebuilt
+ because it scans the contents of
+ the &hello_c; file
+ for the <literal>#include</literal> lines that indicate
+ another file is being included in the compilation.
+ &SCons; records these as
+ <emphasis>implicit dependencies</emphasis>
+ of the target file,
+ Consequently,
+ when the &hello_h; file changes,
+ &SCons; realizes that the &hello_c; file includes it,
+ and rebuilds the resulting &hello; program
+ that depends on both the &hello_c; and &hello_h; files.
+
+ </para>
+
+ <para>
+
+ Like the &cv-link-LIBPATH; variable,
+ the &cv-CPPPATH; variable
+ may be a list of directories,
+ or a string separated by
+ the system-specific path separation character
+ (':' on POSIX/Linux, ';' on Windows).
+ Either way, &SCons; creates the
+ right command-line options
+ so that the following example:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c', CPPPATH = ['include', '/home/project/inc'])
+ </programlisting>
+
+ <para>
+
+ Will look like this on POSIX or Linux:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c -Iinclude -I/home/project/inc hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ And like this on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q hello.exe</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ cl /Fohello.obj /c hello.c /nologo /Iinclude /I\home\project\inc
+ link /nologo /OUT:hello.exe hello.obj
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Caching Implicit Dependencies</title>
+
+ <para>
+
+ Scanning each file for <literal>#include</literal> lines
+ does take some extra processing time.
+ When you're doing a full build of a large system,
+ the scanning time is usually a very small percentage
+ of the overall time spent on the build.
+ You're most likely to notice the scanning time,
+ however, when you <emphasis>rebuild</emphasis>
+ all or part of a large system:
+ &SCons; will likely take some extra time to "think about"
+ what must be built before it issues the
+ first build command
+ (or decides that everything is up to date
+ and nothing must be rebuilt).
+
+ <!--
+ Isn't this expensive? The answer is, it depends. If you do a full build of a
+ large system, the scanning time is insignificant. If you do a rebuild of a
+ large system, then Cons will spend a fair amount of time thinking about it
+ before it decides that nothing has to be done (although not necessarily more
+ time than make!). The good news is that Cons makes it very easy to
+ intelligently subset your build, when you are working on localized changes.
+ -->
+
+ </para>
+
+ <para>
+
+ In practice, having &SCons; scan files saves time
+ relative to the amount of potential time
+ lost to tracking down subtle problems
+ introduced by incorrect dependencies.
+ Nevertheless, the "waiting time"
+ while &SCons; scans files can annoy
+ individual developers waiting for their builds to finish.
+ Consequently, &SCons; lets you cache
+ the implicit dependencies
+ that its scanners find,
+ for use by later builds.
+ You can do this by specifying the
+ &implicit-cache; option on the command line:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --implicit-cache hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ If you don't want to specify &implicit-cache;
+ on the command line each time,
+ you can make it the default behavior for your build
+ by setting the &implicit_cache; option
+ in an &SConscript; file:
+
+ </para>
+
+ <programlisting>
+ SetOption('implicit_cache', 1)
+ </programlisting>
+
+ <para>
+
+ &SCons; does not cache implicit dependencies like this by default
+ because the &implicit-cache; causes &SCons; to simply use the implicit
+ dependencies stored during the last run, without any checking
+ for whether or not those dependencies are still correct.
+ Specifically, this means &implicit-cache; instructs &SCons;
+ to <emphasis>not</emphasis> rebuild "correctly" in the
+ following cases:
+
+
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+
+ When &implicit-cache; is used, &SCons; will ignore any changes that
+ may have been made to search paths
+ (like &cv-CPPPATH; or &cv-LIBPATH;,).
+ This can lead to &SCons; not rebuilding a file if a change to
+ &cv-CPPPATH; would normally cause a different, same-named file from
+ a different directory to be used.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ When &implicit-cache; is used, &SCons; will not detect if a
+ same-named file has been added to a directory that is earlier in
+ the search path than the directory in which the file was found
+ last time.
+
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ <section>
+ <title>The &implicit-deps-changed; Option</title>
+
+ <para>
+
+ When using cached implicit dependencies,
+ sometimes you want to "start fresh"
+ and have &SCons; re-scan the files
+ for which it previously cached the dependencies.
+ For example,
+ if you have recently installed a new version of
+ external code that you use for compilation,
+ the external header files will have changed
+ and the previously-cached implicit dependencies
+ will be out of date.
+ You can update them by
+ running &SCons; with the &implicit-deps-changed; option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --implicit-deps-changed hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ In this case, &SCons; will re-scan all of the implicit dependencies
+ and cache updated copies of the information.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &implicit-deps-unchanged; Option</title>
+
+ <para>
+
+ By default when caching dependencies,
+ &SCons; notices when a file has been modified
+ and re-scans the file for any updated
+ implicit dependency information.
+ Sometimes, however, you may want
+ to force &SCons; to use the cached implicit dependencies,
+ even if the source files changed.
+ This can speed up a build for example,
+ when you have changed your source files
+ but know that you haven't changed
+ any <literal>#include</literal> lines.
+ In this case,
+ you can use the &implicit-deps-unchanged; option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --implicit-deps-unchanged hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ In this case,
+ &SCons; will assume that the cached implicit
+ dependencies are correct and
+ will not bother to re-scan changed files.
+ For typical builds after small,
+ incremental changes to source files,
+ the savings may not be very big,
+ but sometimes every bit of
+ improved performance counts.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>XXX max drift</title>
+
+ XXX SetOption('max_drift')
+
+ </section>
+
+ -->
+
+ </section>
+
+ <section>
+ <title>Explicit Dependencies: the &Depends; Function</title>
+
+ <para>
+
+ Sometimes a file depends on another file
+ that is not detected by an &SCons; scanner.
+ For this situation,
+ &SCons; allows you to specific explicitly that one file
+ depends on another file,
+ and must be rebuilt whenever that file changes.
+ This is specified using the &Depends; method:
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c')
+ Depends(hello, 'other_file')
+ </programlisting>
+
+ <!-- XXX mention that you can use arrays for target and source? -->
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit other_file</userinput>
+ [CHANGE THE CONTENTS OF other_file]
+ % <userinput>scons -Q hello</userinput>
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ Note that the dependency
+ (the second argument to &Depends;)
+ may also be a list of Node objects
+ (for example, as returned by a call to a Builder):
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c')
+ goodbye = Program('goodbye.c')
+ Depends(hello, goodbye)
+ </programlisting>
+
+ <para>
+
+ in which case the dependency or dependencies
+ will be built before the target(s):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -c goodbye.c -o goodbye.o
+ cc -o goodbye goodbye.o
+ cc -c hello.c -o hello.o
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Dependencies From External Files: the &ParseDepends;
+ Function</title>
+
+ <para>
+
+ &SCons; has built-in scanners for a number of languages. Sometimes
+ these scanners fail to extract certain implicit dependencies due
+ to limitations of the scanner implementation.
+
+ </para>
+
+ <para>
+
+ The following example illustrates a case where the built-in C
+ scanner is unable to extract the implicit dependency on a header
+ file.
+
+ </para>
+
+ <programlisting>
+ #define FOO_HEADER &lt;foo.h&gt;
+ #include FOO_HEADER
+
+ int main() {
+ return FOO;
+ }
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -I. hello.c
+ cc -o hello hello.o
+ % <userinput>edit foo.h</userinput>
+ [CHANGE CONTENTS OF foo.h]
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Apparently, the scanner does not know about the header dependency.
+ Being not a full-fledged C preprocessor, the scanner does not
+ expand the macro.
+
+ </para>
+
+ <para>
+
+ In these cases, you may also use the compiler to extract the
+ implicit dependencies. &ParseDepends; can parse the contents of
+ the compiler output in the style of &Make;, and explicitly
+ establish all of the listed dependencies.
+
+ </para>
+
+ <para>
+
+ The following example uses &ParseDepends; to process a compiler
+ generated dependency file which is generated as a side effect
+ during compilation of the object file:
+
+ </para>
+
+ <!-- XXX The ParseDepends example below fakes proper working by a
+ priori specification of the dependency file. The produced hello.d
+ file is not found (or used) for unknown reasons. -->
+
+ <programlisting>
+ obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.')
+ SideEffect('hello.d', obj)
+ ParseDepends('hello.d')
+ Program('hello', obj)
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -MD -MF hello.d -I. hello.c
+ cc -o hello hello.o
+ % <userinput>edit foo.h</userinput>
+ [CHANGE CONTENTS OF foo.h]
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -MD -MF hello.d -I. hello.c
+ </screen>
+
+ <para>
+
+ Parsing dependencies from a compiler-generated
+ <filename>.d</filename> file has a chicken-and-egg problem, that
+ causes unnecessary rebuilds:
+
+ </para>
+
+
+
+ <!--
+ <scons_output example="parsedeprebuild" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -MD -MF hello.d -I. hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q --debug=explain</userinput>
+ scons: rebuilding `hello.o' because `foo.h' is a new dependency
+ cc -o hello.o -c -MD -MF hello.d -I. hello.c
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ In the first pass, the dependency file is generated while the
+ object file is compiled. At that time, &SCons; does not know about
+ the dependency on <filename>foo.h</filename>. In the second pass,
+ the object file is regenerated because <filename>foo.h</filename>
+ is detected as a new dependency.
+
+ </para>
+
+ <para>
+
+ &ParseDepends; immediately reads the specified file at invocation
+ time and just returns if the file does not exist. A dependency
+ file generated during the build process is not automatically
+ parsed again. Hence, the compiler-extracted dependencies are not
+ stored in the signature database during the same build pass. This
+ limitation of &ParseDepends; leads to unnecessary recompilations.
+ Therefore, &ParseDepends; should only be used if scanners are not
+ available for the employed language or not powerful enough for the
+ specific task.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Ignoring Dependencies: the &Ignore; Function</title>
+
+ <para>
+
+ Sometimes it makes sense
+ to not rebuild a program,
+ even if a dependency file changes.
+ In this case,
+ you would tell &SCons; specifically
+ to ignore a dependency as follows:
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c')
+ Ignore(hello, 'hello.h')
+ </programlisting>
+
+ <!-- XXX mention that you can use lists for target and source? -->
+
+ <!--
+ <scons_output example="ignore">
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.h]">edit hello.h</scons_output_command>
+ <scons_output_command>scons -Q hello</scons_output_command>
+ XXX THIS EXAMPLE SHOULD BE UP-TO-DATE! XXX
+ </scons_output>
+ -->
+
+ <screen>
+ % <userinput>scons -Q hello</userinput>
+ cc -c -o hello.o hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ % <userinput>edit hello.h</userinput>
+ [CHANGE THE CONTENTS OF hello.h]
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+
+ <para>
+
+ Now, the above example is a little contrived,
+ because it's hard to imagine a real-world situation
+ where you wouldn't want to rebuild &hello;
+ if the &hello_h; file changed.
+ A more realistic example
+ might be if the &hello;
+ program is being built in a
+ directory that is shared between multiple systems
+ that have different copies of the
+ &stdio_h; include file.
+ In that case,
+ &SCons; would notice the differences between
+ the different systems' copies of &stdio_h;
+ and would rebuild &hello;
+ each time you change systems.
+ You could avoid these rebuilds as follows:
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c', CPPPATH=['/usr/include'])
+ Ignore(hello, '/usr/include/stdio.h')
+ </programlisting>
+
+ <para>
+ &Ignore; can also be used to prevent a generated file from being built
+ by default. This is due to the fact that directories depend on
+ their contents. So to ignore a generated file from the default build,
+ you specify that the directory should ignore the generated file.
+ Note that the file will still be built if the user specifically
+ requests the target on scons command line, or if the file is
+ a dependency of another file which is requested and/or is built
+ by default.
+ </para>
+
+ <programlisting>
+ hello_obj=Object('hello.c')
+ hello = Program(hello_obj)
+ Ignore('.',[hello,hello_obj])
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ % <userinput>scons -Q hello</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello</userinput>
+ scons: `hello' is up to date.
+ </screen>
+ </section>
+
+ <section>
+ <title>Order-Only Dependencies: the &Requires; Function</title>
+
+ <para>
+
+ Occasionally,
+ it may be useful to specify that a certain
+ file or directory must, if necessary,
+ be built or created before some other target is built,
+ but that changes to that file or directory
+ do <emphasis>not</emphasis>
+ require that the target itself be rebuilt.
+ Such a relationship is called an
+ <emphasis>order-only dependency</emphasis>
+ because it only affects the order in which
+ things must be built--the dependency before the target--but
+ it is not a strict dependency relationship
+ because the target should not
+ change in response to changes in the dependent file.
+
+ </para>
+
+ <para>
+
+ For example, suppose that you want to create a file
+ every time you run a build
+ that identifies the time the build was performed,
+ the version number, etc.,
+ and which is included in every program that you build.
+ The version file's contents will change every build.
+ If you specify a normal dependency relationship,
+ then every program that depends on
+ that file would be rebuilt every time you ran &SCons;.
+ For example, we could use some Python code in
+ a &SConstruct; file to create a new <filename>version.c</filename> file
+ with a string containing the current date every time
+ we run &SCons;,
+ and then link a program with the resulting object file
+ by listing <filename>version.c</filename> in the sources:
+
+ </para>
+
+ <programlisting>
+ 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'])
+ </programlisting>
+
+ <para>
+
+ If we list <filename>version.c</filename> as an actual source file,
+ though, then <filename>version.o</filename>
+ will get rebuilt every time we run &SCons;
+ (because the &SConstruct; file itself changes
+ the contents of <filename>version.c</filename>)
+ and the <filename>hello</filename> executable
+ will get re-linked every time
+ (because the <filename>version.o</filename> file changes):
+
+ </para>
+
+ <!--
+
+ <scons_output example="no-Requires">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ gcc -o hello.o -c hello.c
+ gcc -o version.o -c version.c
+ gcc -o hello hello.o version.o
+ % <userinput>scons -Q</userinput>
+ gcc -o version.o -c version.c
+ gcc -o hello hello.o version.o
+ % <userinput>scons -Q</userinput>
+ gcc -o version.o -c version.c
+ gcc -o hello hello.o version.o
+ </screen>
+
+ <para>
+
+ One solution is to use the &Requires; function
+ to specify that the <filename>version.o</filename>
+ must be rebuilt before it is used by the link step,
+ but that changes to <filename>version.o</filename>
+ should not actually cause the <filename>hello</filename>
+ executable to be re-linked:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ Notice that because we can no longer list <filename>version.c</filename>
+ as one of the sources for the <filename>hello</filename> program,
+ we have to find some other way to get it into the link command line.
+ For this example, we're cheating a bit and stuffing the
+ object file name (extracted from <literal>version_obj</literal>
+ list returned by the &b-Object; call)
+ into the &cv-link-LINKFLAGS; variable,
+ because &cv-LINKFLAGS; is already included
+ in the &cv-link-LINKCOM; command line.
+
+ </para>
+
+ <para>
+
+ With these changes,
+ we get the desired behavior of
+ re-building the <filename>version.o</filename> file,
+ and therefore re-linking the <filename>hello</filename> executable,
+ only when the <filename>hello.c</filename> has changed:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o version.o -c version.c
+ cc -o hello.o -c hello.c
+ cc -o hello version.o hello.o
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ % <userinput>edit hello.c</userinput>
+ [CHANGE THE CONTENTS OF hello.c]
+ % <userinput>scons -Q</userinput>
+ cc -o version.o -c version.c
+ cc -o hello.o -c hello.c
+ cc -o hello version.o hello.o
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &AlwaysBuild; Function</title>
+
+ <para>
+
+ How &SCons; handles dependencies can also be affected
+ by the &AlwaysBuild; method.
+ When a file is passed to the &AlwaysBuild; method,
+ like so:
+
+ </para>
+
+ <programlisting>
+ hello = Program('hello.c')
+ AlwaysBuild(hello)
+ </programlisting>
+
+ <para>
+
+ Then the specified target file (&hello; in our example)
+ will always be considered out-of-date and
+ rebuilt whenever that target file is evaluated
+ while walking the dependency graph:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q</userinput>
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ The &AlwaysBuild; function has a somewhat misleading name,
+ because it does not actually mean the target file will
+ be rebuilt every single time &SCons; is invoked.
+ Instead, it means that the target will, in fact,
+ be rebuilt whenever the target file is encountered
+ while evaluating the targets specified on
+ the command line (and their dependencies).
+ So specifying some other target on the command line,
+ a target that does <emphasis>not</emphasis>
+ itself depend on the &AlwaysBuild; target,
+ will still be rebuilt only if it's out-of-date
+ with respect to its dependencies:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q hello.o</userinput>
+ scons: `hello.o' is up to date.
+ </screen>
+
+ <!--
+
+ XXX AlwaysBuild() and Alias Nodes
+
+ XXX AlwaysBuild() and Dir Nodes
+
+ XXX AlwaysBuild() with no sources
+
+ -->
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>The &Salt; Method</title>
+
+ <para>
+
+ XXX Salt() (are we going to implement this ?)
+
+ original Cons classic POD documentation:
+
+=head2 The C<Salt> method
+
+The C<Salt> method adds a constant value to the signature calculation
+for every derived file. It is invoked as follows:
+
+ Salt $string;
+
+Changing the Salt value will force a complete rebuild of every derived
+file. This can be used to force rebuilds in certain desired
+circumstances. For example,
+
+ Salt `uname -s`;
+
+Would force a complete rebuild of every derived file whenever the
+operating system on which the build is performed (as reported by C<uname
+-s>) changes.
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 More on construction environments
+
+As previously mentioned, a B<construction environment> is an object that
+has a set of keyword/value pairs and a set of methods, and which is used
+to tell Cons how target files should be built. This section describes
+how Cons uses and expands construction environment values to control its
+build behavior.
+
+=head2 Construction variable expansion
+
+Construction variables from a construction environment are expanded
+by preceding the keyword with a C<%> (percent sign):
+
+ Construction variables:
+ XYZZY => 'abracadabra',
+
+ The string: "The magic word is: %XYZZY!"
+ expands to: "The magic word is: abracadabra!"
+
+A construction variable name may be surrounded by C<{> and C<}> (curly
+braces), which are stripped as part of the expansion. This can
+sometimes be necessary to separate a variable expansion from trailing
+alphanumeric characters:
+
+ Construction variables:
+ OPT => 'value1',
+ OPTION => 'value2',
+
+ The string: "%OPT %{OPT}ION %OPTION %{OPTION}"
+ expands to: "value1 value1ION value2 value2"
+
+Construction variable expansion is recursive, that is, a string
+containing C<%->expansions after substitution will be re-expanded until
+no further substitutions can be made:
+
+ Construction variables:
+ STRING => 'The result is: %FOO',
+ FOO => '%BAR',
+ BAR => 'final value',
+
+ The string: "The string says: %STRING"
+ expands to: "The string says: The result is: final value"
+
+If a construction variable is not defined in an environment, then the
+null string is substituted:
+
+ Construction variables:
+ FOO => 'value1',
+ BAR => 'value2',
+
+ The string: "%FOO <%NO_VARIABLE> %BAR"
+ expands to: "value1 <> value2"
+
+A doubled C<%%> will be replaced by a single C<%>:
+
+ The string: "Here is a percent sign: %%"
+ expands to: "Here is a percent sign: %"
+
+=head2 Default construction variables
+
+When you specify no arguments when creating a new construction
+environment:
+
+ $env = new cons();
+
+Cons creates a reference to a new, default construction
+environment. This contains a number of construction variables and some
+methods. At the present writing, the default construction variables on a
+UNIX system are:
+
+ CC => 'cc',
+ CFLAGS => '',
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ CXX => '%CC',
+ CXXFLAGS => '%CFLAGS',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>',
+ INCDIRPREFIX => '-I',
+ INCDIRSUFFIX => '',
+ LINK => '%CXX',
+ LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD -r -o %> %<',
+ LIBDIRPREFIX => '-L',
+ LIBDIRSUFFIX => '',
+ AR => 'ar',
+ ARFLAGS => 'r',
+ ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'],
+ RANLIB => 'ranlib',
+ AS => 'as',
+ ASFLAGS => '',
+ ASCOM => '%AS %ASFLAGS %< -o %>',
+ LD => 'ld',
+ LDFLAGS => '',
+ PREFLIB => 'lib',
+ SUFLIB => '.a',
+ SUFLIBS => '.so:.a',
+ SUFOBJ => '.o',
+ SIGNATURE => [ '*' => 'build' ],
+ ENV => { 'PATH' => '/bin:/usr/bin' },
+
+
+And on a Windows system (Windows NT), the default construction variables
+are (unless the default rule style is set using the B<DefaultRules>
+method):
+
+ CC => 'cl',
+ CFLAGS => '/nologo',
+ CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>',
+ INCDIRPREFIX => '/I',
+ INCDIRSUFFIX => '',
+ LINK => 'link',
+ LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD /r /o %> %<',
+ LIBDIRPREFIX => '/LIBPATH:',
+ LIBDIRSUFFIX => '',
+ AR => 'lib',
+ ARFLAGS => '/nologo ',
+ ARCOM => "%AR %ARFLAGS /out:%> %<",
+ RANLIB => '',
+ LD => 'link',
+ LDFLAGS => '/nologo ',
+ PREFLIB => '',
+ SUFEXE => '.exe',
+ SUFLIB => '.lib',
+ SUFLIBS => '.dll:.lib',
+ SUFOBJ => '.obj',
+ SIGNATURE => [ '*' => 'build' ],
+
+These variables are used by the various methods associated with the
+environment. In particular, any method that ultimately invokes an external
+command will substitute these variables into the final command, as
+appropriate. For example, the C<Objects> method takes a number of source
+files and arranges to derive, if necessary, the corresponding object
+files:
+
+ Objects $env 'foo.c', 'bar.c';
+
+This will arrange to produce, if necessary, F<foo.o> and F<bar.o>. The
+command invoked is simply C<%CCCOM>, which expands, through substitution,
+to the appropriate external command required to build each object. The
+substitution rules will be discussed in detail in the next section.
+
+The construction variables are also used for other purposes. For example,
+C<CPPPATH> is used to specify a colon-separated path of include
+directories. These are intended to be passed to the C preprocessor and are
+also used by the C-file scanning machinery to determine the dependencies
+involved in a C Compilation.
+
+Variables beginning with underscore are created by various methods,
+and should normally be considered ``internal'' variables. For example,
+when a method is called which calls for the creation of an object from
+a C source, the variable C<_IFLAGS> is created: this corresponds to the
+C<-I> switches required by the C compiler to represent the directories
+specified by C<CPPPATH>.
+
+Note that, for any particular environment, the value of a variable is set
+once, and then never reset (to change a variable, you must create a new
+environment. Methods are provided for copying existing environments for this
+purpose). Some internal variables, such as C<_IFLAGS> are created on demand,
+but once set, they remain fixed for the life of the environment.
+
+The C<CFLAGS>, C<LDFLAGS>, and C<ARFLAGS> variables all supply a place
+for passing options to the compiler, loader, and archiver, respectively.
+
+The C<INCDIRPREFIX> and C<INCDIRSUFFIX> variables specify option
+strings to be appended to the beginning and end, respectively, of each
+include directory so that the compiler knows where to find F<.h> files.
+Similarly, the C<LIBDIRPREFIX> and C<LIBDIRSUFFIX> variables specify the
+option string to be appended to the beginning of and end, respectively,
+of each directory that the linker should search for libraries.
+
+Another variable, C<ENV>, is used to determine the system environment during
+the execution of an external command. By default, the only environment
+variable that is set is C<PATH>, which is the execution path for a UNIX
+command. For the utmost reproducibility, you should really arrange to set
+your own execution path, in your top-level F<Construct> file (or perhaps by
+importing an appropriate construction package with the Perl C<use>
+command). The default variables are intended to get you off the ground.
+
+=head2 Expanding variables in construction commands
+
+Within a construction command, construction variables will be expanded
+according to the rules described above. In addition to normal variable
+expansion from the construction environment, construction commands also
+expand the following pseudo-variables to insert the specific input and
+output files in the command line that will be executed:
+
+=over 10
+
+=item %>
+
+The target file name. In a multi-target command, this expands to the
+first target mentioned.)
+
+=item %0
+
+Same as C<%E<gt>>.
+
+=item %1, %2, ..., %9
+
+These refer to the first through ninth input file, respectively.
+
+=item %E<lt>
+
+The full set of input file names. If any of these have been used
+anywhere else in the current command line (via C<%1>, C<%2>, etc.), then
+those will be deleted from the list provided by C<%E<lt>>. Consider the
+following command found in a F<Conscript> file in the F<test> directory:
+
+ Command $env 'tgt', qw(foo bar baz), qq(
+ echo %< -i %1 > %>
+ echo %< -i %2 >> %>
+ echo %< -i %3 >> %>
+ );
+
+If F<tgt> needed to be updated, then this would result in the execution of
+the following commands, assuming that no remapping has been established for
+the F<test> directory:
+
+ echo test/bar test/baz -i test/foo > test/tgt
+ echo test/foo test/baz -i test/bar >> test/tgt
+ echo test/foo test/bar -i test/baz >> test/tgt
+
+=back
+
+Any of the above pseudo-variables may be followed immediately by one of
+the following suffixes to select a portion of the expanded path name:
+
+ :a the absolute path to the file name
+ :b the directory plus the file name stripped of any suffix
+ :d the directory
+ :f the file name
+ :s the file name suffix
+ :F the file name stripped of any suffix
+ :S the absolute path path to a Linked source file
+
+Continuing with the above example, C<%E<lt>:f> would expand to C<foo bar baz>,
+and C<%E<gt>:d> would expand to C<test>.
+
+There are additional C<%> elements which affect the command line(s):
+
+=over 10
+
+=item %[ %]
+
+It is possible to programmatically rewrite part of the command by
+enclosing part of it between C<%[> and C<%]>. This will call the
+construction variable named as the first word enclosed in the brackets
+as a Perl code reference; the results of this call will be used to
+replace the contents of the brackets in the command line. For example,
+given an existing input file named F<tgt.in>:
+
+ @keywords = qw(foo bar baz);
+ $env = new cons(X_COMMA => sub { join(",", @_) });
+ Command $env 'tgt', 'tgt.in', qq(
+ echo '# Keywords: %[X_COMMA @keywords %]' > %>
+ cat %< >> %>
+ );
+
+This will execute:
+
+ echo '# Keywords: foo,bar,baz' > tgt
+ cat tgt.in >> tgt
+
+=item %( %)
+
+Cons includes the text of the command line in the MD5 signature for a
+build, so that targets get rebuilt if you change the command line (to
+add or remove an option, for example). Command-line text in between
+C<%(> and C<%)>, however, will be ignored for MD5 signature calculation.
+
+Internally, Cons uses C<%(> and C<%)> around include and library
+directory options (C<-I> and C<-L> on UNIX systems, C</I> and
+C</LIBPATH> on Windows NT) to avoid rebuilds just because the directory
+list changes. Rebuilds occur only if the changed directory list causes
+any included I<files> to change, and a changed include file is detected
+by the MD5 signature calculation on the actual file contents.
+
+=back
+
+XXX DESCRIBE THE Literal() FUNCTION, TOO XXX
+
+=head2 Expanding construction variables in file names
+
+Cons expands construction variables in the source and target file names
+passed to the various construction methods according to the expansion
+rules described above:
+
+ $env = new cons(
+ DESTDIR => 'programs',
+ SRCDIR => 'src',
+ );
+ Program $env '%DESTDIR/hello', '%SRCDIR/hello.c';
+
+This allows for flexible configuration, through the construction
+environment, of directory names, suffixes, etc.
+
+-->
+
+ <para>
+
+ An <literal>environment</literal>
+ 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:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>External Environment</term>
+
+ <listitem>
+ <para>
+
+ The <literal>external environment</literal>
+ 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 <literal>os.environ</literal> dictionary.
+ See <xref linkend="sect-external-environments"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>&ConsEnv;</term>
+
+ <listitem>
+ <para>
+
+ 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 <xref linkend="sect-construction-environments"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Execution Environment</term>
+
+ <listitem>
+ <para>
+
+ An <literal>execution environment</literal>
+ 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 <literal>external environment</literal>
+ (see above).
+ See <xref linkend="sect-execution-environments"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis>
+ 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.
+
+ </para>
+
+ <section id="sect-external-environments">
+ <title>Using Values From the External Environment</title>
+
+ <para>
+
+ The <literal>external environment</literal>
+ variable settings that
+ the user has in force
+ when executing &SCons;
+ are available through the normal Python
+ <envar>os.environ</envar>
+ dictionary.
+ This means that you must add an
+ <literal>import os</literal> statement
+ to any &SConscript; file
+ in which you want to use
+ values from the user's external environment.
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ import os
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ More usefully, you can use the
+ <envar>os.environ</envar>
+ dictionary in your &SConscript;
+ files to initialize &consenvs;
+ with values from the user's external environment.
+ See the next section,
+ <xref linkend="sect-construction-environments"></xref>,
+ for information on how to do this.
+
+ </para>
+
+ </section>
+
+ <section id="sect-construction-environments">
+ <title>Construction Environments</title>
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <section>
+ <title>Creating a &ConsEnv;: the &Environment; Function</title>
+
+ <para>
+
+ A &consenv; is created by the &Environment; method:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ </sconstruct>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment(CC = 'gcc',
+ CCFLAGS = '-O2')
+
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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 <literal>-O2</literal>
+ (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:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Fetching Values From a &ConsEnv;</title>
+
+ <para>
+
+ You can fetch individual construction variables
+ using the normal syntax
+ for accessing individual named items in a Python dictionary:
+
+ </para>
+
+ <scons_example name="ex6">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ print "CC is:", env['CC']
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ex6">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex6b">
+ <file name="SConstruct" printme="1">
+ env = Environment(FOO = 'foo', BAR = 'bar')
+ dict = env.Dictionary()
+ for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']:
+ print "key = %s, value = %s" % (key, dict[key])
+ </file>
+ </scons_Example>
+
+ <para>
+
+ This &SConstruct; file
+ will print the specified dictionary items for us on POSIX
+ systems as follows:
+
+ </para>
+
+ <scons_output example="ex6b" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And on Windows:
+
+ </para>
+
+ <scons_output example="ex6b" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ dict = env.Dictionary()
+ keys = dict.keys()
+ keys.sort()
+ for key in keys:
+ print "construction variable = '%s', value = '%s'" % (key, dict[key])
+ </sconstruct>
+
+ </section>
+
+ <section>
+ <title>Expanding Values From a &ConsEnv;: the &subst; Method</title>
+
+ <para>
+
+ Another way to get information from
+ a construction environment.
+ is to use the &subst; method
+ on a string containing <literal>$</literal> expansions
+ of construction variable names.
+ As a simple example,
+ the example from the previous
+ section that used
+ <literal>env['CC']</literal>
+ to fetch the value of &cv-link-CC;
+ could also be written as:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ print "CC is:", env.subst('$CC')
+ </sconstruct>
+
+ <para>
+
+ 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;:
+
+ </para>
+
+ <sconstruct>
+ env = Environment(CCFLAGS = '-DFOO')
+ print "CCCOM is:", env['CCCOM']
+ </sconstruct>
+
+ <para>
+
+ Will print the unexpanded value of &cv-CCCOM;,
+ showing us the construction
+ variables that still need to be expanded:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Calling the &subst; method on <varname>$CCOM</varname>,
+ however:
+
+ </para>
+
+ <sconstruct>
+ env = Environment(CCFLAGS = '-DFOO')
+ print "CCCOM is:", env.subst('$CCCOM')
+ </sconstruct>
+
+ <para>
+
+ Will recursively expand all of
+ the construction variables prefixed
+ with <literal>$</literal> (dollar signs),
+ showing us the final output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ CCCOM is: gcc -DFOO -c -o
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ You can, however, control the settings
+ in the default contstruction environment
+ by using the &DefaultEnvironment; function
+ to initialize various settings:
+
+ </para>
+
+ <sconstruct>
+
+ DefaultEnvironment(CC = '/usr/local/bin/gcc')
+
+ </sconstruct>
+
+ <para>
+
+ When configured as above,
+ all calls to the &Program;
+ or &Object; Builder
+ will build object files with the
+ <filename>/usr/local/bin/gcc</filename>
+ compiler.
+
+ </para>
+
+ <para>
+
+ 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 <filename>/usr/local/bin/gcc</filename>
+ but as a separate step after
+ the default construction environment has been initialized:
+
+ </para>
+
+ <sconstruct>
+
+ env = DefaultEnvironment()
+ env['CC'] = '/usr/local/bin/gcc'
+
+ </sconstruct>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+
+ env = DefaultEnvironment(tools = ['gcc', 'gnulink'],
+ CC = '/usr/local/bin/gcc')
+
+ </sconstruct>
+
+ <para>
+
+ 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
+ <filename>/usr/local/bin/gcc</filename>.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Multiple &ConsEnvs;</title>
+
+ <para>
+
+ 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 <literal>-O2</literal> flag
+ and another with the <literal>-g</literal> (debug) flag,
+ we would do this like so:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ opt = Environment(CCFLAGS = '-O2')
+ dbg = Environment(CCFLAGS = '-g')
+
+ opt.Program('foo', 'foo.c')
+
+ dbg.Program('bar', 'bar.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ <file name="bar.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct" printme="1">
+ opt = Environment(CCFLAGS = '-O2')
+ dbg = Environment(CCFLAGS = '-g')
+
+ opt.Program('foo', 'foo.c')
+
+ dbg.Program('foo', 'foo.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then &SCons; generates the following error:
+
+ </para>
+
+ <scons_output example="ex3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ This is because the two &b-Program; calls have
+ each implicitly told &SCons; to generate an object file named
+ <filename>foo.o</filename>,
+ one with a &cv-link-CCFLAGS; value of
+ <literal>-O2</literal>
+ and one with a &cv-link-CCFLAGS; value of
+ <literal>-g</literal>.
+ &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
+ <filename>foo.c</filename>
+ to a separately-named object file
+ using the &b-link-Object; builder, like so:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ex4">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Making Copies of &ConsEnvs;: the &Clone; Method</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then our output would look like:
+
+ </para>
+
+ <scons_output example="ex5">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Replacing Values: the &Replace; Method</title>
+
+ <para>
+
+ You can replace existing construction variable values
+ using the &Replace; method:
+
+ </para>
+
+ <scons_example name="Replace1">
+ <file name="SConstruct" printme="1">
+ env = Environment(CCFLAGS = '-DDEFINE1')
+ env.Replace(CCFLAGS = '-DDEFINE2')
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ The replacing value
+ (<literal>-DDEFINE2</literal> in the above example)
+ completely replaces the value in the
+ construction environment:
+
+ </para>
+
+ <scons_output example="Replace1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can safely call &Replace;
+ for construction variables that
+ don't exist in the construction environment:
+
+ </para>
+
+ <scons_example name="Replace-nonexistent">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Replace(NEW_VARIABLE = 'xyzzy')
+ print "NEW_VARIABLE =", env['NEW_VARIABLE']
+ </file>
+ </scons_example>
+
+ <para>
+
+ In this case,
+ the construction variable simply
+ gets added to the construction environment:
+
+ </para>
+
+ <scons_output example="Replace-nonexistent">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Replace2">
+ <file name="SConstruct" printme="1">
+ env = Environment(CCFLAGS = '-DDEFINE1')
+ print "CCFLAGS =", env['CCFLAGS']
+ env.Program('foo.c')
+
+ env.Replace(CCFLAGS = '-DDEFINE2')
+ print "CCFLAGS =", env['CCFLAGS']
+ env.Program('bar.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ <file name="bar.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ The timing of when the replacement
+ actually occurs relative
+ to when the targets get built
+ becomes apparent
+ if we run &scons; without the <literal>-Q</literal>
+ option:
+
+ </para>
+
+ <scons_output example="Replace2">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Because the replacement occurs while
+ the &SConscript; files are being read,
+ the &cv-link-CCFLAGS;
+ variable has already been set to
+ <literal>-DDEFINE2</literal>
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Setting Values Only If They're Not Already Defined: the &SetDefault; Method</title>
+
+ <para>
+
+ 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 <function>set_default</function>
+ method of Python dictionary objects:
+
+ </para>
+
+ <sconstruct>
+ env.SetDefault(SPECIAL_FLAG = '-extra-option')
+ </sconstruct>
+
+ <para>
+
+ This is especially useful
+ when writing your own <literal>Tool</literal> modules
+ to apply variables to construction environments.
+ <!--
+ See <xref linkend="chap-tool-modules"></xref>
+ for more information about writing
+ Tool modules.
+ -->
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Appending to the End of Values: the &Append; Method</title>
+
+ <para>
+
+ You can append a value to
+ an existing construction variable
+ using the &Append; method:
+
+ </para>
+
+ <scons_example name="ex8">
+ <file name="SConstruct" printme="1">
+ env = Environment(CCFLAGS = ['-DMY_VALUE'])
+ env.Append(CCFLAGS = ['-DLAST'])
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; then supplies both the <literal>-DMY_VALUE</literal> and
+ <literal>-DLAST</literal> flags when compiling the object file:
+
+ </para>
+
+ <scons_output example="ex8">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If the construction variable doesn't already exist,
+ the &Append; method will create it:
+
+ </para>
+
+ <scons_example name="Append-nonexistent">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Append(NEW_VARIABLE = 'added')
+ print "NEW_VARIABLE =", env['NEW_VARIABLE']
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which yields:
+
+ </para>
+
+ <scons_output example="Append-nonexistent">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Appending Unique Values: the &AppendUnique; Method</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ env.AppendUnique(CCFLAGS=['-g'])
+ </sconstruct>
+
+ <para>
+
+ In the above example,
+ the <literal>-g</literal> would be added
+ only if the &cv-CCFLAGS; variable
+ does not already contain a <literal>-g</literal> value.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Appending to the Beginning of Values: the &Prepend; Method</title>
+
+ <para>
+
+ You can append a value to the beginning of
+ an existing construction variable
+ using the &Prepend; method:
+
+ </para>
+
+ <scons_example name="ex9">
+ <file name="SConstruct" printme="1">
+ env = Environment(CCFLAGS = ['-DMY_VALUE'])
+ env.Prepend(CCFLAGS = ['-DFIRST'])
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ int main() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; then supplies both the <literal>-DFIRST</literal> and
+ <literal>-DMY_VALUE</literal> flags when compiling the object file:
+
+ </para>
+
+ <scons_output example="ex9">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If the construction variable doesn't already exist,
+ the &Prepend; method will create it:
+
+ </para>
+
+ <scons_example name="Prepend-nonexistent">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Prepend(NEW_VARIABLE = 'added')
+ print "NEW_VARIABLE =", env['NEW_VARIABLE']
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which yields:
+
+ </para>
+
+ <scons_output example="Prepend-nonexistent">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Prepending Unique Values: the &PrependUnique; Method</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ env.PrependUnique(CCFLAGS=['-g'])
+ </sconstruct>
+
+ <para>
+
+ In the above example,
+ the <literal>-g</literal> would be added
+ only if the &cv-CCFLAGS; variable
+ does not already contain a <literal>-g</literal> value.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section id="sect-execution-environments">
+ <title>Controlling the Execution Environment for Issued Commands</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ The default value of the &PATH; environment variable
+ on a POSIX system
+ is <literal>/usr/local/bin:/bin:/usr/bin</literal>.
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ path = ['/usr/local/bin', '/bin', '/usr/bin']
+ env = Environment(ENV = {'PATH' : path})
+ </sconstruct>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin']
+ </sconstruct>
+
+ <para>
+
+ 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):
+
+ </para>
+
+ <sconstruct>
+ env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin'
+ </sconstruct>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <!--
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Command('foo', [], '__ROOT__/usr/bin/printenv.py')
+ </file>
+ <file name="__ROOT__/usr/bin/printenv.py" chmod="0755">
+ #!/usr/bin/env python
+ import os
+ import sys
+ if len(sys.argv) > 1:
+ keys = sys.argv[1:]
+ else:
+ keys = os.environ.keys()
+ keys.sort()
+ for key in keys:
+ print " " + key + "=" + os.environ[key]
+ </file>
+ </scons_example>
+
+ <para>
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <section>
+ <title>Propagating &PATH; From the External Environment</title>
+
+ <para>
+
+ 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 <literal>os.environ</literal>
+ dictionary,
+ which is Python's way of letting you
+ get at the external environment:
+
+ </para>
+
+ <sconstruct>
+ import os
+ env = Environment(ENV = {'PATH' : os.environ['PATH']})
+ </sconstruct>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ import os
+ env = Environment(ENV = os.environ)
+ </sconstruct>
+
+ <para>
+
+ 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 <literal>/bin</literal> and
+ <literal>/usr/local/bin</literal> 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Adding to <varname>PATH</varname> Values in the Execution Environment</title>
+
+ <para>
+
+ 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 <envar>$PATH</envar> variable on Linux or POSIX systems,
+ or the <envar>%PATH%</envar> 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 <filename>/usr/local</filename> directories
+ to the <envar>$PATH</envar> and <envar>$LIB</envar> variables,
+ you might:
+
+ </para>
+
+ <sconstruct>
+ env = Environment(ENV = os.environ)
+ env.PrependENVPath('PATH', '/usr/local/bin')
+ env.AppendENVPath('LIB', '/usr/local/lib')
+ </sconstruct>
+
+ <para>
+
+ Note that the added values are strings,
+ and if you want to add multiple directories to
+ a variable like <envar>$PATH</envar>,
+ you must include the path separate character
+ (<literal>:</literal> on Linux or POSIX,
+ <literal>;</literal> on Windows)
+ in the string.
+
+ </para>
+
+ </section>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 More on construction environments
+
+As previously mentioned, a B<construction environment> is an object that
+has a set of keyword/value pairs and a set of methods, and which is used
+to tell Cons how target files should be built. This section describes
+how Cons uses and expands construction environment values to control its
+build behavior.
+
+=head2 Construction variable expansion
+
+Construction variables from a construction environment are expanded
+by preceding the keyword with a C<%> (percent sign):
+
+ Construction variables:
+ XYZZY => 'abracadabra',
+
+ The string: "The magic word is: %XYZZY!"
+ expands to: "The magic word is: abracadabra!"
+
+A construction variable name may be surrounded by C<{> and C<}> (curly
+braces), which are stripped as part of the expansion. This can
+sometimes be necessary to separate a variable expansion from trailing
+alphanumeric characters:
+
+ Construction variables:
+ OPT => 'value1',
+ OPTION => 'value2',
+
+ The string: "%OPT %{OPT}ION %OPTION %{OPTION}"
+ expands to: "value1 value1ION value2 value2"
+
+Construction variable expansion is recursive, that is, a string
+containing C<%->expansions after substitution will be re-expanded until
+no further substitutions can be made:
+
+ Construction variables:
+ STRING => 'The result is: %FOO',
+ FOO => '%BAR',
+ BAR => 'final value',
+
+ The string: "The string says: %STRING"
+ expands to: "The string says: The result is: final value"
+
+If a construction variable is not defined in an environment, then the
+null string is substituted:
+
+ Construction variables:
+ FOO => 'value1',
+ BAR => 'value2',
+
+ The string: "%FOO <%NO_VARIABLE> %BAR"
+ expands to: "value1 <> value2"
+
+A doubled C<%%> will be replaced by a single C<%>:
+
+ The string: "Here is a percent sign: %%"
+ expands to: "Here is a percent sign: %"
+
+=head2 Default construction variables
+
+When you specify no arguments when creating a new construction
+environment:
+
+ $env = new cons();
+
+Cons creates a reference to a new, default construction
+environment. This contains a number of construction variables and some
+methods. At the present writing, the default construction variables on a
+UNIX system are:
+
+ CC => 'cc',
+ CFLAGS => '',
+ CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+ CXX => '%CC',
+ CXXFLAGS => '%CFLAGS',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>',
+ INCDIRPREFIX => '-I',
+ INCDIRSUFFIX => '',
+ LINK => '%CXX',
+ LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD -r -o %> %<',
+ LIBDIRPREFIX => '-L',
+ LIBDIRSUFFIX => '',
+ AR => 'ar',
+ ARFLAGS => 'r',
+ ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'],
+ RANLIB => 'ranlib',
+ AS => 'as',
+ ASFLAGS => '',
+ ASCOM => '%AS %ASFLAGS %< -o %>',
+ LD => 'ld',
+ LDFLAGS => '',
+ PREFLIB => 'lib',
+ SUFLIB => '.a',
+ SUFLIBS => '.so:.a',
+ SUFOBJ => '.o',
+ SIGNATURE => [ '*' => 'build' ],
+ ENV => { 'PATH' => '/bin:/usr/bin' },
+
+
+And on a Windows system (Windows NT), the default construction variables
+are (unless the default rule style is set using the B<DefaultRules>
+method):
+
+ CC => 'cl',
+ CFLAGS => '/nologo',
+ CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>',
+ CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>',
+ INCDIRPREFIX => '/I',
+ INCDIRSUFFIX => '',
+ LINK => 'link',
+ LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS',
+ LINKMODULECOM => '%LD /r /o %> %<',
+ LIBDIRPREFIX => '/LIBPATH:',
+ LIBDIRSUFFIX => '',
+ AR => 'lib',
+ ARFLAGS => '/nologo ',
+ ARCOM => "%AR %ARFLAGS /out:%> %<",
+ RANLIB => '',
+ LD => 'link',
+ LDFLAGS => '/nologo ',
+ PREFLIB => '',
+ SUFEXE => '.exe',
+ SUFLIB => '.lib',
+ SUFLIBS => '.dll:.lib',
+ SUFOBJ => '.obj',
+ SIGNATURE => [ '*' => 'build' ],
+
+These variables are used by the various methods associated with the
+environment. In particular, any method that ultimately invokes an external
+command will substitute these variables into the final command, as
+appropriate. For example, the C<Objects> method takes a number of source
+files and arranges to derive, if necessary, the corresponding object
+files:
+
+ Objects $env 'foo.c', 'bar.c';
+
+This will arrange to produce, if necessary, F<foo.o> and F<bar.o>. The
+command invoked is simply C<%CCCOM>, which expands, through substitution,
+to the appropriate external command required to build each object. The
+substitution rules will be discussed in detail in the next section.
+
+The construction variables are also used for other purposes. For example,
+C<CPPPATH> is used to specify a colon-separated path of include
+directories. These are intended to be passed to the C preprocessor and are
+also used by the C-file scanning machinery to determine the dependencies
+involved in a C Compilation.
+
+Variables beginning with underscore are created by various methods,
+and should normally be considered ``internal'' variables. For example,
+when a method is called which calls for the creation of an object from
+a C source, the variable C<_IFLAGS> is created: this corresponds to the
+C<-I> switches required by the C compiler to represent the directories
+specified by C<CPPPATH>.
+
+Note that, for any particular environment, the value of a variable is set
+once, and then never reset (to change a variable, you must create a new
+environment. Methods are provided for copying existing environments for this
+purpose). Some internal variables, such as C<_IFLAGS> are created on demand,
+but once set, they remain fixed for the life of the environment.
+
+The C<CFLAGS>, C<LDFLAGS>, and C<ARFLAGS> variables all supply a place
+for passing options to the compiler, loader, and archiver, respectively.
+
+The C<INCDIRPREFIX> and C<INCDIRSUFFIX> variables specify option
+strings to be appended to the beginning and end, respectively, of each
+include directory so that the compiler knows where to find F<.h> files.
+Similarly, the C<LIBDIRPREFIX> and C<LIBDIRSUFFIX> variables specify the
+option string to be appended to the beginning of and end, respectively,
+of each directory that the linker should search for libraries.
+
+Another variable, C<ENV>, is used to determine the system environment during
+the execution of an external command. By default, the only environment
+variable that is set is C<PATH>, which is the execution path for a UNIX
+command. For the utmost reproducibility, you should really arrange to set
+your own execution path, in your top-level F<Construct> file (or perhaps by
+importing an appropriate construction package with the Perl C<use>
+command). The default variables are intended to get you off the ground.
+
+=head2 Expanding variables in construction commands
+
+Within a construction command, construction variables will be expanded
+according to the rules described above. In addition to normal variable
+expansion from the construction environment, construction commands also
+expand the following pseudo-variables to insert the specific input and
+output files in the command line that will be executed:
+
+=over 10
+
+=item %>
+
+The target file name. In a multi-target command, this expands to the
+first target mentioned.)
+
+=item %0
+
+Same as C<%E<gt>>.
+
+=item %1, %2, ..., %9
+
+These refer to the first through ninth input file, respectively.
+
+=item %E<lt>
+
+The full set of input file names. If any of these have been used
+anywhere else in the current command line (via C<%1>, C<%2>, etc.), then
+those will be deleted from the list provided by C<%E<lt>>. Consider the
+following command found in a F<Conscript> file in the F<test> directory:
+
+ Command $env 'tgt', qw(foo bar baz), qq(
+ echo %< -i %1 > %>
+ echo %< -i %2 >> %>
+ echo %< -i %3 >> %>
+ );
+
+If F<tgt> needed to be updated, then this would result in the execution of
+the following commands, assuming that no remapping has been established for
+the F<test> directory:
+
+ echo test/bar test/baz -i test/foo > test/tgt
+ echo test/foo test/baz -i test/bar >> test/tgt
+ echo test/foo test/bar -i test/baz >> test/tgt
+
+=back
+
+Any of the above pseudo-variables may be followed immediately by one of
+the following suffixes to select a portion of the expanded path name:
+
+ :a the absolute path to the file name
+ :b the directory plus the file name stripped of any suffix
+ :d the directory
+ :f the file name
+ :s the file name suffix
+ :F the file name stripped of any suffix
+ :S the absolute path path to a Linked source file
+
+Continuing with the above example, C<%E<lt>:f> would expand to C<foo bar baz>,
+and C<%E<gt>:d> would expand to C<test>.
+
+There are additional C<%> elements which affect the command line(s):
+
+=over 10
+
+=item %[ %]
+
+It is possible to programmatically rewrite part of the command by
+enclosing part of it between C<%[> and C<%]>. This will call the
+construction variable named as the first word enclosed in the brackets
+as a Perl code reference; the results of this call will be used to
+replace the contents of the brackets in the command line. For example,
+given an existing input file named F<tgt.in>:
+
+ @keywords = qw(foo bar baz);
+ $env = new cons(X_COMMA => sub { join(",", @_) });
+ Command $env 'tgt', 'tgt.in', qq(
+ echo '# Keywords: %[X_COMMA @keywords %]' > %>
+ cat %< >> %>
+ );
+
+This will execute:
+
+ echo '# Keywords: foo,bar,baz' > tgt
+ cat tgt.in >> tgt
+
+=item %( %)
+
+Cons includes the text of the command line in the MD5 signature for a
+build, so that targets get rebuilt if you change the command line (to
+add or remove an option, for example). Command-line text in between
+C<%(> and C<%)>, however, will be ignored for MD5 signature calculation.
+
+Internally, Cons uses C<%(> and C<%)> around include and library
+directory options (C<-I> and C<-L> on UNIX systems, C</I> and
+C</LIBPATH> on Windows NT) to avoid rebuilds just because the directory
+list changes. Rebuilds occur only if the changed directory list causes
+any included I<files> to change, and a changed include file is detected
+by the MD5 signature calculation on the actual file contents.
+
+=back
+
+XXX DESCRIBE THE Literal() FUNCTION, TOO XXX
+
+=head2 Expanding construction variables in file names
+
+Cons expands construction variables in the source and target file names
+passed to the various construction methods according to the expansion
+rules described above:
+
+ $env = new cons(
+ DESTDIR => 'programs',
+ SRCDIR => 'src',
+ );
+ Program $env '%DESTDIR/hello', '%SRCDIR/hello.c';
+
+This allows for flexible configuration, through the construction
+environment, of directory names, suffixes, etc.
+
+-->
+
+ <para>
+
+ An <literal>environment</literal>
+ 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:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>External Environment</term>
+
+ <listitem>
+ <para>
+
+ The <literal>external environment</literal>
+ 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 <literal>os.environ</literal> dictionary.
+ See <xref linkend="sect-external-environments"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>&ConsEnv;</term>
+
+ <listitem>
+ <para>
+
+ 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 <xref linkend="sect-construction-environments"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Execution Environment</term>
+
+ <listitem>
+ <para>
+
+ An <literal>execution environment</literal>
+ 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 <literal>external environment</literal>
+ (see above).
+ See <xref linkend="sect-execution-environments"></xref>, below.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis>
+ 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.
+
+ </para>
+
+ <section id="sect-external-environments">
+ <title>Using Values From the External Environment</title>
+
+ <para>
+
+ The <literal>external environment</literal>
+ variable settings that
+ the user has in force
+ when executing &SCons;
+ are available through the normal Python
+ <envar>os.environ</envar>
+ dictionary.
+ This means that you must add an
+ <literal>import os</literal> statement
+ to any &SConscript; file
+ in which you want to use
+ values from the user's external environment.
+
+ </para>
+
+ <programlisting>
+ import os
+ </programlisting>
+
+ <para>
+
+ More usefully, you can use the
+ <envar>os.environ</envar>
+ dictionary in your &SConscript;
+ files to initialize &consenvs;
+ with values from the user's external environment.
+ See the next section,
+ <xref linkend="sect-construction-environments"></xref>,
+ for information on how to do this.
+
+ </para>
+
+ </section>
+
+ <section id="sect-construction-environments">
+ <title>Construction Environments</title>
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <section>
+ <title>Creating a &ConsEnv;: the &Environment; Function</title>
+
+ <para>
+
+ A &consenv; is created by the &Environment; method:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ import os
+
+ env = Environment(CC = 'gcc',
+ CCFLAGS = '-O2')
+
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ 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 <literal>-O2</literal>
+ (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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ gcc -o foo.o -c -O2 foo.c
+ gcc -o foo foo.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Fetching Values From a &ConsEnv;</title>
+
+ <para>
+
+ You can fetch individual construction variables
+ using the normal syntax
+ for accessing individual named items in a Python dictionary:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ print "CC is:", env['CC']
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ CC is: cc
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(FOO = 'foo', BAR = 'bar')
+ dict = env.Dictionary()
+ for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']:
+ print "key = %s, value = %s" % (key, dict[key])
+ </programlisting>
+
+ <para>
+
+ This &SConstruct; file
+ will print the specified dictionary items for us on POSIX
+ systems as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ key = OBJSUFFIX, value = .o
+ key = LIBSUFFIX, value = .a
+ key = PROGSUFFIX, value =
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ And on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ key = OBJSUFFIX, value = .obj
+ key = LIBSUFFIX, value = .lib
+ key = PROGSUFFIX, value = .exe
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ dict = env.Dictionary()
+ keys = dict.keys()
+ keys.sort()
+ for key in keys:
+ print "construction variable = '%s', value = '%s'" % (key, dict[key])
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Expanding Values From a &ConsEnv;: the &subst; Method</title>
+
+ <para>
+
+ Another way to get information from
+ a construction environment.
+ is to use the &subst; method
+ on a string containing <literal>$</literal> expansions
+ of construction variable names.
+ As a simple example,
+ the example from the previous
+ section that used
+ <literal>env['CC']</literal>
+ to fetch the value of &cv-link-CC;
+ could also be written as:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ print "CC is:", env.subst('$CC')
+ </programlisting>
+
+ <para>
+
+ 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;:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = '-DFOO')
+ print "CCCOM is:", env['CCCOM']
+ </programlisting>
+
+ <para>
+
+ Will print the unexpanded value of &cv-CCCOM;,
+ showing us the construction
+ variables that still need to be expanded:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Calling the &subst; method on <varname>$CCOM</varname>,
+ however:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = '-DFOO')
+ print "CCCOM is:", env.subst('$CCCOM')
+ </programlisting>
+
+ <para>
+
+ Will recursively expand all of
+ the construction variables prefixed
+ with <literal>$</literal> (dollar signs),
+ showing us the final output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ CCCOM is: gcc -DFOO -c -o
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ You can, however, control the settings
+ in the default contstruction environment
+ by using the &DefaultEnvironment; function
+ to initialize various settings:
+
+ </para>
+
+ <programlisting>
+
+ DefaultEnvironment(CC = '/usr/local/bin/gcc')
+
+ </programlisting>
+
+ <para>
+
+ When configured as above,
+ all calls to the &Program;
+ or &Object; Builder
+ will build object files with the
+ <filename>/usr/local/bin/gcc</filename>
+ compiler.
+
+ </para>
+
+ <para>
+
+ 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 <filename>/usr/local/bin/gcc</filename>
+ but as a separate step after
+ the default construction environment has been initialized:
+
+ </para>
+
+ <programlisting>
+
+ env = DefaultEnvironment()
+ env['CC'] = '/usr/local/bin/gcc'
+
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+
+ env = DefaultEnvironment(tools = ['gcc', 'gnulink'],
+ CC = '/usr/local/bin/gcc')
+
+ </programlisting>
+
+ <para>
+
+ 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
+ <filename>/usr/local/bin/gcc</filename>.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Multiple &ConsEnvs;</title>
+
+ <para>
+
+ 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 <literal>-O2</literal> flag
+ and another with the <literal>-g</literal> (debug) flag,
+ we would do this like so:
+
+ </para>
+
+ <programlisting>
+ opt = Environment(CCFLAGS = '-O2')
+ dbg = Environment(CCFLAGS = '-g')
+
+ opt.Program('foo', 'foo.c')
+
+ dbg.Program('bar', 'bar.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ opt = Environment(CCFLAGS = '-O2')
+ dbg = Environment(CCFLAGS = '-g')
+
+ opt.Program('foo', 'foo.c')
+
+ dbg.Program('foo', 'foo.c')
+ </programlisting>
+
+ <para>
+
+ Then &SCons; generates the following error:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+
+ 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
+ </screen>
+
+ <para>
+
+ This is because the two &b-Program; calls have
+ each implicitly told &SCons; to generate an object file named
+ <filename>foo.o</filename>,
+ one with a &cv-link-CCFLAGS; value of
+ <literal>-O2</literal>
+ and one with a &cv-link-CCFLAGS; value of
+ <literal>-g</literal>.
+ &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
+ <filename>foo.c</filename>
+ to a separately-named object file
+ using the &b-link-Object; builder, like so:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Making Copies of &ConsEnvs;: the &Clone; Method</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ Then our output would look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Replacing Values: the &Replace; Method</title>
+
+ <para>
+
+ You can replace existing construction variable values
+ using the &Replace; method:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = '-DDEFINE1')
+ env.Replace(CCFLAGS = '-DDEFINE2')
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ The replacing value
+ (<literal>-DDEFINE2</literal> in the above example)
+ completely replaces the value in the
+ construction environment:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o foo.o -c -DDEFINE2 foo.c
+ cc -o foo foo.o
+ </screen>
+
+ <para>
+
+ You can safely call &Replace;
+ for construction variables that
+ don't exist in the construction environment:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Replace(NEW_VARIABLE = 'xyzzy')
+ print "NEW_VARIABLE =", env['NEW_VARIABLE']
+ </programlisting>
+
+ <para>
+
+ In this case,
+ the construction variable simply
+ gets added to the construction environment:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ NEW_VARIABLE = xyzzy
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = '-DDEFINE1')
+ print "CCFLAGS =", env['CCFLAGS']
+ env.Program('foo.c')
+
+ env.Replace(CCFLAGS = '-DDEFINE2')
+ print "CCFLAGS =", env['CCFLAGS']
+ env.Program('bar.c')
+ </programlisting>
+
+ <para>
+
+ The timing of when the replacement
+ actually occurs relative
+ to when the targets get built
+ becomes apparent
+ if we run &scons; without the <literal>-Q</literal>
+ option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ Because the replacement occurs while
+ the &SConscript; files are being read,
+ the &cv-link-CCFLAGS;
+ variable has already been set to
+ <literal>-DDEFINE2</literal>
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Setting Values Only If They're Not Already Defined: the &SetDefault; Method</title>
+
+ <para>
+
+ 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 <function>set_default</function>
+ method of Python dictionary objects:
+
+ </para>
+
+ <programlisting>
+ env.SetDefault(SPECIAL_FLAG = '-extra-option')
+ </programlisting>
+
+ <para>
+
+ This is especially useful
+ when writing your own <literal>Tool</literal> modules
+ to apply variables to construction environments.
+ <!--
+ See <xref linkend="chap-tool-modules"></xref>
+ for more information about writing
+ Tool modules.
+ -->
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Appending to the End of Values: the &Append; Method</title>
+
+ <para>
+
+ You can append a value to
+ an existing construction variable
+ using the &Append; method:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = ['-DMY_VALUE'])
+ env.Append(CCFLAGS = ['-DLAST'])
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ &SCons; then supplies both the <literal>-DMY_VALUE</literal> and
+ <literal>-DLAST</literal> flags when compiling the object file:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o foo.o -c -DMY_VALUE -DLAST foo.c
+ cc -o foo foo.o
+ </screen>
+
+ <para>
+
+ If the construction variable doesn't already exist,
+ the &Append; method will create it:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Append(NEW_VARIABLE = 'added')
+ print "NEW_VARIABLE =", env['NEW_VARIABLE']
+ </programlisting>
+
+ <para>
+
+ Which yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ NEW_VARIABLE = added
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Appending Unique Values: the &AppendUnique; Method</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env.AppendUnique(CCFLAGS=['-g'])
+ </programlisting>
+
+ <para>
+
+ In the above example,
+ the <literal>-g</literal> would be added
+ only if the &cv-CCFLAGS; variable
+ does not already contain a <literal>-g</literal> value.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Appending to the Beginning of Values: the &Prepend; Method</title>
+
+ <para>
+
+ You can append a value to the beginning of
+ an existing construction variable
+ using the &Prepend; method:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCFLAGS = ['-DMY_VALUE'])
+ env.Prepend(CCFLAGS = ['-DFIRST'])
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ &SCons; then supplies both the <literal>-DFIRST</literal> and
+ <literal>-DMY_VALUE</literal> flags when compiling the object file:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o foo.o -c -DFIRST -DMY_VALUE foo.c
+ cc -o foo foo.o
+ </screen>
+
+ <para>
+
+ If the construction variable doesn't already exist,
+ the &Prepend; method will create it:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Prepend(NEW_VARIABLE = 'added')
+ print "NEW_VARIABLE =", env['NEW_VARIABLE']
+ </programlisting>
+
+ <para>
+
+ Which yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ NEW_VARIABLE = added
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Prepending Unique Values: the &PrependUnique; Method</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env.PrependUnique(CCFLAGS=['-g'])
+ </programlisting>
+
+ <para>
+
+ In the above example,
+ the <literal>-g</literal> would be added
+ only if the &cv-CCFLAGS; variable
+ does not already contain a <literal>-g</literal> value.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section id="sect-execution-environments">
+ <title>Controlling the Execution Environment for Issued Commands</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ The default value of the &PATH; environment variable
+ on a POSIX system
+ is <literal>/usr/local/bin:/bin:/usr/bin</literal>.
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ path = ['/usr/local/bin', '/bin', '/usr/bin']
+ env = Environment(ENV = {'PATH' : path})
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin']
+ </programlisting>
+
+ <para>
+
+ 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):
+
+ </para>
+
+ <programlisting>
+ env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin'
+ </programlisting>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <!--
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Command('foo', [], '__ROOT__/usr/bin/printenv.py')
+ </file>
+ <file name="__ROOT__/usr/bin/printenv.py" chmod="0755">
+ #!/usr/bin/env python
+ import os
+ import sys
+ if len(sys.argv) > 1:
+ keys = sys.argv[1:]
+ else:
+ keys = os.environ.keys()
+ keys.sort()
+ for key in keys:
+ print " " + key + "=" + os.environ[key]
+ </file>
+ </scons_example>
+
+ <para>
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <section>
+ <title>Propagating &PATH; From the External Environment</title>
+
+ <para>
+
+ 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 <literal>os.environ</literal>
+ dictionary,
+ which is Python's way of letting you
+ get at the external environment:
+
+ </para>
+
+ <programlisting>
+ import os
+ env = Environment(ENV = {'PATH' : os.environ['PATH']})
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ import os
+ env = Environment(ENV = os.environ)
+ </programlisting>
+
+ <para>
+
+ 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 <literal>/bin</literal> and
+ <literal>/usr/local/bin</literal> 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Adding to <varname>PATH</varname> Values in the Execution Environment</title>
+
+ <para>
+
+ 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 <envar>$PATH</envar> variable on Linux or POSIX systems,
+ or the <envar>%PATH%</envar> 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 <filename>/usr/local</filename> directories
+ to the <envar>$PATH</envar> and <envar>$LIB</envar> variables,
+ you might:
+
+ </para>
+
+ <programlisting>
+ env = Environment(ENV = os.environ)
+ env.PrependENVPath('PATH', '/usr/local/bin')
+ env.AppendENVPath('LIB', '/usr/local/lib')
+ </programlisting>
+
+ <para>
+
+ Note that the added values are strings,
+ and if you want to add multiple directories to
+ a variable like <envar>$PATH</envar>,
+ you must include the path separate character
+ (<literal>:</literal> on Linux or POSIX,
+ <literal>;</literal> on Windows)
+ in the string.
+
+ </para>
+
+ </section>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>XXX</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>XXX</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>XXX</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>XXX</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &SCons; provides a number of platform-independent functions,
+ called <literal>factories</literal>,
+ that perform common file system manipulations
+ like copying, moving or deleting files and directories,
+ or making directories.
+ These functions are <literal>factories</literal>
+ 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.
+
+ </para>
+
+ <section>
+ <title>Copying Files or Directories: The &Copy; Factory</title>
+
+ <para>
+
+ Suppose you want to arrange to make a copy of a file,
+ and don't have a suitable pre-existing builder.
+ <footnote>
+ <para>
+ 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.
+ </para>
+ </footnote>
+ One way would be to use the &Copy; action factory
+ in conjunction with the &Command; builder:
+
+ </para>
+
+ <scons_example name="Copy1">
+ <file name="SConstruct" printme="1">
+ Command("file.out", "file.in", Copy("$TARGET", "$SOURCE"))
+ </file>
+ <file name="file.in">file.in</file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="Copy1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can, of course, name a file explicitly
+ instead of using &cv-TARGET; or &cv-SOURCE;:
+
+ </para>
+
+ <scons_example name="Copy2">
+ <file name="SConstruct" printme="1">
+ Command("file.out", [], Copy("$TARGET", "file.in"))
+ </file>
+ <file name="file.in">file.in</file>
+ </scons_example>
+
+ <para>
+
+ Which executes as:
+
+ </para>
+
+ <scons_output example="Copy2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Copy3">
+ <file name="S" printme="1">
+ Command("file.out", "file.in",
+ [
+ Copy("tempfile", "$SOURCE"),
+ "modify tempfile",
+ Copy("$TARGET", "tempfile"),
+ ])
+ </file>
+ <file name="SConstruct">
+ env = DefaultEnvironment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ SConscript('S')
+ </file>
+ <file name="file.in">file.in</file>
+ <file name="modify" chmod="0755">
+ touch $*
+ </file>
+ </scons_example>
+
+ <para>
+
+ The output then looks like:
+
+ </para>
+
+ <scons_output example="Copy3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Deleting Files or Directories: The &Delete; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Delete1">
+ <file name="S" printme="1">
+ Command("file.out", "file.in",
+ [
+ Delete("tempfile"),
+ Copy("tempfile", "$SOURCE"),
+ "modify tempfile",
+ Copy("$TARGET", "tempfile"),
+ ])
+ </file>
+ <file name="SConstruct">
+ env = DefaultEnvironment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ SConscript('S')
+ </file>
+ <file name="file.in">file.in</file>
+ <file name="modify" chmod="0755">
+ touch $*
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which then executes as follows:
+
+ </para>
+
+ <scons_output example="Delete1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Of course, like all of these &Action; factories,
+ the &Delete factory also expands
+ &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately.
+ For example:
+
+ </para>
+
+ <scons_example name="Delete2">
+ <file name="SConstruct" printme="1">
+ Command("file.out", "file.in",
+ [
+ Delete("$TARGET"),
+ Copy("$TARGET", "$SOURCE")
+ ])
+ </file>
+ <file name="file.in">file.in</file>
+ </scons_example>
+
+ <para>
+
+ Executes as:
+
+ </para>
+
+ <scons_output example="Delete2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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 <literal>Delete("$SOURCE")</literal>
+ is not something you usually want to do!
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Moving (Renaming) Files or Directories: The &Move; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Move">
+ <file name="S" printme="1">
+ Command("file.out", "file.in",
+ [
+ Copy("tempfile", "$SOURCE"),
+ "modify tempfile",
+ Move("$TARGET", "tempfile"),
+ ])
+ </file>
+ <file name="SConstruct">
+ env = DefaultEnvironment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ SConscript('S')
+ </file>
+ <file name="file.in">file.in</file>
+ <file name="modify" chmod="0755">
+ touch $*
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which would execute as:
+
+ </para>
+
+ <scons_output example="Move">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Updating the Modification Time of a File: The &Touch; Factory</title>
+
+ <para>
+
+ If you just need to update the
+ recorded modification time for a file,
+ use the &Touch; factory:
+
+ </para>
+
+ <scons_example name="Touch">
+ <file name="S" printme="1">
+ Command("file.out", "file.in",
+ [
+ Copy("$TARGET", "$SOURCE"),
+ Touch("$TARGET"),
+ ])
+ </file>
+ <file name="SConstruct">
+ env = DefaultEnvironment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ SConscript('S')
+ </file>
+ <file name="file.in">file.in</file>
+ </scons_example>
+
+ <para>
+
+ Which executes as:
+
+ </para>
+
+ <scons_output example="Touch">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Creating a Directory: The &Mkdir; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Mkdir">
+ <file name="S" printme="1">
+ Command("file.out", "file.in",
+ [
+ Delete("tempdir"),
+ Mkdir("tempdir"),
+ Copy("tempdir/${SOURCE.file}", "$SOURCE"),
+ "process tempdir",
+ Move("$TARGET", "tempdir/output_file"),
+ Delete("tempdir"),
+ ])
+ </file>
+ <file name="SConstruct">
+ env = DefaultEnvironment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ SConscript('S')
+ </file>
+ <file name="file.in">file.in</file>
+ <file name="process" chmod="0755">
+ touch $*
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which executes as:
+
+ </para>
+
+ <scons_output example="Mkdir">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Changing File or Directory Permissions: The &Chmod; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Chmod">
+ <file name="SConstruct" printme="1">
+ Command("file.out", "file.in",
+ [
+ Copy("$TARGET", "$SOURCE"),
+ Chmod("$TARGET", 0755),
+ ])
+ </file>
+ <file name="file.in">file.in</file>
+ </scons_example>
+
+ <para>
+
+ Which executes:
+
+ </para>
+
+ <scons_output example="Chmod">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Executing an action immediately: the &Execute; Function</title>
+
+ <para>
+
+ 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,
+
+ </para>
+
+ <scons_example name="Execute">
+ <file name="SConstruct" printme="1">
+ Execute(Mkdir('__ROOT__/tmp/my_temp_directory'))
+ </file>
+ </scons_example>
+
+ <para>
+
+ Notice that this will
+ create the directory while
+ the &SConscript; file is being read:
+
+ </para>
+
+ <scons_output example="Execute">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If you're familiar with Python,
+ you may wonder why you would want to use this
+ instead of just calling the native Python
+ <function>os.mkdir()</function> function.
+ The advantage here is that the &Mkdir;
+ action will behave appropriately if the user
+ specifies the &SCons; <option>-n</option> or
+ <option>-q</option> options--that is,
+ it will print the action but not actually
+ make the directory when <option>-n</option> is specified,
+ or make the directory but not print the action
+ when <option>-q</option> is specified.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis>, 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):
+
+ </para>
+
+ <sconstruct>
+ if Execute(Mkdir('__ROOT__/tmp/my_temp_directory')):
+ # A problem occurred while making the temp directory.
+ Exit(1)
+ </sconstruct>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &SCons; provides a number of platform-independent functions,
+ called <literal>factories</literal>,
+ that perform common file system manipulations
+ like copying, moving or deleting files and directories,
+ or making directories.
+ These functions are <literal>factories</literal>
+ 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.
+
+ </para>
+
+ <section>
+ <title>Copying Files or Directories: The &Copy; Factory</title>
+
+ <para>
+
+ Suppose you want to arrange to make a copy of a file,
+ and don't have a suitable pre-existing builder.
+ <footnote>
+ <para>
+ 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.
+ </para>
+ </footnote>
+ One way would be to use the &Copy; action factory
+ in conjunction with the &Command; builder:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in", Copy("$TARGET", "$SOURCE"))
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Copy("file.out", "file.in")
+ </screen>
+
+ <para>
+
+ You can, of course, name a file explicitly
+ instead of using &cv-TARGET; or &cv-SOURCE;:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", [], Copy("$TARGET", "file.in"))
+ </programlisting>
+
+ <para>
+
+ Which executes as:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Copy("file.out", "file.in")
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Copy("tempfile", "$SOURCE"),
+ "modify tempfile",
+ Copy("$TARGET", "tempfile"),
+ ])
+ </programlisting>
+
+ <para>
+
+ The output then looks like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Copy("tempfile", "file.in")
+ modify tempfile
+ Copy("file.out", "tempfile")
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Deleting Files or Directories: The &Delete; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Delete("tempfile"),
+ Copy("tempfile", "$SOURCE"),
+ "modify tempfile",
+ Copy("$TARGET", "tempfile"),
+ ])
+ </programlisting>
+
+ <para>
+
+ Which then executes as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Delete("tempfile")
+ Copy("tempfile", "file.in")
+ modify tempfile
+ Copy("file.out", "tempfile")
+ </screen>
+
+ <para>
+
+ Of course, like all of these &Action; factories,
+ the &Delete; factory also expands
+ &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately.
+ For example:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Delete("$TARGET"),
+ Copy("$TARGET", "$SOURCE")
+ ])
+ </programlisting>
+
+ <para>
+
+ Executes as:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Delete("file.out")
+ Copy("file.out", "file.in")
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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 <literal>Delete("$SOURCE")</literal>
+ is not something you usually want to do!
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Moving (Renaming) Files or Directories: The &Move; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Copy("tempfile", "$SOURCE"),
+ "modify tempfile",
+ Move("$TARGET", "tempfile"),
+ ])
+ </programlisting>
+
+ <para>
+
+ Which would execute as:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Copy("tempfile", "file.in")
+ modify tempfile
+ Move("file.out", "tempfile")
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Updating the Modification Time of a File: The &Touch; Factory</title>
+
+ <para>
+
+ If you just need to update the
+ recorded modification time for a file,
+ use the &Touch; factory:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Copy("$TARGET", "$SOURCE"),
+ Touch("$TARGET"),
+ ])
+ </programlisting>
+
+ <para>
+
+ Which executes as:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Copy("file.out", "file.in")
+ Touch("file.out")
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Creating a Directory: The &Mkdir; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Delete("tempdir"),
+ Mkdir("tempdir"),
+ Copy("tempdir/${SOURCE.file}", "$SOURCE"),
+ "process tempdir",
+ Move("$TARGET", "tempdir/output_file"),
+ Delete("tempdir"),
+ ])
+ </programlisting>
+
+ <para>
+
+ Which executes as:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Changing File or Directory Permissions: The &Chmod; Factory</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Command("file.out", "file.in",
+ [
+ Copy("$TARGET", "$SOURCE"),
+ Chmod("$TARGET", 0755),
+ ])
+ </programlisting>
+
+ <para>
+
+ Which executes:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Copy("file.out", "file.in")
+ Chmod("file.out", 0755)
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Executing an action immediately: the &Execute; Function</title>
+
+ <para>
+
+ 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,
+
+ </para>
+
+ <programlisting>
+ Execute(Mkdir('/tmp/my_temp_directory'))
+ </programlisting>
+
+ <para>
+
+ Notice that this will
+ create the directory while
+ the &SConscript; file is being read:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ If you're familiar with Python,
+ you may wonder why you would want to use this
+ instead of just calling the native Python
+ <function>os.mkdir()</function> function.
+ The advantage here is that the &Mkdir;
+ action will behave appropriately if the user
+ specifies the &SCons; <option>-n</option> or
+ <option>-q</option> options--that is,
+ it will print the action but not actually
+ make the directory when <option>-n</option> is specified,
+ or make the directory but not print the action
+ when <option>-q</option> is specified.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>not</emphasis>, 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):
+
+ </para>
+
+ <programlisting>
+ if Execute(Mkdir('/tmp/my_temp_directory')):
+ # A problem occurred while making the temp directory.
+ Exit(1)
+ </programlisting>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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
+ <literal>-c</literal> option to "clean"
+ a tree of its built targets.
+
+ These behaviours can be suppressed with the
+ &Precious; and &NoClean; functions, respectively.
+
+ </para>
+
+ <section>
+ <title>Preventing target removal during build: the &Precious; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="precious-ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment(RANLIBCOM='')
+ lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ env.Precious(lib)
+ </file>
+ <file name="f1.c">
+ int f1() { }
+ </file>
+ <file name="f2.c">
+ int f2() { }
+ </file>
+ <file name="f3.c">
+ int f3() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Although the output doesn't look any different,
+ &SCons; does not, in fact,
+ delete the target library before rebuilding it:
+
+ </para>
+
+ <scons_output example="precious-ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ &SCons; will, however, still delete files marked as &Precious;
+ when the <literal>-c</literal> option is used.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Preventing target removal during clean: the &NoClean; Function</title>
+
+ <para>
+
+ By default, &SCons; removes all built targets when invoked
+ with the <literal>-c</literal> 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:
+
+ </para>
+
+ <scons_example name="noclean-ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment(RANLIBCOM='')
+ lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ env.NoClean(lib)
+ </file>
+ <file name="f1.c">
+ int f1() { }
+ </file>
+ <file name="f2.c">
+ int f2() { }
+ </file>
+ <file name="f3.c">
+ int f3() { }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Notice that the <filename>libfoo.a</filename>
+ is not listed as a removed file:
+
+ </para>
+
+ <scons_output example="noclean-ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -c</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Removing additional files during clean: the &Clean; Function</title>
+
+ <para>
+
+ There may be additional files that you want removed
+ when the <literal>-c</literal> 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.
+
+ </para>
+
+ <para>
+
+ You can use the &Clean; function to arrange for additional files
+ to be removed when the <literal>-c</literal> option is used.
+ Notice, however, that the &Clean; function takes two arguments,
+ and the <emphasis>second</emphasis> argument
+ is the name of the additional file you want cleaned
+ (<filename>foo.log</filename> in this example):
+
+ </para>
+
+ <scons_example name="clean-ex1">
+ <file name="S" printme="1">
+ t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE')
+ Clean(t, 'foo.log')
+ </file>
+ <file name="SConstruct">
+ env = DefaultEnvironment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ SConscript('S')
+ </file>
+ <file name="foo.in">
+ foo.in
+ </file>
+ <file name="foo.log">
+ foo.log
+ </file>
+ <file name="build" chmod="0755">
+ cat $3 > $2
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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
+ <filename>foo.out</filename>
+ target.
+ Now whenever the
+ <filename>foo.out</filename> target is cleaned
+ by the <literal>-c</literal> option,
+ the <filename>foo.log</filename> file
+ will be removed as well:
+
+ </para>
+
+ <scons_output example="clean-ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ </scons_output>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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
+ <literal>-c</literal> option to "clean"
+ a tree of its built targets.
+
+ These behaviours can be suppressed with the
+ &Precious; and &NoClean; functions, respectively.
+
+ </para>
+
+ <section>
+ <title>Preventing target removal during build: the &Precious; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(RANLIBCOM='')
+ lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ env.Precious(lib)
+ </programlisting>
+
+ <para>
+
+ Although the output doesn't look any different,
+ &SCons; does not, in fact,
+ delete the target library before rebuilding it:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ &SCons; will, however, still delete files marked as &Precious;
+ when the <literal>-c</literal> option is used.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Preventing target removal during clean: the &NoClean; Function</title>
+
+ <para>
+
+ By default, &SCons; removes all built targets when invoked
+ with the <literal>-c</literal> 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(RANLIBCOM='')
+ lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ env.NoClean(lib)
+ </programlisting>
+
+ <para>
+
+ Notice that the <filename>libfoo.a</filename>
+ is not listed as a removed file:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ % <userinput>scons -c</userinput>
+ 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.
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Removing additional files during clean: the &Clean; Function</title>
+
+ <para>
+
+ There may be additional files that you want removed
+ when the <literal>-c</literal> 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.
+
+ </para>
+
+ <para>
+
+ You can use the &Clean; function to arrange for additional files
+ to be removed when the <literal>-c</literal> option is used.
+ Notice, however, that the &Clean; function takes two arguments,
+ and the <emphasis>second</emphasis> argument
+ is the name of the additional file you want cleaned
+ (<filename>foo.log</filename> in this example):
+
+ </para>
+
+ <programlisting>
+ t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE')
+ Clean(t, 'foo.log')
+ </programlisting>
+
+ <para>
+
+ 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
+ <filename>foo.out</filename>
+ target.
+ Now whenever the
+ <filename>foo.out</filename> target is cleaned
+ by the <literal>-c</literal> option,
+ the <filename>foo.log</filename> file
+ will be removed as well:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ build -o foo.out foo.in
+ % <userinput>scons -Q -c</userinput>
+ Removed foo.out
+ Removed foo.log
+ </screen>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+
+=head2 The Build command
+
+By default, Cons does not change its working directory to the directory
+containing a subsidiary F<Conscript> file it is including. This behavior
+can be enabled for a build by specifying, in the top-level F<Construct>
+file:
+
+ Conscript_chdir 1;
+
+When enabled, Cons will change to the subsidiary F<Conscript> file's
+containing directory while reading in that file, and then change back
+to the top-level directory once the file has been processed.
+
+It is expected that this behavior will become the default in some future
+version of Cons. To prepare for this transition, builds that expect
+Cons to remain at the top of the build while it reads in a subsidiary
+F<Conscript> file should explicitly disable this feature as follows:
+
+ Conscript_chdir 0;
+
+=head2 Relative, top-relative, and absolute file names
+
+(There is another file prefix, ``!'', that is interpreted specially by
+Cons. See discussion of the C<Link> command, below, for details.)
+
+
+=head2 Using modules in build scripts
+
+You may pull modules into each F<Conscript> file using the normal Perl
+C<use> or C<require> statements:
+
+ use English;
+ require My::Module;
+
+Each C<use> or C<require> only affects the one F<Conscript> file in which
+it appears. To use a module in multiple F<Conscript> files, you must
+put a C<use> or C<require> statement in each one that needs the module.
+
+
+=head2 Scope of variables
+
+The top-level F<Construct> file and all F<Conscript> files begin life in
+a common, separate Perl package. B<Cons> controls the symbol table for
+the package so that, the symbol table for each script is empty, except
+for the F<Construct> file, which gets some of the command line arguments.
+All of the variables that are set or used, therefore, are set by the
+script itself, not by some external script.
+
+Variables can be explicitly B<imported> by a script from its parent
+script. To import a variable, it must have been B<exported> by the parent
+and initialized (otherwise an error will occur).
+
+
+=head2 The Export command
+
+The C<Export> command is used as in the following example:
+
+ $env = new cons();
+ $INCLUDE = "#export/include";
+ $LIB = "#export/lib";
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+
+The values of the simple variables mentioned in the C<Export> list will be
+squirreled away by any subsequent C<Build> commands. The C<Export> command
+will only export Perl B<scalar> variables, that is, variables whose name
+begins with C<$>. Other variables, objects, etc. can be exported by
+reference, but all scripts will refer to the same object, and this object
+should be considered to be read-only by the subsidiary scripts and by the
+original exporting script. It's acceptable, however, to assign a new value
+to the exported scalar variable, that won't change the underlying variable
+referenced. This sequence, for example, is OK:
+
+ $env = new cons();
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+ $env = new cons(CFLAGS => '-O');
+ Build qw( other/Conscript );
+
+It doesn't matter whether the variable is set before or after the C<Export>
+command. The important thing is the value of the variable at the time the
+C<Build> command is executed. This is what gets squirreled away. Any
+subsequent C<Export> commands, by the way, invalidate the first: you must
+mention all the variables you wish to export on each C<Export> command.
+
+
+=head2 The Import command
+
+Variables exported by the C<Export> command can be imported into subsidiary
+scripts by the C<Import> command. The subsidiary script always imports
+variables directly from the superior script. Consider this example:
+
+ Import qw( env INCLUDE );
+
+This is only legal if the parent script exported both C<$env> and
+C<$INCLUDE>. It also must have given each of these variables values. It is
+OK for the subsidiary script to only import a subset of the exported
+variables (in this example, C<$LIB>, which was exported by the previous
+example, is not imported).
+
+All the imported variables are automatically re-exported, so the sequence:
+
+ Import qw ( env INCLUDE );
+ Build qw ( beneath-me/Conscript );
+
+will supply both C<$env> and C<$INCLUDE> to the subsidiary file. If only
+C<$env> is to be exported, then the following will suffice:
+
+ Import qw ( env INCLUDE );
+ Export qw ( env );
+ Build qw ( beneath-me/Conscript );
+
+Needless to say, the variables may be modified locally before invoking
+C<Build> on the subsidiary script.
+
+=head2 Build script evaluation order
+
+The only constraint on the ordering of build scripts is that superior
+scripts are evaluated before their inferior scripts. The top-level
+F<Construct> file, for instance, is evaluated first, followed by any
+inferior scripts. This is all you really need to know about the evaluation
+order, since order is generally irrelevant. Consider the following C<Build>
+command:
+
+ Build qw(
+ drivers/display/Conscript
+ drivers/mouse/Conscript
+ parser/Conscript
+ utilities/Conscript
+ );
+
+We've chosen to put the script names in alphabetical order, simply because
+that's the most convenient for maintenance purposes. Changing the order will
+make no difference to the build.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>&SConscript; Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ SConscript(['drivers/display/SConscript',
+ 'drivers/mouse/SConscript',
+ 'parser/SConscript',
+ 'utilities/SConscript'])
+ </sconstruct>
+
+ <para>
+
+ 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 <literal>drivers</literal>
+ subdirectory might contain an intermediate
+ &SConscript; file,
+ in which case the &SConscript; call in
+ the top-level &SConstruct; file
+ would look like:
+
+ </para>
+
+ <sconstruct>
+ SConscript(['drivers/SConscript',
+ 'parser/SConscript',
+ 'utilities/SConscript'])
+ </sconstruct>
+
+ <para>
+
+ And the subsidiary &SConscript; file in the
+ <literal>drivers</literal> subdirectory
+ would look like:
+
+ </para>
+
+ <sconstruct>
+ SConscript(['display/SConscript',
+ 'mouse/SConscript'])
+ </sconstruct>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Path Names Are Relative to the &SConscript; Directory</title>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ SConscript(['prog1/SConscript',
+ 'prog2/SConscript'])
+ </file>
+ <file name="prog1/SConscript">
+ env = Environment()
+ env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c'])
+ </file>
+ <file name="prog2/SConscript">
+ env = Environment()
+ env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c'])
+ </file>
+ <directory name="prog1"></directory>
+ <file name="prog1/main.c">
+ x
+ </file>
+ <file name="prog1/foo1.c">
+ x
+ </file>
+ <file name="prog1/foo2.c">
+ x
+ </file>
+ <directory name="prog2"></directory>
+ <file name="prog2/main.c">
+ x
+ </file>
+ <file name="prog2/bar1.c">
+ x
+ </file>
+ <file name="prog2/bar2.c">
+ x
+ </file>
+ </scons_example>
+
+ <para>
+
+ And subsidiary &SConscript; files that look like this:
+
+ </para>
+
+ <scons_example_file example="ex1" name="prog1/SConscript">
+ </scons_example_file>
+
+ <para>
+
+ And this:
+
+ </para>
+
+ <scons_example_file example="ex1" name="prog2/SConscript">
+ </scons_example_file>
+
+ <para>
+
+ Then, when we run &SCons; in the top-level directory,
+ our build looks like:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Top-Level Path Names in Subsidiary &SConscript; Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct">
+ SConscript('src/prog/SConscript')
+ </file>
+ <file name="src/prog/SConscript" printme="1">
+ env = Environment()
+ env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c'])
+ </file>
+ <file name="src/prog/main.c">
+ x
+ </file>
+ <file name="lib/foo1.c">
+ x
+ </file>
+ <file name="src/prog/foo2.c">
+ x
+ </file>
+ </scons_example>
+
+ <para>
+
+ In this example,
+ the <literal>lib</literal> directory is
+ directly underneath the top-level &SConstruct; directory.
+ If the above &SConscript; file is in a subdirectory
+ named <literal>src/prog</literal>,
+ the output would look like:
+
+ </para>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ (Notice that the <literal>lib/foo1.o</literal> object file
+ is built in the same directory as its source file.
+ See <xref linkend="chap-separate"></xref>, below,
+ for information about
+ how to build the object file in a different subdirectory.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Absolute Path Names</title>
+
+ <para>
+
+ Of course, you can always specify
+ an absolute path name for a file--for example:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct">
+ SConscript('src/prog/SConscript')
+ </file>
+ <file name="src/prog/SConscript" printme="1">
+ env = Environment()
+ env.Program('prog', ['main.c', '__ROOT__/usr/joe/lib/foo1.c', 'foo2.c'])
+ </file>
+ <file name="src/prog/main.c">
+ x
+ </file>
+ <file name="__ROOT__/usr/joe/lib/foo1.c">
+ x
+ </file>
+ <file name="src/prog/foo2.c">
+ x
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which, when executed, would yield:
+
+ </para>
+
+ <scons_output example="ex3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ (As was the case with top-relative path names,
+ notice that the <literal>/usr/joe/lib/foo1.o</literal> object file
+ is built in the same directory as its source file.
+ See <xref linkend="chap-separate"></xref>, below,
+ for information about
+ how to build the object file in a different subdirectory.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Sharing Environments (and Other Variables) Between &SConscript; Files</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ &SCons; supports the ability to <emphasis>export</emphasis> variables
+ from a parent &SConscript; file
+ to its subsidiary &SConscript; files,
+ which allows you to share common initialized
+ values throughout your build hierarchy.
+
+ </para>
+
+ <section>
+ <title>Exporting Variables</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ Export('env')
+ </sconstruct>
+
+ <para>
+
+ You may export more than one variable name at a time:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ debug = ARGUMENTS['debug']
+ Export('env', 'debug')
+ </sconstruct>
+
+ <para>
+
+ Because white space is not legal in Python variable names,
+ the &Export; function will even automatically split
+ a string into separate names for you:
+
+ </para>
+
+ <sconstruct>
+ Export('env debug')
+ </sconstruct>
+
+ <para>
+
+ Second, you can specify a list of
+ variables to export as a second argument
+ to the &SConscript; function call:
+
+ </para>
+
+ <sconstruct>
+ SConscript('src/SConscript', 'env')
+ </sconstruct>
+
+ <para>
+
+ Or as the &exports; keyword argument:
+
+ </para>
+
+ <sconstruct>
+ SConscript('src/SConscript', exports='env')
+ </sconstruct>
+
+ <para>
+
+ These calls export the specified variables
+ to only the listed &SConscript; files.
+ You may, however, specify more than one
+ &SConscript; file in a list:
+
+ </para>
+
+ <sconstruct>
+ SConscript(['src1/SConscript',
+ 'src2/SConscript'], exports='env')
+ </sconstruct>
+
+ <para>
+
+ This is functionally equivalent to
+ calling the &SConscript; function
+ multiple times with the same &exports; argument,
+ one per &SConscript; file.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Importing Variables</title>
+
+ <para>
+
+ Once a variable has been exported from a calling
+ &SConscript; file,
+ it may be used in other &SConscript; files
+ by calling the &Import; function:
+
+ </para>
+
+ <sconstruct>
+ Import('env')
+ env.Program('prog', ['prog.c'])
+ </sconstruct>
+
+ <para>
+
+ The &Import; call makes the <literal>env</literal> construction
+ environment available to the &SConscript; file,
+ after which the variable can be used to build
+ programs, libraries, etc.
+
+ </para>
+
+ <para>
+
+ Like the &Export; function,
+ the &Import; function can be used
+ with multiple variable names:
+
+ </para>
+
+ <sconstruct>
+ Import('env', 'debug')
+ env = env.Clone(DEBUG = debug)
+ env.Program('prog', ['prog.c'])
+ </sconstruct>
+
+ <para>
+
+ And the &Import; function will similarly
+ split a string along white-space
+ into separate variable names:
+
+ </para>
+
+ <sconstruct>
+ Import('env debug')
+ env = env.Clone(DEBUG = debug)
+ env.Program('prog', ['prog.c'])
+ </sconstruct>
+
+ <para>
+
+ Lastly, as a special case,
+ you may import all of the variables that
+ have been exported by supplying an asterisk
+ to the &Import; function:
+
+ </para>
+
+ <sconstruct>
+ Import('*')
+ env = env.Clone(DEBUG = debug)
+ env.Program('prog', ['prog.c'])
+ </sconstruct>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Returning Values From an &SConscript; File</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Return">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ Export('env')
+ objs = []
+ for subdir in ['foo', 'bar']:
+ o = SConscript('%s/SConscript' % subdir)
+ objs.append(o)
+ env.Library('prog', objs)
+ </file>
+ <directory name="foo"></directory>
+ <directory name="bar"></directory>
+ <file name="foo/SConscript">
+ Import('env')
+ obj = env.Object('foo.c')
+ Return('obj')
+ </file>
+ <file name="bar/SConscript">
+ Import('env')
+ obj = env.Object('bar.c')
+ Return('obj')
+ </file>
+ <file name="foo/foo.c">
+ void foo(void) { printf("foo/foo.c\n"); }
+ </file>
+ <file name="bar/bar.c">
+ void bar(void) { printf("bar/bar.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ We can do this by using the &Return;
+ function in the
+ <literal>foo/SConscript</literal> file like this:
+
+ </para>
+
+ <scons_example_file example="Return" name="foo/SConscript">
+ </scons_example_file>
+
+ <para>
+
+ (The corresponding
+ <literal>bar/SConscript</literal>
+ 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:
+
+ </para>
+
+ <scons_output example="Return">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <!--
+ XXX Return(stop=False)
+ -->
+
+ </section>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Executing From a Subdirectory: the -D, -u and -U Options</title>
+
+ <para>
+
+ XXX -D, -u and -U
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+
+=head2 The Build command
+
+By default, Cons does not change its working directory to the directory
+containing a subsidiary F<Conscript> file it is including. This behavior
+can be enabled for a build by specifying, in the top-level F<Construct>
+file:
+
+ Conscript_chdir 1;
+
+When enabled, Cons will change to the subsidiary F<Conscript> file's
+containing directory while reading in that file, and then change back
+to the top-level directory once the file has been processed.
+
+It is expected that this behavior will become the default in some future
+version of Cons. To prepare for this transition, builds that expect
+Cons to remain at the top of the build while it reads in a subsidiary
+F<Conscript> file should explicitly disable this feature as follows:
+
+ Conscript_chdir 0;
+
+=head2 Relative, top-relative, and absolute file names
+
+(There is another file prefix, ``!'', that is interpreted specially by
+Cons. See discussion of the C<Link> command, below, for details.)
+
+
+=head2 Using modules in build scripts
+
+You may pull modules into each F<Conscript> file using the normal Perl
+C<use> or C<require> statements:
+
+ use English;
+ require My::Module;
+
+Each C<use> or C<require> only affects the one F<Conscript> file in which
+it appears. To use a module in multiple F<Conscript> files, you must
+put a C<use> or C<require> statement in each one that needs the module.
+
+
+=head2 Scope of variables
+
+The top-level F<Construct> file and all F<Conscript> files begin life in
+a common, separate Perl package. B<Cons> controls the symbol table for
+the package so that, the symbol table for each script is empty, except
+for the F<Construct> file, which gets some of the command line arguments.
+All of the variables that are set or used, therefore, are set by the
+script itself, not by some external script.
+
+Variables can be explicitly B<imported> by a script from its parent
+script. To import a variable, it must have been B<exported> by the parent
+and initialized (otherwise an error will occur).
+
+
+=head2 The Export command
+
+The C<Export> command is used as in the following example:
+
+ $env = new cons();
+ $INCLUDE = "#export/include";
+ $LIB = "#export/lib";
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+
+The values of the simple variables mentioned in the C<Export> list will be
+squirreled away by any subsequent C<Build> commands. The C<Export> command
+will only export Perl B<scalar> variables, that is, variables whose name
+begins with C<$>. Other variables, objects, etc. can be exported by
+reference, but all scripts will refer to the same object, and this object
+should be considered to be read-only by the subsidiary scripts and by the
+original exporting script. It's acceptable, however, to assign a new value
+to the exported scalar variable, that won't change the underlying variable
+referenced. This sequence, for example, is OK:
+
+ $env = new cons();
+ Export qw( env INCLUDE LIB );
+ Build qw( util/Conscript );
+ $env = new cons(CFLAGS => '-O');
+ Build qw( other/Conscript );
+
+It doesn't matter whether the variable is set before or after the C<Export>
+command. The important thing is the value of the variable at the time the
+C<Build> command is executed. This is what gets squirreled away. Any
+subsequent C<Export> commands, by the way, invalidate the first: you must
+mention all the variables you wish to export on each C<Export> command.
+
+
+=head2 The Import command
+
+Variables exported by the C<Export> command can be imported into subsidiary
+scripts by the C<Import> command. The subsidiary script always imports
+variables directly from the superior script. Consider this example:
+
+ Import qw( env INCLUDE );
+
+This is only legal if the parent script exported both C<$env> and
+C<$INCLUDE>. It also must have given each of these variables values. It is
+OK for the subsidiary script to only import a subset of the exported
+variables (in this example, C<$LIB>, which was exported by the previous
+example, is not imported).
+
+All the imported variables are automatically re-exported, so the sequence:
+
+ Import qw ( env INCLUDE );
+ Build qw ( beneath-me/Conscript );
+
+will supply both C<$env> and C<$INCLUDE> to the subsidiary file. If only
+C<$env> is to be exported, then the following will suffice:
+
+ Import qw ( env INCLUDE );
+ Export qw ( env );
+ Build qw ( beneath-me/Conscript );
+
+Needless to say, the variables may be modified locally before invoking
+C<Build> on the subsidiary script.
+
+=head2 Build script evaluation order
+
+The only constraint on the ordering of build scripts is that superior
+scripts are evaluated before their inferior scripts. The top-level
+F<Construct> file, for instance, is evaluated first, followed by any
+inferior scripts. This is all you really need to know about the evaluation
+order, since order is generally irrelevant. Consider the following C<Build>
+command:
+
+ Build qw(
+ drivers/display/Conscript
+ drivers/mouse/Conscript
+ parser/Conscript
+ utilities/Conscript
+ );
+
+We've chosen to put the script names in alphabetical order, simply because
+that's the most convenient for maintenance purposes. Changing the order will
+make no difference to the build.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>&SConscript; Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ SConscript(['drivers/display/SConscript',
+ 'drivers/mouse/SConscript',
+ 'parser/SConscript',
+ 'utilities/SConscript'])
+ </programlisting>
+
+ <para>
+
+ 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 <literal>drivers</literal>
+ subdirectory might contain an intermediate
+ &SConscript; file,
+ in which case the &SConscript; call in
+ the top-level &SConstruct; file
+ would look like:
+
+ </para>
+
+ <programlisting>
+ SConscript(['drivers/SConscript',
+ 'parser/SConscript',
+ 'utilities/SConscript'])
+ </programlisting>
+
+ <para>
+
+ And the subsidiary &SConscript; file in the
+ <literal>drivers</literal> subdirectory
+ would look like:
+
+ </para>
+
+ <programlisting>
+ SConscript(['display/SConscript',
+ 'mouse/SConscript'])
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Path Names Are Relative to the &SConscript; Directory</title>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ SConscript(['prog1/SConscript',
+ 'prog2/SConscript'])
+ </programlisting>
+
+ <para>
+
+ And subsidiary &SConscript; files that look like this:
+
+ </para>
+
+
+ <programlisting>
+ env = Environment()
+ env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c'])
+ </programlisting>
+
+ <para>
+
+ And this:
+
+ </para>
+
+
+ <programlisting>
+ env = Environment()
+ env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c'])
+ </programlisting>
+
+ <para>
+
+ Then, when we run &SCons; in the top-level directory,
+ our build looks like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Top-Level Path Names in Subsidiary &SConscript; Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c'])
+ </programlisting>
+
+ <para>
+
+ In this example,
+ the <literal>lib</literal> directory is
+ directly underneath the top-level &SConstruct; directory.
+ If the above &SConscript; file is in a subdirectory
+ named <literal>src/prog</literal>,
+ the output would look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ (Notice that the <literal>lib/foo1.o</literal> object file
+ is built in the same directory as its source file.
+ See <xref linkend="chap-separate"></xref>, below,
+ for information about
+ how to build the object file in a different subdirectory.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Absolute Path Names</title>
+
+ <para>
+
+ Of course, you can always specify
+ an absolute path name for a file--for example:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Program('prog', ['main.c', '/usr/joe/lib/foo1.c', 'foo2.c'])
+ </programlisting>
+
+ <para>
+
+ Which, when executed, would yield:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ (As was the case with top-relative path names,
+ notice that the <literal>/usr/joe/lib/foo1.o</literal> object file
+ is built in the same directory as its source file.
+ See <xref linkend="chap-separate"></xref>, below,
+ for information about
+ how to build the object file in a different subdirectory.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Sharing Environments (and Other Variables) Between &SConscript; Files</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ &SCons; supports the ability to <emphasis>export</emphasis> variables
+ from a parent &SConscript; file
+ to its subsidiary &SConscript; files,
+ which allows you to share common initialized
+ values throughout your build hierarchy.
+
+ </para>
+
+ <section>
+ <title>Exporting Variables</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ Export('env')
+ </programlisting>
+
+ <para>
+
+ You may export more than one variable name at a time:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ debug = ARGUMENTS['debug']
+ Export('env', 'debug')
+ </programlisting>
+
+ <para>
+
+ Because white space is not legal in Python variable names,
+ the &Export; function will even automatically split
+ a string into separate names for you:
+
+ </para>
+
+ <programlisting>
+ Export('env debug')
+ </programlisting>
+
+ <para>
+
+ Second, you can specify a list of
+ variables to export as a second argument
+ to the &SConscript; function call:
+
+ </para>
+
+ <programlisting>
+ SConscript('src/SConscript', 'env')
+ </programlisting>
+
+ <para>
+
+ Or as the &exports; keyword argument:
+
+ </para>
+
+ <programlisting>
+ SConscript('src/SConscript', exports='env')
+ </programlisting>
+
+ <para>
+
+ These calls export the specified variables
+ to only the listed &SConscript; files.
+ You may, however, specify more than one
+ &SConscript; file in a list:
+
+ </para>
+
+ <programlisting>
+ SConscript(['src1/SConscript',
+ 'src2/SConscript'], exports='env')
+ </programlisting>
+
+ <para>
+
+ This is functionally equivalent to
+ calling the &SConscript; function
+ multiple times with the same &exports; argument,
+ one per &SConscript; file.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Importing Variables</title>
+
+ <para>
+
+ Once a variable has been exported from a calling
+ &SConscript; file,
+ it may be used in other &SConscript; files
+ by calling the &Import; function:
+
+ </para>
+
+ <programlisting>
+ Import('env')
+ env.Program('prog', ['prog.c'])
+ </programlisting>
+
+ <para>
+
+ The &Import; call makes the <literal>env</literal> construction
+ environment available to the &SConscript; file,
+ after which the variable can be used to build
+ programs, libraries, etc.
+
+ </para>
+
+ <para>
+
+ Like the &Export; function,
+ the &Import; function can be used
+ with multiple variable names:
+
+ </para>
+
+ <programlisting>
+ Import('env', 'debug')
+ env = env.Clone(DEBUG = debug)
+ env.Program('prog', ['prog.c'])
+ </programlisting>
+
+ <para>
+
+ And the &Import; function will similarly
+ split a string along white-space
+ into separate variable names:
+
+ </para>
+
+ <programlisting>
+ Import('env debug')
+ env = env.Clone(DEBUG = debug)
+ env.Program('prog', ['prog.c'])
+ </programlisting>
+
+ <para>
+
+ Lastly, as a special case,
+ you may import all of the variables that
+ have been exported by supplying an asterisk
+ to the &Import; function:
+
+ </para>
+
+ <programlisting>
+ Import('*')
+ env = env.Clone(DEBUG = debug)
+ env.Program('prog', ['prog.c'])
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Returning Values From an &SConscript; File</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ Export('env')
+ objs = []
+ for subdir in ['foo', 'bar']:
+ o = SConscript('%s/SConscript' % subdir)
+ objs.append(o)
+ env.Library('prog', objs)
+ </programlisting>
+
+ <para>
+
+ We can do this by using the &Return;
+ function in the
+ <literal>foo/SConscript</literal> file like this:
+
+ </para>
+
+
+ <programlisting>
+ Import('env')
+ obj = env.Object('foo.c')
+ Return('obj')
+ </programlisting>
+
+ <para>
+
+ (The corresponding
+ <literal>bar/SConscript</literal>
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <!--
+ XXX Return(stop=False)
+ -->
+
+ </section>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Executing From a Subdirectory: the -D, -u and -U Options</title>
+
+ <para>
+
+ XXX -D, -u and -U
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Install('__ROOT__/usr/bin', hello)
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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 <literal>/</literal>)
+ for it to install anything there:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q __ROOT__/usr/bin</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <literal>install</literal>
+ that can expand to the specified destination directory:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Install('__ROOT__/usr/bin', hello)
+ env.Alias('install', '__ROOT__/usr/bin')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This then yields the more natural
+ ability to install the program
+ in its destination as follows:
+
+ </para>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q install</scons_output_command>
+ </scons_output>
+
+ <section>
+ <title>Installing Multiple Files in a Directory</title>
+
+ <para>
+
+ You can install multiple files into a directory
+ simply by calling the &Install; function multiple times:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="goodbye.c">
+ int main() { printf("Goodbye, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Or, more succinctly, listing the multiple input
+ files in a list
+ (just like you can do with any other builder):
+
+ </para>
+
+ <sconstruct>
+ 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')
+ </sconstruct>
+
+ <para>
+
+ Either of these two examples yields:
+
+ </para>
+
+ <scons_output example="ex3">
+ <scons_output_command>scons -Q install</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Installing a File Under a Different Name</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.InstallAs('__ROOT__/usr/bin/hello-new', hello)
+ env.Alias('install', '__ROOT__/usr/bin')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This installs the <literal>hello</literal>
+ program with the name <literal>hello-new</literal>
+ as follows:
+
+ </para>
+
+ <scons_output example="ex4">
+ <scons_output_command>scons -Q install</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Installing Multiple Files Under Different Names</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="goodbye.c">
+ int main() { printf("Goodbye, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ In this case, the &InstallAs; function
+ loops through both lists simultaneously,
+ and copies each source file into its corresponding
+ target file name:
+
+ </para>
+
+ <scons_output example="ex5">
+ <scons_output_command>scons -Q install</scons_output_command>
+ </scons_output>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Install('/usr/bin', hello)
+ </programlisting>
+
+ <para>
+
+ 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 <literal>/</literal>)
+ for it to install anything there:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q /usr/bin</userinput>
+ Install file: "hello" as "/usr/bin/hello"
+ </screen>
+
+ <para>
+
+ 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 <literal>install</literal>
+ that can expand to the specified destination directory:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.Install('/usr/bin', hello)
+ env.Alias('install', '/usr/bin')
+ </programlisting>
+
+ <para>
+
+ This then yields the more natural
+ ability to install the program
+ in its destination as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ % <userinput>scons -Q install</userinput>
+ Install file: "hello" as "/usr/bin/hello"
+ </screen>
+
+ <section>
+ <title>Installing Multiple Files in a Directory</title>
+
+ <para>
+
+ You can install multiple files into a directory
+ simply by calling the &Install; function multiple times:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ Or, more succinctly, listing the multiple input
+ files in a list
+ (just like you can do with any other builder):
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ goodbye = env.Program('goodbye.c')
+ env.Install('/usr/bin', [hello, goodbye])
+ env.Alias('install', '/usr/bin')
+ </programlisting>
+
+ <para>
+
+ Either of these two examples yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q install</userinput>
+ 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"
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Installing a File Under a Different Name</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ env.InstallAs('/usr/bin/hello-new', hello)
+ env.Alias('install', '/usr/bin')
+ </programlisting>
+
+ <para>
+
+ This installs the <literal>hello</literal>
+ program with the name <literal>hello-new</literal>
+ as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q install</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ Install file: "hello" as "/usr/bin/hello-new"
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Installing Multiple Files Under Different Names</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ In this case, the &InstallAs; function
+ loops through both lists simultaneously,
+ and copies each source file into its corresponding
+ target file name:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q install</userinput>
+ 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"
+ </screen>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ So far, we've 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.
+
+ </para>
+
+ <section>
+ <title>Building Java Class Files: the &b-Java; Builder</title>
+
+ <para>
+
+ The basic activity when programming in Java,
+ of course, is to take one or more <filename>.java</filename> files
+ containing Java source code
+ and to call the Java compiler
+ to turn them into one or more
+ <filename>.class</filename> files.
+ In &SCons;, you do this
+ by giving the &b-link-Java; Builder
+ a target directory in which
+ to put the <filename>.class</filename> files,
+ and a source directory that contains
+ the <filename>.java</filename> files:
+
+ </para>
+
+ <scons_example name="java">
+ <file name="SConstruct" printme="1">
+ Java('classes', 'src')
+ </file>
+ <file name="src/Example1.java">
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="src/Example2.java">
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="src/Example3.java">
+ public class Example3
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ If the <filename>src</filename> directory contains
+ three <filename>.java</filename> source files,
+ then running &SCons; might look like this:
+
+ </para>
+
+ <scons_output example="java">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ &SCons; will actually search the <filename>src</filename>
+ directory tree for all of the <filename>.java</filename> files.
+ The Java compiler will then create the
+ necessary class files in the <filename>classes</filename> subdirectory,
+ based on the class names found in the <filename>.java</filename> files.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>How &SCons; Handles Java Dependencies</title>
+
+ <para>
+
+ In addition to searching the source directory for
+ <filename>.java</filename> files,
+ &SCons; actually runs the <filename>.java</filename> 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 <filename>.class</filename> files
+ will be produced by the &javac; call.
+ So our one-liner example from the preceding section:
+
+ </para>
+
+ <scons_example name="java-classes">
+ <file name="SConstruct" printme="1">
+ Java('classes', 'src')
+ </file>
+ <file name="src/Example1.java">
+ 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");
+ }
+ }
+ </file>
+ <file name="src/Example2.java">
+ public class Example2
+ {
+ class Inner2 {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ }
+ </file>
+ <file name="src/Example3.java">
+ 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");
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Will not only tell you reliably
+ that the <filename>.class</filename> files
+ in the <filename>classes</filename> subdirectory
+ are up-to-date:
+
+ </para>
+
+ <scons_output example="java-classes">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q classes</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But it will also remove all of the generated
+ <filename>.class</filename> files,
+ even for inner classes,
+ without you having to specify them manually.
+ For example, if our
+ <filename>Example1.java</filename>
+ and
+ <filename>Example3.java</filename>
+ files both define additional classes,
+ and the class defined in <filename>Example2.java</filename>
+ has an inner class,
+ running <userinput>scons -c</userinput>
+ will clean up all of those <filename>.class</filename> files
+ as well:
+
+ </para>
+
+ <scons_output example="java-classes">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c classes</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ To ensure correct handling of <filename>.class</filename>
+ dependencies in all cases, you need to tell &SCons; which Java
+ version is being used. This is needed because Java 1.5 changed
+ the <filename>.class</filename> file names for nested anonymous
+ inner classes. Use the <varname>JAVAVERSION</varname> construction
+ variable to specify the version in use. With Java 1.6, the
+ one-liner example can then be defined like this:
+
+ </para>
+
+ <sconstruct>
+ Java('classes', 'src', JAVAVERSION='1.6')
+ </sconstruct>
+
+ <para>
+ See <varname>JAVAVERSION</varname> in the man page for more information.
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder</title>
+
+ <para>
+
+ After building the class files,
+ it's common to collect them into
+ a Java archive (<filename>.jar</filename>) 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:
+
+ </para>
+
+ <scons_example name="jar1">
+ <file name="SConstruct" printme="1">
+ Java(target = 'classes', source = 'src')
+ Jar(target = 'test.jar', source = 'classes')
+ </file>
+ <file name="src/Example1.java">
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="src/Example2.java">
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="src/Example3.java">
+ public class Example3
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; will then pass that directory
+ to the &jar; command,
+ which will collect all of the underlying
+ <filename>.class</filename> files:
+
+ </para>
+
+ <scons_output example="jar1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If you want to keep all of the
+ <filename>.class</filename> files
+ for multiple programs in one location,
+ and only archive some of them in
+ each <filename>.jar</filename> file,
+ you can pass the &b-Jar; builder a
+ list of files as its source.
+ It's extremely simple to create multiple
+ <filename>.jar</filename> 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:
+
+ </para>
+
+ <scons_example name="jar2">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ <file name="prog1/Example1.java">
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="prog1/Example2.java">
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="prog2/Example3.java">
+ public class Example3
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ <file name="prog2/Example4.java">
+ public class Example4
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This will then create
+ <filename>prog1.jar</filename>
+ and <filename>prog2.jar</filename>
+ next to the subdirectories
+ that contain their <filename>.java</filename> files:
+
+ </para>
+
+ <scons_output example="jar2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Building C Header and Stub Files: the &b-JavaH; Builder</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="javah">
+ <file name="SConstruct" printme="1">
+ classes = Java(target = 'classes', source = 'src/pkg/sub')
+ JavaH(target = 'native', source = classes)
+ </file>
+ <file name="src/pkg/sub/Example1.java">
+ package pkg.sub;
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example2.java">
+ package pkg.sub;
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example3.java">
+ package pkg.sub;
+ public class Example3
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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 <option>-d</option>
+ when &SCons; runs &javah;:
+
+ </para>
+
+ <scons_output example="javah">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ In this case,
+ the call to &javah;
+ will generate the header files
+ <filename>native/pkg_sub_Example1.h</filename>,
+ <filename>native/pkg_sub_Example2.h</filename>
+ and
+ <filename>native/pkg_sub_Example3.h</filename>.
+ Notice that &SCons; remembered that the class
+ files were generated with a target directory of
+ <filename>classes</filename>,
+ and that it then specified that target directory
+ as the <option>-classpath</option> option
+ to the call to &javah;.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>can</emphasis>
+ 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;:
+
+ </para>
+
+ <scons_example name="JAVACLASSDIR">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="src/pkg/sub/Example1.java">
+ package pkg.sub;
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example2.java">
+ package pkg.sub;
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example3.java">
+ package pkg.sub;
+ public class Example3
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ The &cv-JAVACLASSDIR; value then
+ gets converted into the <option>-classpath</option>
+ when &SCons; runs &javah;:
+
+ </para>
+
+ <scons_output example="JAVACLASSDIR">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="javah_file">
+ <file name="SConstruct" printme="1">
+ classes = Java(target = 'classes', source = 'src/pkg/sub')
+ JavaH(target = File('native.h'), source = classes)
+ </file>
+ <file name="src/pkg/sub/Example1.java">
+ package pkg.sub;
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example2.java">
+ package pkg.sub;
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example3.java">
+ package pkg.sub;
+ public class Example3
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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 <filename>native.h</filename>.
+ When a file is used, though,
+ &SCons; correctly converts the file name
+ into the &javah; <option>-o</option> option:
+
+ </para>
+
+ <scons_output example="javah_file">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder</title>
+
+ <para>
+
+ 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 <filename>_Stub.class</filename>
+ and <filename>_Skel.class</filename> files will
+ be placed:
+
+ </para>
+
+ <scons_example name="RMIC">
+ <file name="SConstruct" printme="1">
+ classes = Java(target = 'classes', source = 'src/pkg/sub')
+ RMIC(target = 'outdir', source = classes)
+ </file>
+ <file name="src/pkg/sub/Example1.java">
+ package pkg.sub;
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ <file name="src/pkg/sub/Example2.java">
+ package pkg.sub;
+ public class Example2
+ {
+ public static void main(String[] args)
+ {
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ As it did with the &b-link-JavaH; Builder,
+ &SCons; remembers the class directory
+ and passes it as the <option>-classpath</option> option
+ to &rmic:
+
+ </para>
+
+ <scons_output example="RMIC">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ This example would generate the files
+ <filename>outdir/pkg/sub/Example1_Skel.class</filename>,
+ <filename>outdir/pkg/sub/Example1_Stub.class</filename>,
+ <filename>outdir/pkg/sub/Example2_Skel.class</filename> and
+ <filename>outdir/pkg/sub/Example2_Stub.class</filename>.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ So far, we've 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.
+
+ </para>
+
+ <section>
+ <title>Building Java Class Files: the &b-Java; Builder</title>
+
+ <para>
+
+ The basic activity when programming in Java,
+ of course, is to take one or more <filename>.java</filename> files
+ containing Java source code
+ and to call the Java compiler
+ to turn them into one or more
+ <filename>.class</filename> files.
+ In &SCons;, you do this
+ by giving the &b-link-Java; Builder
+ a target directory in which
+ to put the <filename>.class</filename> files,
+ and a source directory that contains
+ the <filename>.java</filename> files:
+
+ </para>
+
+ <programlisting>
+ Java('classes', 'src')
+ </programlisting>
+
+ <para>
+
+ If the <filename>src</filename> directory contains
+ three <filename>.java</filename> source files,
+ then running &SCons; might look like this:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java
+ </screen>
+
+ <para>
+
+ &SCons; will actually search the <filename>src</filename>
+ directory tree for all of the <filename>.java</filename> files.
+ The Java compiler will then create the
+ necessary class files in the <filename>classes</filename> subdirectory,
+ based on the class names found in the <filename>.java</filename> files.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>How &SCons; Handles Java Dependencies</title>
+
+ <para>
+
+ In addition to searching the source directory for
+ <filename>.java</filename> files,
+ &SCons; actually runs the <filename>.java</filename> 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 <filename>.class</filename> files
+ will be produced by the &javac; call.
+ So our one-liner example from the preceding section:
+
+ </para>
+
+ <programlisting>
+ Java('classes', 'src')
+ </programlisting>
+
+ <para>
+
+ Will not only tell you reliably
+ that the <filename>.class</filename> files
+ in the <filename>classes</filename> subdirectory
+ are up-to-date:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java
+ % <userinput>scons -Q classes</userinput>
+ scons: `classes' is up to date.
+ </screen>
+
+ <para>
+
+ But it will also remove all of the generated
+ <filename>.class</filename> files,
+ even for inner classes,
+ without you having to specify them manually.
+ For example, if our
+ <filename>Example1.java</filename>
+ and
+ <filename>Example3.java</filename>
+ files both define additional classes,
+ and the class defined in <filename>Example2.java</filename>
+ has an inner class,
+ running <userinput>scons -c</userinput>
+ will clean up all of those <filename>.class</filename> files
+ as well:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java
+ % <userinput>scons -Q -c classes</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ To ensure correct handling of <filename>.class</filename>
+ dependencies in all cases, you need to tell &SCons; which Java
+ version is being used. This is needed because Java 1.5 changed
+ the <filename>.class</filename> file names for nested anonymous
+ inner classes. Use the <varname>JAVAVERSION</varname> construction
+ variable to specify the version in use. With Java 1.6, the
+ one-liner example can then be defined like this:
+
+ </para>
+
+ <programlisting>
+ Java('classes', 'src', JAVAVERSION='1.6')
+ </programlisting>
+
+ <para>
+ See <varname>JAVAVERSION</varname> in the man page for more information.
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder</title>
+
+ <para>
+
+ After building the class files,
+ it's common to collect them into
+ a Java archive (<filename>.jar</filename>) 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:
+
+ </para>
+
+ <programlisting>
+ Java(target = 'classes', source = 'src')
+ Jar(target = 'test.jar', source = 'classes')
+ </programlisting>
+
+ <para>
+
+ &SCons; will then pass that directory
+ to the &jar; command,
+ which will collect all of the underlying
+ <filename>.class</filename> files:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java
+ jar cf test.jar classes
+ </screen>
+
+ <para>
+
+ If you want to keep all of the
+ <filename>.class</filename> files
+ for multiple programs in one location,
+ and only archive some of them in
+ each <filename>.jar</filename> file,
+ you can pass the &b-Jar; builder a
+ list of files as its source.
+ It's extremely simple to create multiple
+ <filename>.jar</filename> 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:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ This will then create
+ <filename>prog1.jar</filename>
+ and <filename>prog2.jar</filename>
+ next to the subdirectories
+ that contain their <filename>.java</filename> files:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Building C Header and Stub Files: the &b-JavaH; Builder</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ classes = Java(target = 'classes', source = 'src/pkg/sub')
+ JavaH(target = 'native', source = classes)
+ </programlisting>
+
+ <para>
+
+ 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 <option>-d</option>
+ when &SCons; runs &javah;:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ In this case,
+ the call to &javah;
+ will generate the header files
+ <filename>native/pkg_sub_Example1.h</filename>,
+ <filename>native/pkg_sub_Example2.h</filename>
+ and
+ <filename>native/pkg_sub_Example3.h</filename>.
+ Notice that &SCons; remembered that the class
+ files were generated with a target directory of
+ <filename>classes</filename>,
+ and that it then specified that target directory
+ as the <option>-classpath</option> option
+ to the call to &javah;.
+
+ </para>
+
+ <para>
+
+ 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 <emphasis>can</emphasis>
+ 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;:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ The &cv-JAVACLASSDIR; value then
+ gets converted into the <option>-classpath</option>
+ when &SCons; runs &javah;:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ classes = Java(target = 'classes', source = 'src/pkg/sub')
+ JavaH(target = File('native.h'), source = classes)
+ </programlisting>
+
+ <para>
+
+ 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 <filename>native.h</filename>.
+ When a file is used, though,
+ &SCons; correctly converts the file name
+ into the &javah; <option>-o</option> option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder</title>
+
+ <para>
+
+ 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 <filename>_Stub.class</filename>
+ and <filename>_Skel.class</filename> files will
+ be placed:
+
+ </para>
+
+ <programlisting>
+ classes = Java(target = 'classes', source = 'src/pkg/sub')
+ RMIC(target = 'outdir', source = classes)
+ </programlisting>
+
+ <para>
+
+ As it did with the &b-link-JavaH; Builder,
+ &SCons; remembers the class directory
+ and passes it as the <option>-classpath</option> option
+ to &rmic;:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ This example would generate the files
+ <filename>outdir/pkg/sub/Example1_Skel.class</filename>,
+ <filename>outdir/pkg/sub/Example1_Stub.class</filename>,
+ <filename>outdir/pkg/sub/Example2_Skel.class</filename> and
+ <filename>outdir/pkg/sub/Example2_Stub.class</filename>.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Specifying the Name of the Target (Output) File</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="target">
+ <file name="SConstruct" printme="1">
+ Program('new_hello', 'hello.c')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ (&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:
+ <literal>"program = source files"</literal>.)
+
+ </para>
+
+ <para>
+
+ Now &SCons; will build an executable program
+ named &new_hello; when run on a POSIX system:
+
+ </para>
+
+ <scons_output example="target" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And &SCons; will build an executable program
+ named &new_hello_exe; when run on a Windows system:
+
+ </para>
+
+ <scons_output example="target" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Compiling Multiple Source Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ Program(['prog.c', 'file1.c', 'file2.c'])
+ </file>
+ <file name="prog.c">
+ int main() { printf("prog.c\n"); }
+ </file>
+ <file name="file1.c">
+ void file1() { printf("file1.c\n"); }
+ </file>
+ <file name="file2.c">
+ void file2() { printf("file2.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ A build of the above example would look like:
+
+ </para>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct" printme="1">
+ Program('program', ['prog.c', 'file1.c', 'file2.c'])
+ </file>
+ <file name="prog.c">
+ int main() { printf("prog.c\n"); }
+ </file>
+ <file name="file1.c">
+ void file1() { printf("file1.c\n"); }
+ </file>
+ <file name="file2.c">
+ void file2() { printf("file2.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ On Linux, a build of this example would look like:
+
+ </para>
+
+ <scons_output example="ex3" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Or on Windows:
+
+ </para>
+
+ <scons_output example="ex3" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Making a list of files with &Glob;</title>
+
+ <para>
+
+ You can also use the &Glob; function to find all files matching a
+ certain template, using the standard shell pattern matching
+ characters <literal>*</literal>, <literal>?</literal>
+ and <literal>[abc]</literal> to match any of
+ <literal>a</literal>, <literal>b</literal> or <literal>c</literal>.
+ <literal>[!abc]</literal> is also supported,
+ to match any character <emphasis>except</emphasis>
+ <literal>a</literal>, <literal>b</literal> or <literal>c</literal>.
+ This makes many multi-source-file builds quite easy:
+
+ </para>
+
+ <sconstruct>
+ Program('program', Glob('*.c'))
+ </sconstruct>
+
+ <para>
+
+ The SCons man page has more details on using &Glob;
+ with variant directories
+ (see <xref linkend="chap-variants"></xref>, below)
+ and repositories
+ (see <xref linkend="chap-repositories"></xref>, below),
+ and returning strings rather than Nodes.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Specifying Single Files Vs. Lists of Files</title>
+
+ <para>
+
+ We've now shown you two ways to specify
+ the source for a program,
+ one with a list of files:
+
+ </para>
+
+ <sconstruct>
+ Program('hello', ['file1.c', 'file2.c'])
+ </sconstruct>
+
+ <para>
+
+ And one with a single file:
+
+ </para>
+
+ <sconstruct>
+ Program('hello', 'hello.c')
+ </sconstruct>
+
+ <para>
+
+ You could actually put a single file name in a list, too,
+ which you might prefer just for the sake of consistency:
+
+ </para>
+
+ <sconstruct>
+ Program('hello', ['hello.c'])
+ </sconstruct>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <important>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ # The following two calls both work correctly:
+ Program('program1', 'program1.c')
+ Program('program2', ['program2.c'])
+ </sconstruct>
+
+ <para>
+
+ Trying to do "Python things" that mix strings and
+ lists will cause errors or lead to incorrect results:
+
+ </para>
+
+ <sconstruct>
+ 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'])
+ </sconstruct>
+
+ </important>
+
+ </section>
+
+ <section>
+ <title>Making Lists of Files Easier to Read</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('program', Split('main.c file1.c file2.c'))
+ </programlisting>
+
+ <para>
+
+ (If you're already familiar with Python,
+ you'll have realized that this is similar to the
+ <function>split()</function> method
+ in the Python standard <function>string</function> module.
+ Unlike the <function>string.split()</function> 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.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ src_files = Split('main.c file1.c file2.c')
+ Program('program', src_files)
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ src_files = Split("""main.c
+ file1.c
+ file2.c""")
+ Program('program', src_files)
+ </programlisting>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Keyword Arguments</title>
+
+ <para>
+
+ &SCons; also allows you to identify
+ the output file and input source files
+ using Python keyword arguments.
+ The output file is known as the
+ <emphasis>target</emphasis>,
+ and the source file(s) are known (logically enough) as the
+ <emphasis>source</emphasis>.
+ The Python syntax for this is:
+
+ </para>
+
+ <programlisting>
+ src_files = Split('main.c file1.c file2.c')
+ Program(target = 'program', source = src_files)
+ </programlisting>
+
+ <para>
+
+ Because the keywords explicitly identify
+ what each argument is,
+ you can actually reverse the order if you prefer:
+
+ </para>
+
+ <programlisting>
+ src_files = Split('main.c file1.c file2.c')
+ Program(source = src_files, target = 'program')
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Compiling Multiple Programs</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct" printme="1">
+ Program('foo.c')
+ Program('bar', ['bar1.c', 'bar2.c'])
+ </file>
+ <file name="foo.c">
+ int main() { printf("foo.c\n"); }
+ </file>
+ <file name="bar1.c">
+ int main() { printf("bar1.c\n"); }
+ </file>
+ <file name="bar2.c">
+ void bar2() { printf("bar2.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; would then build the programs as follows:
+
+ </para>
+
+ <scons_output example="ex4">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Sharing Source Files Between Multiple Programs</title>
+
+ <para>
+
+ 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
+ <xref linkend="chap-libraries"></xref>, below.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ Program(Split('foo.c common1.c common2.c'))
+ Program('bar', Split('bar1.c bar2.c common1.c common2.c'))
+ </file>
+ <file name="foo.c">
+ int main() { printf("foo.c\n"); }
+ </file>
+ <file name="bar1.c">
+ int main() { printf("bar1.c\n"); }
+ </file>
+ <file name="bar2.c">
+ int bar2() { printf("bar2.c\n"); }
+ </file>
+ <file name="common1.c">
+ void common1() { printf("common1.c\n"); }
+ </file>
+ <file name="common2.c">
+ void common22() { printf("common2.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <scons_output example="ex5">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 &plus; operator:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ This is functionally equivalent to the previous example.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Specifying the Name of the Target (Output) File</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('new_hello', 'hello.c')
+ </programlisting>
+
+ <para>
+
+ (&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:
+ <literal>"program = source files"</literal>.)
+
+ </para>
+
+ <para>
+
+ Now &SCons; will build an executable program
+ named &new_hello; when run on a POSIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o new_hello hello.o
+ </screen>
+
+ <para>
+
+ And &SCons; will build an executable program
+ named &new_hello_exe; when run on a Windows system:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ cl /Fohello.obj /c hello.c /nologo
+ link /nologo /OUT:new_hello.exe hello.obj
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Compiling Multiple Source Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program(['prog.c', 'file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ A build of the above example would look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('program', ['prog.c', 'file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ On Linux, a build of this example would look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Or on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Making a list of files with &Glob;</title>
+
+ <para>
+
+ You can also use the &Glob; function to find all files matching a
+ certain template, using the standard shell pattern matching
+ characters <literal>*</literal>, <literal>?</literal>
+ and <literal>[abc]</literal> to match any of
+ <literal>a</literal>, <literal>b</literal> or <literal>c</literal>.
+ <literal>[!abc]</literal> is also supported,
+ to match any character <emphasis>except</emphasis>
+ <literal>a</literal>, <literal>b</literal> or <literal>c</literal>.
+ This makes many multi-source-file builds quite easy:
+
+ </para>
+
+ <programlisting>
+ Program('program', Glob('*.c'))
+ </programlisting>
+
+ <para>
+
+ The SCons man page has more details on using &Glob;
+ with variant directories
+ (see <xref linkend="chap-variants"></xref>, below)
+ and repositories
+ (see <xref linkend="chap-repositories"></xref>, below),
+ and returning strings rather than Nodes.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Specifying Single Files Vs. Lists of Files</title>
+
+ <para>
+
+ We've now shown you two ways to specify
+ the source for a program,
+ one with a list of files:
+
+ </para>
+
+ <programlisting>
+ Program('hello', ['file1.c', 'file2.c'])
+ </programlisting>
+
+ <para>
+
+ And one with a single file:
+
+ </para>
+
+ <programlisting>
+ Program('hello', 'hello.c')
+ </programlisting>
+
+ <para>
+
+ You could actually put a single file name in a list, too,
+ which you might prefer just for the sake of consistency:
+
+ </para>
+
+ <programlisting>
+ Program('hello', ['hello.c'])
+ </programlisting>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <important>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ # The following two calls both work correctly:
+ Program('program1', 'program1.c')
+ Program('program2', ['program2.c'])
+ </programlisting>
+
+ <para>
+
+ Trying to do "Python things" that mix strings and
+ lists will cause errors or lead to incorrect results:
+
+ </para>
+
+ <programlisting>
+ 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'])
+ </programlisting>
+
+ </important>
+
+ </section>
+
+ <section>
+ <title>Making Lists of Files Easier to Read</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('program', Split('main.c file1.c file2.c'))
+ </programlisting>
+
+ <para>
+
+ (If you're already familiar with Python,
+ you'll have realized that this is similar to the
+ <function>split()</function> method
+ in the Python standard <function>string</function> module.
+ Unlike the <function>string.split()</function> 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.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ src_files = Split('main.c file1.c file2.c')
+ Program('program', src_files)
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ src_files = Split("""main.c
+ file1.c
+ file2.c""")
+ Program('program', src_files)
+ </programlisting>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Keyword Arguments</title>
+
+ <para>
+
+ &SCons; also allows you to identify
+ the output file and input source files
+ using Python keyword arguments.
+ The output file is known as the
+ <emphasis>target</emphasis>,
+ and the source file(s) are known (logically enough) as the
+ <emphasis>source</emphasis>.
+ The Python syntax for this is:
+
+ </para>
+
+ <programlisting>
+ src_files = Split('main.c file1.c file2.c')
+ Program(target = 'program', source = src_files)
+ </programlisting>
+
+ <para>
+
+ Because the keywords explicitly identify
+ what each argument is,
+ you can actually reverse the order if you prefer:
+
+ </para>
+
+ <programlisting>
+ src_files = Split('main.c file1.c file2.c')
+ Program(source = src_files, target = 'program')
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Compiling Multiple Programs</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('foo.c')
+ Program('bar', ['bar1.c', 'bar2.c'])
+ </programlisting>
+
+ <para>
+
+ &SCons; would then build the programs as follows:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Sharing Source Files Between Multiple Programs</title>
+
+ <para>
+
+ 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
+ <xref linkend="chap-libraries"></xref>, below.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program(Split('foo.c common1.c common2.c'))
+ Program('bar', Split('bar1.c bar2.c common1.c common2.c'))
+ </programlisting>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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 &plus; operator:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ This is functionally equivalent to the previous example.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Building Libraries</title>
+
+ <para>
+
+ You build your own libraries by specifying &b-link-Library;
+ instead of &b-link-Program;:
+
+ </para>
+
+ <scons_example name="ex1" printme="1">
+ <file name="SConstruct" printme="1">
+ Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ </file>
+ <file name="f1.c">
+ void f1() { printf("f1.c\n"); }
+ </file>
+ <file name="f2.c">
+ void f2() { printf("f2.c\n"); }
+ </file>
+ <file name="f3.c">
+ void f3() { printf("f3.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &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):
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ On a Windows system,
+ a build of the above example would look like:
+
+ </para>
+
+ <scons_output example="ex1" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Building Libraries From Source Code or Object Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="objects" printme="1">
+ <file name="SConstruct" printme="1">
+ Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
+ </file>
+ <file name="f1.c">
+ void f1() { printf("f1.c\n"); }
+ </file>
+ <file name="f2.o">
+ object file
+ </file>
+ <file name="f3.c">
+ void f3() { printf("f3.c\n"); }
+ </file>
+ <file name="f4.o">
+ object file
+ </file>
+ </scons_example>
+
+ <para>
+
+ And SCons realizes that only the source code files
+ must be compiled into object files
+ before creating the final library:
+
+ </para>
+
+ <scons_output example="objects" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Of course, in this example, the object files
+ must already exist for the build to succeed.
+ See <xref linkend="chap-nodes"></xref>, below,
+ for information about how you can
+ build object files explicitly
+ and include the built files in a library.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Static Libraries Explicitly: the &b-StaticLibrary; Builder</title>
+
+ <para>
+
+ 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;:
+
+ </para>
+
+ <scons_example name="StaticLibrary" printme="1">
+ <file name="SConstruct" printme="1">
+ StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
+ </file>
+ </scons_example>
+
+ <para>
+
+ There is no functional difference between the
+ &b-link-StaticLibrary; and &b-Library; functions.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="SharedLibrary" printme="1">
+ <file name="SConstruct" printme="1">
+ SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
+ </file>
+ <file name="f1.c">
+ void f1() { printf("f1.c\n"); }
+ </file>
+ <file name="f2.c">
+ void f2() { printf("f2.c\n"); }
+ </file>
+ <file name="f3.c">
+ void f3() { printf("f3.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ The output on POSIX:
+
+ </para>
+
+ <scons_output example="SharedLibrary" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And the output on Windows:
+
+ </para>
+
+ <scons_output example="SharedLibrary" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice again that &SCons; takes care of
+ building the output file correctly,
+ adding the <literal>-shared</literal> option
+ for a POSIX compilation,
+ and the <literal>/dll</literal> option on Windows.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Linking with Libraries</title>
+
+ <para>
+
+ 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:
+
+ <!-- In the preceding paragraph, the "$" notation for
+ LIBS, LIBPATH etc. is used for the first time.
+ Maybe some words of explanation would be nice. -->
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.')
+ </file>
+ <file name="f1.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="f2.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="f3.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="prog.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Notice, of course, that you don't need to specify a library
+ prefix (like <literal>lib</literal>)
+ or suffix (like <literal>.a</literal> or <literal>.lib</literal>).
+ &SCons; uses the correct prefix or suffix for the current system.
+
+ </para>
+
+ <para>
+
+ On a POSIX or Linux system,
+ a build of the above example would look like:
+
+ </para>
+
+ <scons_output example="ex2" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ On a Windows system,
+ a build of the above example would look like:
+
+ </para>
+
+ <scons_output example="ex2" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ As usual, notice that &SCons; has taken care
+ of constructing the correct command lines
+ to link with the specified library on each system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ Program('prog.c', LIBS='foo', LIBPATH='.')
+ </sconstruct>
+
+ <para>
+
+ is equivalent to:
+
+ </para>
+
+ <sconstruct>
+ Program('prog.c', LIBS=['foo'], LIBPATH='.')
+ </sconstruct>
+
+ <para>
+
+ This is similar to the way that &SCons;
+ handles either a string or a list to
+ specify a single source file.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding Libraries: the &cv-LIBPATH; Construction Variable</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct" printme="1">
+ Program('prog.c', LIBS = 'm',
+ LIBPATH = ['/usr/lib', '/usr/local/lib'])
+ </file>
+ <file name="prog.c">
+ int main() { printf("prog.c\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ LIBPATH = '/usr/lib:/usr/local/lib'
+ </sconstruct>
+
+ <para>
+
+ or a semi-colon on Windows systems:
+
+ </para>
+
+ <sconstruct>
+ LIBPATH = 'C:\\lib;D:\\lib'
+ </sconstruct>
+
+ <para>
+
+ (Note that Python requires that the backslash
+ separators in a Windows path name
+ be escaped within strings.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ex3" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ On a Windows system,
+ a build of the above example would look like:
+
+ </para>
+
+ <scons_output example="ex3" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ <!-- The link command is too wide in the PDF version.
+ There are some other examples of this throughout the document. -->
+
+ <para>
+
+ Note again that &SCons; has taken care of
+ the system-specific details of creating
+ the right command-line options.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Building Libraries</title>
+
+ <para>
+
+ You build your own libraries by specifying &b-link-Library;
+ instead of &b-link-Program;:
+
+ </para>
+
+ <programlisting>
+ Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ </programlisting>
+
+ <para>
+
+ &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):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ On a Windows system,
+ a build of the above example would look like:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Building Libraries From Source Code or Object Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
+ </programlisting>
+
+ <para>
+
+ And SCons realizes that only the source code files
+ must be compiled into object files
+ before creating the final library:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Of course, in this example, the object files
+ must already exist for the build to succeed.
+ See <xref linkend="chap-nodes"></xref>, below,
+ for information about how you can
+ build object files explicitly
+ and include the built files in a library.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Static Libraries Explicitly: the &b-StaticLibrary; Builder</title>
+
+ <para>
+
+ 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;:
+
+ </para>
+
+ <programlisting>
+ StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
+ </programlisting>
+
+ <para>
+
+ There is no functional difference between the
+ &b-link-StaticLibrary; and &b-Library; functions.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
+ </programlisting>
+
+ <para>
+
+ The output on POSIX:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ And the output on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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)
+ </screen>
+
+ <para>
+
+ Notice again that &SCons; takes care of
+ building the output file correctly,
+ adding the <literal>-shared</literal> option
+ for a POSIX compilation,
+ and the <literal>/dll</literal> option on Windows.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Linking with Libraries</title>
+
+ <para>
+
+ 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:
+
+ <!-- In the preceding paragraph, the "$" notation for
+ LIBS, LIBPATH etc. is used for the first time.
+ Maybe some words of explanation would be nice. -->
+
+ </para>
+
+ <programlisting>
+ Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.')
+ </programlisting>
+
+ <para>
+
+ Notice, of course, that you don't need to specify a library
+ prefix (like <literal>lib</literal>)
+ or suffix (like <literal>.a</literal> or <literal>.lib</literal>).
+ &SCons; uses the correct prefix or suffix for the current system.
+
+ </para>
+
+ <para>
+
+ On a POSIX or Linux system,
+ a build of the above example would look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ On a Windows system,
+ a build of the above example would look like:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+
+ As usual, notice that &SCons; has taken care
+ of constructing the correct command lines
+ to link with the specified library on each system.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('prog.c', LIBS='foo', LIBPATH='.')
+ </programlisting>
+
+ <para>
+
+ is equivalent to:
+
+ </para>
+
+ <programlisting>
+ Program('prog.c', LIBS=['foo'], LIBPATH='.')
+ </programlisting>
+
+ <para>
+
+ This is similar to the way that &SCons;
+ handles either a string or a list to
+ specify a single source file.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding Libraries: the &cv-LIBPATH; Construction Variable</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Program('prog.c', LIBS = 'm',
+ LIBPATH = ['/usr/lib', '/usr/local/lib'])
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ LIBPATH = '/usr/lib:/usr/local/lib'
+ </programlisting>
+
+ <para>
+
+ or a semi-colon on Windows systems:
+
+ </para>
+
+ <programlisting>
+ LIBPATH = 'C:\\lib;D:\\lib'
+ </programlisting>
+
+ <para>
+
+ (Note that Python requires that the backslash
+ separators in a Windows path name
+ be escaped within strings.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o prog.o -c prog.c
+ cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm
+ </screen>
+
+ <para>
+
+ On a Windows system,
+ a build of the above example would look like:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+ <!-- The link command is too wide in the PDF version.
+ There are some other examples of this throughout the document. -->
+
+ <para>
+
+ Note again that &SCons; has taken care of
+ the system-specific details of creating
+ the right command-line options.
+
+ </para>
+
+ </section>
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 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+
+ <!ENTITY % version SYSTEM "../version.xml">
+ %version;
+
+ <!ENTITY % scons SYSTEM "../scons.mod">
+ %scons;
+
+ <!ENTITY % builders-mod SYSTEM "builders.mod">
+ %builders-mod;
+
+ <!ENTITY % tools-mod SYSTEM "tools.mod">
+ %tools-mod;
+
+ <!ENTITY % variables-mod SYSTEM "variables.mod">
+ %variables-mod;
+
+ <!ENTITY actions SYSTEM "actions.xml">
+ <!ENTITY alias SYSTEM "alias.xml">
+ <!ENTITY ant SYSTEM "ant.xml">
+ <!ENTITY build-install SYSTEM "build-install.xml">
+ <!ENTITY builders SYSTEM "builders.xml">
+ <!ENTITY builders-built-in SYSTEM "builders-built-in.xml">
+ <!ENTITY builders-commands SYSTEM "builders-commands.xml">
+ <!ENTITY builders-writing SYSTEM "builders-writing.xml">
+ <!ENTITY add-method SYSTEM "add-method.xml">
+ <!ENTITY caching SYSTEM "caching.xml">
+ <!ENTITY command-line SYSTEM "command-line.xml">
+ <!ENTITY copyright SYSTEM "copyright.xml">
+ <!ENTITY depends SYSTEM "depends.xml">
+ <!ENTITY environments SYSTEM "environments.xml">
+ <!ENTITY errors SYSTEM "errors.xml">
+ <!ENTITY example SYSTEM "example.xml">
+ <!ENTITY factories SYSTEM "factories.xml">
+ <!ENTITY file-removal SYSTEM "file-removal.xml">
+ <!ENTITY hierarchy SYSTEM "hierarchy.xml">
+ <!ENTITY java SYSTEM "java.xml">
+ <!ENTITY install SYSTEM "install.xml">
+ <!ENTITY less-simple SYSTEM "less-simple.xml">
+ <!ENTITY libraries SYSTEM "libraries.xml">
+ <!ENTITY make SYSTEM "make.xml">
+ <!ENTITY mergeflags SYSTEM "mergeflags.xml">
+ <!ENTITY misc SYSTEM "misc.xml">
+ <!ENTITY nodes SYSTEM "nodes.xml">
+ <!ENTITY output SYSTEM "output.xml">
+ <!ENTITY parseconfig SYSTEM "parseconfig.xml">
+ <!ENTITY parseflags SYSTEM "parseflags.xml">
+ <!ENTITY preface SYSTEM "preface.xml">
+ <!ENTITY python SYSTEM "python.xml">
+ <!ENTITY repositories SYSTEM "repositories.xml">
+ <!ENTITY run SYSTEM "run.xml">
+ <!ENTITY scanners SYSTEM "scanners.xml">
+ <!ENTITY sconf SYSTEM "sconf.xml">
+ <!ENTITY separate SYSTEM "separate.xml">
+ <!ENTITY simple SYSTEM "simple.xml">
+ <!ENTITY sourcecode SYSTEM "sourcecode.xml">
+ <!ENTITY tasks SYSTEM "tasks.xml">
+ <!ENTITY tools SYSTEM "tools.xml">
+ <!ENTITY troubleshoot SYSTEM "troubleshoot.xml">
+ <!ENTITY variables-xml SYSTEM "variables.xml">
+ <!ENTITY variants SYSTEM "variants.xml">
+
+ <!ENTITY builders-gen SYSTEM "builders.gen">
+ <!ENTITY tools-gen SYSTEM "tools.gen">
+ <!ENTITY variables-gen SYSTEM "variables.gen">
+
+]>
+
+ <!--
+
+ XXX FindFile()
+ XXX FindPathDirs()
+ XXX GetBuildPath()
+ XXX GetLaunchDir()
+
+ XXX ParseDepends()
+ XXX Platform()
+ XXX SConsignFile()
+ XXX Tools()
+
+ XXX GetOption('duplicate')
+ XXX SetOption('duplicate')
+ XXX - - duplicate=
+
+ XXX CheckTypeSize()
+
+ XXX - - diskcheck=
+
+ XXX - - warn=
+
+ -->
+
+<book>
+ <bookinfo>
+ <title>SCons User Guide &buildversion;</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <edition>Revision &buildrevision; (&builddate;)</edition>
+
+ <pubdate>2004, 2005, 2006, 2007, 2008</pubdate>
+
+ <copyright>
+ <year>2004, 2005, 2006, 2007, 2008</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <legalnotice>
+ &copyright;
+ </legalnotice>
+
+ <releaseinfo>version &buildversion;</releaseinfo>
+
+ </bookinfo>
+
+ <preface id="chap-preface">
+ <title>Preface</title>
+ &preface;
+ </preface>
+
+ <chapter id="chap-build-install">
+ <title>Building and Installing &SCons;</title>
+ &build-install;
+ </chapter>
+
+ <chapter id="chap-simple">
+ <title>Simple Builds</title>
+ &simple;
+ </chapter>
+
+ <chapter id="chap-less-simple">
+ <title>Less Simple Things to Do With Builds</title>
+ &less-simple;
+ </chapter>
+
+ <chapter id="chap-libraries">
+ <title>Building and Linking with Libraries</title>
+ &libraries;
+ </chapter>
+
+ <chapter id="chap-nodes">
+ <title>Node Objects</title>
+ &nodes;
+ </chapter>
+
+ <chapter id="chap-depends">
+ <title>Dependencies</title>
+ &depends;
+ </chapter>
+
+ <chapter id="chap-environments">
+ <title>Environments</title>
+ &environments;
+ </chapter>
+
+ <!-- These next three sections should be combined into one chapter -->
+ <chapter id="chap-mergeflags">
+ <title>Merging Options into the Environment: the &MergeFlags; Function</title>
+ &mergeflags;
+ </chapter>
+ <chapter id="chap-parseflags">
+ <title>Separating Compile Arguments into their Variables: the &ParseFlags; Function</title>
+ &parseflags;
+ </chapter>
+ <chapter id="chap-parseconfig">
+ <title>Finding Installed Library Information: the &ParseConfig; Function</title>
+ &parseconfig;
+ </chapter>
+
+ <chapter id="chap-output">
+ <title>Controlling Build Output</title>
+ &output;
+ </chapter>
+
+ <chapter id="chap-command-line">
+ <title>Controlling a Build From the Command Line</title>
+ &command-line;
+ </chapter>
+
+ <chapter id="chap-install">
+ <title>Installing Files in Other Directories: the &Install; Builder</title>
+ &install;
+ </chapter>
+
+ <chapter id="chap-factories">
+ <title>Platform-Independent File System Manipulation</title>
+ &factories;
+ </chapter>
+
+ <chapter id="chap-file-removal">
+ <title>Controlling Removal of Targets</title>
+ &file-removal;
+ </chapter>
+
+ <chapter id="chap-hierarchical">
+ <title>Hierarchical Builds</title>
+ &hierarchy;
+ </chapter>
+
+ <chapter id="chap-separate">
+ <title>Separating Source and Build Directories</title>
+ &separate;
+ </chapter>
+
+ <chapter id="chap-variants">
+ <title>Variant Builds</title>
+ &variants;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-builders-built-in">
+ <title>Built-In Builders</title>
+ &builders-built-in;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-builders-writing">
+ <title>Writing Your Own Builders</title>
+ &builders-writing;
+ </chapter>
+
+ <chapter id="chap-builders-commands">
+ <title>Not Writing a Builder: the &Command; Builder</title>
+ &builders-commands;
+ </chapter>
+
+ <chapter id="chap-add-method">
+ <title>Pseudo-Builders: the AddMethod function</title>
+ &add-method;
+ </chapter>
+
+ <!--
+
+ XXX Action()
+ XXX AddPostAction()
+ XXX AddPreAction()
+
+ <chapter id="chap-actions">
+ <title>&SCons; Actions</title>
+ &actions;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-scanners">
+ <title>Writing Scanners</title>
+ &scanners;
+ </chapter>
+
+ <chapter id="chap-repositories">
+ <title>Building From Code Repositories</title>
+ &repositories;
+ </chapter>
+
+ <chapter id="chap-sconf">
+ <title>Multi-Platform Configuration (&Autoconf; Functionality)</title>
+ &sconf;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-sourcecode">
+ <title>Fetching Files From Source Code Management Systems</title>
+ &sourcecode;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-caching">
+ <title>Caching Built Files</title>
+ &caching;
+ </chapter>
+
+ <chapter id="chap-alias">
+ <title>Alias Targets</title>
+ &alias;
+ </chapter>
+
+ <chapter id="chap-java">
+ <title>Java Builds</title>
+ &java;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-run">
+ <title>How to Run &SCons;</title>
+ &run;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-misc">
+ <title>Miscellaneous Functionality</title>
+ &misc;
+ </chapter>
+
+ <chapter id="chap-troubleshooting">
+ <title>Troubleshooting</title>
+ &troubleshoot;
+ </chapter>
+
+ <appendix id="app-variables">
+ <title>Construction Variables</title>
+ &variables-xml;
+ </appendix>
+
+ <appendix id="app-builders">
+ <title>Builders</title>
+ &builders;
+ </appendix>
+
+ <appendix id="app-tools">
+ <title>Tools</title>
+ &tools;
+ </appendix>
+
+ <appendix id="app-tasks">
+ <title>Handling Common Tasks</title>
+ &tasks;
+ </appendix>
+
+ <!--
+
+ <appendix id="app-python">
+ <title>Python Overview</title>
+ &example;
+ </appendix>
+
+ <appendix id="app-example">
+ <title>Complex &SCons; Example</title>
+ &example;
+ </appendix>
+
+ <appendix id="app-make">
+ <title>Converting From Make</title>
+ &make;
+ </appendix>
+
+ <appendix id="app-cons">
+ <title>Converting From Cons</title>
+ &cons;
+ </appendix>
+
+ <appendix id="app-ant">
+ <title>Converting From Ant</title>
+ &ant;
+ </appendix>
+
+ -->
+
+</book>
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 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+
+ <!ENTITY % version SYSTEM "../version.xml">
+ %version;
+
+ <!ENTITY % scons SYSTEM "../scons.mod">
+ %scons;
+
+ <!ENTITY % builders-mod SYSTEM "builders.mod">
+ %builders-mod;
+
+ <!ENTITY % tools-mod SYSTEM "tools.mod">
+ %tools-mod;
+
+ <!ENTITY % variables-mod SYSTEM "variables.mod">
+ %variables-mod;
+
+ <!ENTITY actions SYSTEM "actions.xml">
+ <!ENTITY alias SYSTEM "alias.xml">
+ <!ENTITY ant SYSTEM "ant.xml">
+ <!ENTITY build-install SYSTEM "build-install.xml">
+ <!ENTITY builders SYSTEM "builders.xml">
+ <!ENTITY builders-built-in SYSTEM "builders-built-in.xml">
+ <!ENTITY builders-commands SYSTEM "builders-commands.xml">
+ <!ENTITY builders-writing SYSTEM "builders-writing.xml">
+ <!ENTITY add-method SYSTEM "add-method.xml">
+ <!ENTITY caching SYSTEM "caching.xml">
+ <!ENTITY command-line SYSTEM "command-line.xml">
+ <!ENTITY copyright SYSTEM "copyright.xml">
+ <!ENTITY depends SYSTEM "depends.xml">
+ <!ENTITY environments SYSTEM "environments.xml">
+ <!ENTITY errors SYSTEM "errors.xml">
+ <!ENTITY example SYSTEM "example.xml">
+ <!ENTITY factories SYSTEM "factories.xml">
+ <!ENTITY file-removal SYSTEM "file-removal.xml">
+ <!ENTITY hierarchy SYSTEM "hierarchy.xml">
+ <!ENTITY java SYSTEM "java.xml">
+ <!ENTITY install SYSTEM "install.xml">
+ <!ENTITY less-simple SYSTEM "less-simple.xml">
+ <!ENTITY libraries SYSTEM "libraries.xml">
+ <!ENTITY make SYSTEM "make.xml">
+ <!ENTITY mergeflags SYSTEM "mergeflags.xml">
+ <!ENTITY misc SYSTEM "misc.xml">
+ <!ENTITY nodes SYSTEM "nodes.xml">
+ <!ENTITY output SYSTEM "output.xml">
+ <!ENTITY parseconfig SYSTEM "parseconfig.xml">
+ <!ENTITY parseflags SYSTEM "parseflags.xml">
+ <!ENTITY preface SYSTEM "preface.xml">
+ <!ENTITY python SYSTEM "python.xml">
+ <!ENTITY repositories SYSTEM "repositories.xml">
+ <!ENTITY run SYSTEM "run.xml">
+ <!ENTITY scanners SYSTEM "scanners.xml">
+ <!ENTITY sconf SYSTEM "sconf.xml">
+ <!ENTITY separate SYSTEM "separate.xml">
+ <!ENTITY simple SYSTEM "simple.xml">
+ <!ENTITY sourcecode SYSTEM "sourcecode.xml">
+ <!ENTITY tasks SYSTEM "tasks.xml">
+ <!ENTITY tools SYSTEM "tools.xml">
+ <!ENTITY troubleshoot SYSTEM "troubleshoot.xml">
+ <!ENTITY variables-xml SYSTEM "variables.xml">
+ <!ENTITY variants SYSTEM "variants.xml">
+
+ <!ENTITY builders-gen SYSTEM "builders.gen">
+ <!ENTITY tools-gen SYSTEM "tools.gen">
+ <!ENTITY variables-gen SYSTEM "variables.gen">
+
+]>
+
+ <!--
+
+ XXX FindFile()
+ XXX FindPathDirs()
+ XXX GetBuildPath()
+ XXX GetLaunchDir()
+
+ XXX ParseDepends()
+ XXX Platform()
+ XXX SConsignFile()
+ XXX Tools()
+
+ XXX GetOption('duplicate')
+ XXX SetOption('duplicate')
+ XXX - - duplicate=
+
+ XXX CheckTypeSize()
+
+ XXX - - diskcheck=
+
+ XXX - - warn=
+
+ -->
+
+<book>
+ <bookinfo>
+ <title>SCons User Guide &buildversion;</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <edition>Revision &buildrevision; (&builddate;)</edition>
+
+ <pubdate>2004, 2005, 2006, 2007, 2008</pubdate>
+
+ <copyright>
+ <year>2004, 2005, 2006, 2007, 2008</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <legalnotice>
+ &copyright;
+ </legalnotice>
+
+ <releaseinfo>version &buildversion;</releaseinfo>
+
+ </bookinfo>
+
+ <preface id="chap-preface">
+ <title>Preface</title>
+ &preface;
+ </preface>
+
+ <chapter id="chap-build-install">
+ <title>Building and Installing &SCons;</title>
+ &build-install;
+ </chapter>
+
+ <chapter id="chap-simple">
+ <title>Simple Builds</title>
+ &simple;
+ </chapter>
+
+ <chapter id="chap-less-simple">
+ <title>Less Simple Things to Do With Builds</title>
+ &less-simple;
+ </chapter>
+
+ <chapter id="chap-libraries">
+ <title>Building and Linking with Libraries</title>
+ &libraries;
+ </chapter>
+
+ <chapter id="chap-nodes">
+ <title>Node Objects</title>
+ &nodes;
+ </chapter>
+
+ <chapter id="chap-depends">
+ <title>Dependencies</title>
+ &depends;
+ </chapter>
+
+ <chapter id="chap-environments">
+ <title>Environments</title>
+ &environments;
+ </chapter>
+
+ <!-- These next three sections should be combined into one chapter -->
+ <chapter id="chap-mergeflags">
+ <title>Merging Options into the Environment: the &MergeFlags; Function</title>
+ &mergeflags;
+ </chapter>
+ <chapter id="chap-parseflags">
+ <title>Separating Compile Arguments into their Variables: the &ParseFlags; Function</title>
+ &parseflags;
+ </chapter>
+ <chapter id="chap-parseconfig">
+ <title>Finding Installed Library Information: the &ParseConfig; Function</title>
+ &parseconfig;
+ </chapter>
+
+ <chapter id="chap-output">
+ <title>Controlling Build Output</title>
+ &output;
+ </chapter>
+
+ <chapter id="chap-command-line">
+ <title>Controlling a Build From the Command Line</title>
+ &command-line;
+ </chapter>
+
+ <chapter id="chap-install">
+ <title>Installing Files in Other Directories: the &Install; Builder</title>
+ &install;
+ </chapter>
+
+ <chapter id="chap-factories">
+ <title>Platform-Independent File System Manipulation</title>
+ &factories;
+ </chapter>
+
+ <chapter id="chap-file-removal">
+ <title>Controlling Removal of Targets</title>
+ &file-removal;
+ </chapter>
+
+ <chapter id="chap-hierarchical">
+ <title>Hierarchical Builds</title>
+ &hierarchy;
+ </chapter>
+
+ <chapter id="chap-separate">
+ <title>Separating Source and Build Directories</title>
+ &separate;
+ </chapter>
+
+ <chapter id="chap-variants">
+ <title>Variant Builds</title>
+ &variants;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-builders-built-in">
+ <title>Built-In Builders</title>
+ &builders-built-in;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-builders-writing">
+ <title>Writing Your Own Builders</title>
+ &builders-writing;
+ </chapter>
+
+ <chapter id="chap-builders-commands">
+ <title>Not Writing a Builder: the &Command; Builder</title>
+ &builders-commands;
+ </chapter>
+
+ <chapter id="chap-add-method">
+ <title>Pseudo-Builders: the AddMethod function</title>
+ &add-method;
+ </chapter>
+
+ <!--
+
+ XXX Action()
+ XXX AddPostAction()
+ XXX AddPreAction()
+
+ <chapter id="chap-actions">
+ <title>&SCons; Actions</title>
+ &actions;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-scanners">
+ <title>Writing Scanners</title>
+ &scanners;
+ </chapter>
+
+ <chapter id="chap-repositories">
+ <title>Building From Code Repositories</title>
+ &repositories;
+ </chapter>
+
+ <chapter id="chap-sconf">
+ <title>Multi-Platform Configuration (&Autoconf; Functionality)</title>
+ &sconf;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-sourcecode">
+ <title>Fetching Files From Source Code Management Systems</title>
+ &sourcecode;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-caching">
+ <title>Caching Built Files</title>
+ &caching;
+ </chapter>
+
+ <chapter id="chap-alias">
+ <title>Alias Targets</title>
+ &alias;
+ </chapter>
+
+ <chapter id="chap-java">
+ <title>Java Builds</title>
+ &java;
+ </chapter>
+
+ <!--
+
+ <chapter id="chap-run">
+ <title>How to Run &SCons;</title>
+ &run;
+ </chapter>
+
+ -->
+
+ <chapter id="chap-misc">
+ <title>Miscellaneous Functionality</title>
+ &misc;
+ </chapter>
+
+ <chapter id="chap-troubleshooting">
+ <title>Troubleshooting</title>
+ &troubleshoot;
+ </chapter>
+
+ <appendix id="app-variables">
+ <title>Construction Variables</title>
+ &variables-xml;
+ </appendix>
+
+ <appendix id="app-builders">
+ <title>Builders</title>
+ &builders;
+ </appendix>
+
+ <appendix id="app-tools">
+ <title>Tools</title>
+ &tools;
+ </appendix>
+
+ <appendix id="app-tasks">
+ <title>Handling Common Tasks</title>
+ &tasks;
+ </appendix>
+
+ <!--
+
+ <appendix id="app-python">
+ <title>Python Overview</title>
+ &example;
+ </appendix>
+
+ <appendix id="app-example">
+ <title>Complex &SCons; Example</title>
+ &example;
+ </appendix>
+
+ <appendix id="app-make">
+ <title>Converting From Make</title>
+ &make;
+ </appendix>
+
+ <appendix id="app-cons">
+ <title>Converting From Cons</title>
+ &cons;
+ </appendix>
+
+ <appendix id="app-ant">
+ <title>Converting From Ant</title>
+ &ant;
+ </appendix>
+
+ -->
+
+</book>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Why Cons? Why not Make?
+
+Cons is a B<make> replacement. In the following paragraphs, we look at a few
+of the undesirable characteristics of make, and typical build environments
+based on make, that motivated the development of Cons.
+
+=head2 Build complexity
+
+Traditional make-based systems of any size tend to become quite complex. The
+original make utility and its derivatives have contributed to this tendency
+in a number of ways. Make is not good at dealing with systems that are
+spread over multiple directories. Various work-arounds are used to overcome
+this difficulty; the usual choice is for make to invoke itself recursively
+for each sub-directory of a build. This leads to complicated code, in which
+it is often unclear how a variable is set, or what effect the setting of a
+variable will have on the build as a whole. The make scripting language has
+gradually been extended to provide more possibilities, but these have
+largely served to clutter an already overextended language. Often, builds
+are done in multiple passes in order to provide appropriate products from
+one directory to another directory. This represents a further increase in
+build complexity.
+
+
+=head2 Build reproducibility
+
+The bane of all makes has always been the correct handling of
+dependencies. Most often, an attempt is made to do a reasonable job of
+dependencies within a single directory, but no serious attempt is made to do
+the job between directories. Even when dependencies are working correctly,
+make's reliance on a simple time stamp comparison to determine whether a
+file is out of date with respect to its dependents is not, in general,
+adequate for determining when a file should be rederived. If an external
+library, for example, is rebuilt and then ``snapped'' into place, the
+timestamps on its newly created files may well be earlier than the last
+local build, since it was built before it became visible.
+
+
+=head2 Variant builds
+
+Make provides only limited facilities for handling variant builds. With the
+proliferation of hardware platforms and the need for debuggable
+vs. optimized code, the ability to easily create these variants is
+essential. More importantly, if variants are created, it is important to
+either be able to separate the variants or to be able to reproduce the
+original or variant at will. With make it is very difficult to separate the
+builds into multiple build directories, separate from the source. And if
+this technique isn't used, it's also virtually impossible to guarantee at
+any given time which variant is present in the tree, without resorting to a
+complete rebuild.
+
+
+=head2 Repositories
+
+Make provides only limited support for building software from code that
+exists in a central repository directory structure. The VPATH feature of
+GNU make (and some other make implementations) is intended to provide this,
+but doesn't work as expected: it changes the path of target file to the
+VPATH name too early in its analysis, and therefore searches for all
+dependencies in the VPATH directory. To ensure correct development builds,
+it is important to be able to create a file in a local build directory and
+have any files in a code repository (a VPATH directory, in make terms) that
+depend on the local file get rebuilt properly. This isn't possible with
+VPATH, without coding a lot of complex repository knowledge directly into
+the makefiles.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Differences Between &Make; and &SCons;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Advantages of &SCons; Over &Make;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Why Cons? Why not Make?
+
+Cons is a B<make> replacement. In the following paragraphs, we look at a few
+of the undesirable characteristics of make, and typical build environments
+based on make, that motivated the development of Cons.
+
+=head2 Build complexity
+
+Traditional make-based systems of any size tend to become quite complex. The
+original make utility and its derivatives have contributed to this tendency
+in a number of ways. Make is not good at dealing with systems that are
+spread over multiple directories. Various work-arounds are used to overcome
+this difficulty; the usual choice is for make to invoke itself recursively
+for each sub-directory of a build. This leads to complicated code, in which
+it is often unclear how a variable is set, or what effect the setting of a
+variable will have on the build as a whole. The make scripting language has
+gradually been extended to provide more possibilities, but these have
+largely served to clutter an already overextended language. Often, builds
+are done in multiple passes in order to provide appropriate products from
+one directory to another directory. This represents a further increase in
+build complexity.
+
+
+=head2 Build reproducibility
+
+The bane of all makes has always been the correct handling of
+dependencies. Most often, an attempt is made to do a reasonable job of
+dependencies within a single directory, but no serious attempt is made to do
+the job between directories. Even when dependencies are working correctly,
+make's reliance on a simple time stamp comparison to determine whether a
+file is out of date with respect to its dependents is not, in general,
+adequate for determining when a file should be rederived. If an external
+library, for example, is rebuilt and then ``snapped'' into place, the
+timestamps on its newly created files may well be earlier than the last
+local build, since it was built before it became visible.
+
+
+=head2 Variant builds
+
+Make provides only limited facilities for handling variant builds. With the
+proliferation of hardware platforms and the need for debuggable
+vs. optimized code, the ability to easily create these variants is
+essential. More importantly, if variants are created, it is important to
+either be able to separate the variants or to be able to reproduce the
+original or variant at will. With make it is very difficult to separate the
+builds into multiple build directories, separate from the source. And if
+this technique isn't used, it's also virtually impossible to guarantee at
+any given time which variant is present in the tree, without resorting to a
+complete rebuild.
+
+
+=head2 Repositories
+
+Make provides only limited support for building software from code that
+exists in a central repository directory structure. The VPATH feature of
+GNU make (and some other make implementations) is intended to provide this,
+but doesn't work as expected: it changes the path of target file to the
+VPATH name too early in its analysis, and therefore searches for all
+dependencies in the VPATH directory. To ensure correct development builds,
+it is important to be able to create a file in a local build directory and
+have any files in a code repository (a VPATH directory, in make terms) that
+depend on the local file get rebuilt properly. This isn't possible with
+VPATH, without coding a lot of complex repository knowledge directly into
+the makefiles.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Differences Between &Make; and &SCons;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Advantages of &SCons; Over &Make;</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ &MergeFlags; tries to be intelligent about merging options.
+ When merging options to any variable
+ whose name ends in <varname>PATH</varname>,
+ &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."
+
+ </para>
+
+ <scons_example name="MergeFlags1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Append(CCFLAGS = '-option -O3 -O1')
+ flags = { 'CCFLAGS' : '-whatever -O3' }
+ env.MergeFlags(flags)
+ print env['CCFLAGS']
+ </file>
+ </scons_example>
+
+ <scons_output example="MergeFlags1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that the default value for &cv-link-CCFLAGS;
+ <!--
+ [TODO: for when we make CLVar public]
+ is a <varname>CLVar</varname>,
+ -->
+ is an internal &SCons; object
+ which automatically converts
+ the options we specified as a string into a list.
+
+ </para>
+
+ <scons_example name="MergeFlags2">
+ <file name="SConstruct" printme="1">
+ 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']
+ </file>
+ </scons_example>
+
+ <scons_output example="MergeFlags2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that the default value for &cv-link-CPPPATH;
+ <!--
+ [TODO: for when we make CLVar public]
+ is a Python list, not a <varname>CLVar</varname>,
+ -->
+ is a normal Python list,
+ so we must specify its values as a list
+ in the dictionary we pass to the &MergeFlags; function.
+
+ </para>
+
+ <para>
+
+ If &MergeFlags; is passed anything other than a dictionary,
+ it calls the &ParseFlags; method to convert it into a dictionary.
+
+ </para>
+
+ <scons_example name="MergeFlags3">
+ <file name="SConstruct" printme="1">
+ 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']
+ </file>
+ </scons_example>
+
+ <scons_output example="MergeFlags3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ &MergeFlags; tries to be intelligent about merging options.
+ When merging options to any variable
+ whose name ends in <varname>PATH</varname>,
+ &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."
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Append(CCFLAGS = '-option -O3 -O1')
+ flags = { 'CCFLAGS' : '-whatever -O3' }
+ env.MergeFlags(flags)
+ print env['CCFLAGS']
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ ['-option', '-O1', '-whatever', '-O3']
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Note that the default value for &cv-link-CCFLAGS;
+ <!--
+ [TODO: for when we make CLVar public]
+ is a <varname>CLVar</varname>,
+ -->
+ is an internal &SCons; object
+ which automatically converts
+ the options we specified as a string into a list.
+
+ </para>
+
+ <programlisting>
+ 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']
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include']
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ Note that the default value for &cv-link-CPPPATH;
+ <!--
+ [TODO: for when we make CLVar public]
+ is a Python list, not a <varname>CLVar</varname>,
+ -->
+ is a normal Python list,
+ so we must specify its values as a list
+ in the dictionary we pass to the &MergeFlags; function.
+
+ </para>
+
+ <para>
+
+ If &MergeFlags; is passed anything other than a dictionary,
+ it calls the &ParseFlags; method to convert it into a dictionary.
+
+ </para>
+
+ <programlisting>
+ 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']
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ ['-option', '-O1', '-whatever', '-O3']
+ ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include']
+ scons: `.' is up to date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &SCons; supports a lot of additional functionality
+ that doesn't readily fit into the other chapters.
+
+ </para>
+
+ <section>
+ <title>Verifying the Python Version: the &EnsurePythonVersion; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ &SCons; provides an &EnsurePythonVersion; function for this.
+ You simply pass it the major and minor versions
+ numbers of the version of Python you require:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing Python version by faking out
+ the infrastructure in some way.
+
+ <scons_example name="EnsurePythonVersion">
+ <file name="SConstruct" printme="1">
+ EnsurePythonVersion(2, 5)
+ </file>
+ </scons_example>
+
+ -->
+
+ <sconstruct>
+ EnsurePythonVersion(2, 5)
+ </sconstruct>
+
+ <para>
+
+ And then &SCons will exit with the following error
+ message when a user runs it with an unsupported
+ earlier version of Python:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing Python version by faking out
+ the infrastructure in some way.
+
+ <scons_output example="EnsurePythonVersion">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Python 2.5 or greater required, but you have Python 2.3.6
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Verifying the SCons Version: the &EnsureSConsVersion; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing SCons version by faking out
+ the infrastructure in some way.
+
+ <scons_example name="EnsureSConsVersion">
+ <file name="SConstruct" printme="1">
+ EnsureSConsVersion(1, 0)
+ </file>
+ </scons_example>
+
+ -->
+
+ <sconstruct>
+ EnsureSConsVersion(1, 0)
+ </sconstruct>
+
+ <para>
+
+ And then &SCons will exit with the following error
+ message when a user runs it with an unsupported
+ earlier version of &SCons;:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing SCons version by faking out
+ the infrastructure in some way.
+
+ <scons_output example="EnsureSConsVersion">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ SCons 1.0 or greater required, but you have SCons 0.98.5
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <scons_example name="Exit">
+ <file name="SConstruct" printme="1">
+ if ARGUMENTS.get('FUTURE'):
+ print "The FUTURE option is not supported yet!"
+ Exit(2)
+ env = Environment()
+ env.Program('hello.c')
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="Exit">
+ <scons_output_command>scons -Q FUTURE=1</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <literal>0</literal>,
+ which indicates successful execution.
+
+ </para>
+
+ <para>
+
+ Note that the &Exit; function
+ is equivalent to calling the Python
+ <function>sys.exit</function> function
+ (which the it actually calls),
+ but because &Exit; is a &SCons; function,
+ you don't have to import the Python
+ <literal>sys</literal> module to use it.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Searching for Files: the &FindFile; Function</title>
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <scons_example name="FindFile1a">
+ <file name="SConstruct" printme="1">
+ # one directory
+ print FindFile('missing', '.')
+ t = FindFile('exists', '.')
+ print t.__class__, t
+ </file>
+ <file name="exists">
+ exists
+ </file>
+ </scons_example>
+
+ <scons_output example="FindFile1a" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <scons_example name="FindFile1b">
+ <file name="SConstruct" printme="1">
+ # 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)
+ </file>
+ <file name="config.h">
+ exists
+ </file>
+ <directory name="src"></directory>
+ <directory name="src/include"></directory>
+ </file>
+ <file name="src/include/private.h">
+ exists
+ <directory name="include"></directory>
+ </file>
+ <file name="include/dist.h">
+ exists
+ </scons_example>
+
+ <scons_output example="FindFile1b" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <!-- The man page says this should work, but it fails.
+ <para>
+
+ If the 'file' parameter is a list of files,
+ a list of File nodes is returned.
+
+ </para>
+
+ <scons_example name="FindFile1c">
+ <file name="SConstruct" printme="1">
+ # several directories
+ includes = [ '.', 'include', 'src/include']
+ headers = [ 'nonesuch.h', 'config.h', 'private.h', 'dist.h']
+ print FindFile(headers, includes)
+ </file>
+ <file name="config.h">
+ exists
+ </file>
+ <directory name="src"></directory>
+ <directory name="src/include"></directory>
+ </file>
+ <file name="src/include/private.h">
+ exists
+ <directory name="include"></directory>
+ </file>
+ <file name="include/dist.h">
+ exists
+ </scons_example>
+
+ <scons_output example="FindFile1c" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+
+ If the file exists in more than one directory,
+ only the first occurrence is returned.
+
+ </para>
+
+ <scons_example name="FindFile1d">
+ <file name="SConstruct" printme="1">
+ print FindFile('multiple', ['sub1', 'sub2', 'sub3'])
+ print FindFile('multiple', ['sub2', 'sub3', 'sub1'])
+ print FindFile('multiple', ['sub3', 'sub1', 'sub2'])
+ </file>
+ <directory name="sub1"></directory>
+ <file name="sub1/multiple">
+ exists
+ </file>
+ <directory name="sub2"></directory>
+ <file name="sub2/multiple">
+ exists
+ </file>
+ <directory name="sub3"></directory>
+ <file name="sub3/multiple">
+ exists
+ </file>
+ </scons_example>
+
+ <scons_output example="FindFile1d" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <!-- file may be a list of file names or a single file name. -->
+
+ <para>
+
+ 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!)
+
+ </para>
+
+ <scons_example name="FindFile2">
+ <file name="SConstruct" printme="1">
+ # Neither file exists, so build will fail
+ Command('derived', 'leaf', 'cat >$TARGET $SOURCE')
+ print FindFile('leaf', '.')
+ print FindFile('derived', '.')
+ </file>
+ </scons_example>
+
+ <scons_output example="FindFile2" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <scons_example name="FindFile2">
+ <file name="SConstruct" printme="1">
+ # Only 'leaf' exists
+ Command('derived', 'leaf', 'cat >$TARGET $SOURCE')
+ print FindFile('leaf', '.')
+ print FindFile('derived', '.')
+ </file>
+ <file name="leaf">
+ leaf
+ </file>
+ </scons_example>
+
+ <scons_output example="FindFile2" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If a source file exists, &FindFile; will correctly return the name
+ in the build directory.
+
+ </para>
+
+ <scons_example name="FindFile3">
+ <file name="SConstruct" printme="1">
+ # Only 'src/leaf' exists
+ VariantDir('build', 'src')
+ print FindFile('leaf', 'build')
+ </file>
+ <directory name="src"></directory>
+ <file name="src/leaf">
+ leaf
+ </file>
+ </scons_example>
+
+ <scons_output example="FindFile3" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Handling Nested Lists: the &Flatten; Function</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <scons_example name="Flatten1">
+ <file name="SConstruct" printme="1">
+ objects = [
+ Object('prog1.c'),
+ Object('prog2.c', CCFLAGS='-DFOO'),
+ ]
+ Program(objects)
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Because the Builder calls in &SCons;
+ flatten their input lists,
+ this works just fine to build the program:
+
+ </para>
+
+ <scons_output example="Flatten1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But if you were debugging your build
+ and wanted to print the absolute path
+ of each object file in the
+ <varname>objects</varname> list,
+ you might try the following simple approach,
+ trying to print each Node's
+ <literal>abspath</literal>
+ attribute:
+
+ </para>
+
+ <scons_example name="Flatten2">
+ <file name="SConstruct" printme="1">
+ objects = [
+ Object('prog1.c'),
+ Object('prog2.c', CCFLAGS='-DFOO'),
+ ]
+ Program(objects)
+
+ for object_file in objects:
+ print object_file.abspath
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ This does not work as expected
+ because each call to <function>str</function>
+ is operating an embedded list returned by
+ each &Object; call,
+ not on the underlying Nodes within those lists:
+
+ </para>
+
+ <scons_output example="Flatten2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The solution is to use the &Flatten; function
+ so that you can pass each Node to
+ the <function>str</function> separately:
+
+ </para>
+
+ <scons_example name="Flatten3">
+ <file name="SConstruct" printme="1">
+ objects = [
+ Object('prog1.c'),
+ Object('prog2.c', CCFLAGS='-DFOO'),
+ ]
+ Program(objects)
+
+ for object_file in Flatten(objects):
+ print object_file.abspath
+ </file>
+ <file name="prog1.c">
+ prog1.c
+ </file>
+ <file name="prog2.c">
+ prog2.c
+ </file>
+ </scons_example>
+
+ <!--
+
+ TODO: can't use this now because it displays the temporary path name
+
+ <scons_output example="Flatten3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ /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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Finding the Invocation Directory: the &GetLaunchDir; Function</title>
+
+ <para>
+
+ If you need to find the directory from
+ which the user invoked the &scons; command,
+ you can use the &GetLaunchDir; function:
+
+ </para>
+
+ <sconstruct>
+ env = Environment(
+ LAUNCHDIR = GetLaunchDir(),
+ )
+ env.Command('directory_build_info',
+ '$LAUNCHDIR/build_info'
+ Copy('$TARGET', '$SOURCE'))
+ </sconstruct>
+
+ <para>
+
+ Because &SCons; is usually invoked from the top-level
+ directory in which the &SConstruct; file lives,
+ the Python <function>os.getcwd()</function>
+ is often equivalent.
+ However, the &SCons;
+ <literal>-u</literal>,
+ <literal>-U</literal>
+ and
+ <literal>-D</literal>
+ 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.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &SCons; supports a lot of additional functionality
+ that doesn't readily fit into the other chapters.
+
+ </para>
+
+ <section>
+ <title>Verifying the Python Version: the &EnsurePythonVersion; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ &SCons; provides an &EnsurePythonVersion; function for this.
+ You simply pass it the major and minor versions
+ numbers of the version of Python you require:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing Python version by faking out
+ the infrastructure in some way.
+
+ <scons_example name="EnsurePythonVersion">
+ <file name="SConstruct" printme="1">
+ EnsurePythonVersion(2, 5)
+ </file>
+ </scons_example>
+
+ -->
+
+ <programlisting>
+ EnsurePythonVersion(2, 5)
+ </programlisting>
+
+ <para>
+
+ And then &SCons; will exit with the following error
+ message when a user runs it with an unsupported
+ earlier version of Python:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing Python version by faking out
+ the infrastructure in some way.
+
+ <scons_output example="EnsurePythonVersion">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Python 2.5 or greater required, but you have Python 2.3.6
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Verifying the SCons Version: the &EnsureSConsVersion; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing SCons version by faking out
+ the infrastructure in some way.
+
+ <scons_example name="EnsureSConsVersion">
+ <file name="SConstruct" printme="1">
+ EnsureSConsVersion(1, 0)
+ </file>
+ </scons_example>
+
+ -->
+
+ <programlisting>
+ EnsureSConsVersion(1, 0)
+ </programlisting>
+
+ <para>
+
+ And then &SCons; will exit with the following error
+ message when a user runs it with an unsupported
+ earlier version of &SCons;:
+
+ </para>
+
+ <!--
+
+ TODO: Figure out how to generate the error message
+ regardless of executing SCons version by faking out
+ the infrastructure in some way.
+
+ <scons_output example="EnsureSConsVersion">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ SCons 1.0 or greater required, but you have SCons 0.98.5
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ if ARGUMENTS.get('FUTURE'):
+ print "The FUTURE option is not supported yet!"
+ Exit(2)
+ env = Environment()
+ env.Program('hello.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q FUTURE=1</userinput>
+ The FUTURE option is not supported yet!
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ 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 <literal>0</literal>,
+ which indicates successful execution.
+
+ </para>
+
+ <para>
+
+ Note that the &Exit; function
+ is equivalent to calling the Python
+ <function>sys.exit</function> function
+ (which the it actually calls),
+ but because &Exit; is a &SCons; function,
+ you don't have to import the Python
+ <literal>sys</literal> module to use it.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Searching for Files: the &FindFile; Function</title>
+
+ <para>
+
+ 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.)
+
+ </para>
+
+ <programlisting>
+ # one directory
+ print FindFile('missing', '.')
+ t = FindFile('exists', '.')
+ print t.__class__, t
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ None
+ SCons.Node.FS.File exists
+ scons: `.' is up to date.
+ </screen>
+
+ <programlisting>
+ # 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)
+</programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ nonesuch.h: None
+ config.h: config.h
+ private.h: src/include/private.h
+ dist.h: include/dist.h
+ scons: `.' is up to date.
+ </screen>
+
+ <!-- The man page says this should work, but it fails.
+ <para>
+
+ If the 'file' parameter is a list of files,
+ a list of File nodes is returned.
+
+ </para>
+
+ <scons_example name="FindFile1c">
+ <file name="SConstruct" printme="1">
+ # several directories
+ includes = [ '.', 'include', 'src/include']
+ headers = [ 'nonesuch.h', 'config.h', 'private.h', 'dist.h']
+ print FindFile(headers, includes)
+ </file>
+ <file name="config.h">
+ exists
+ </file>
+ <directory name="src"></directory>
+ <directory name="src/include"></directory>
+ </file>
+ <file name="src/include/private.h">
+ exists
+ <directory name="include"></directory>
+ </file>
+ <file name="include/dist.h">
+ exists
+ </scons_example>
+
+ <scons_output example="FindFile1c" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+
+ If the file exists in more than one directory,
+ only the first occurrence is returned.
+
+ </para>
+
+ <programlisting>
+ print FindFile('multiple', ['sub1', 'sub2', 'sub3'])
+ print FindFile('multiple', ['sub2', 'sub3', 'sub1'])
+ print FindFile('multiple', ['sub3', 'sub1', 'sub2'])
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ sub1/multiple
+ sub2/multiple
+ sub3/multiple
+ scons: `.' is up to date.
+ </screen>
+
+ <!-- file may be a list of file names or a single file name. -->
+
+ <para>
+
+ 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!)
+
+ </para>
+
+ <programlisting>
+ # Neither file exists, so build will fail
+ Command('derived', 'leaf', 'cat &gt;$TARGET $SOURCE')
+ print FindFile('leaf', '.')
+ print FindFile('derived', '.')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ None
+ derived
+ scons: *** [derived] Source `leaf' not found, needed by target `derived'.
+ </screen>
+
+ <programlisting>
+ # Neither file exists, so build will fail
+ Command('derived', 'leaf', 'cat &gt;$TARGET $SOURCE')
+ print FindFile('leaf', '.')
+ print FindFile('derived', '.')
+
+ # Only 'leaf' exists
+ Command('derived', 'leaf', 'cat &gt;$TARGET $SOURCE')
+ print FindFile('leaf', '.')
+ print FindFile('derived', '.')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ leaf
+ derived
+ cat &gt; derived leaf
+ </screen>
+
+ <para>
+
+ If a source file exists, &FindFile; will correctly return the name
+ in the build directory.
+
+ </para>
+
+ <programlisting>
+ # Only 'src/leaf' exists
+ VariantDir('build', 'src')
+ print FindFile('leaf', 'build')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ build/leaf
+ scons: `.' is up to date.
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Handling Nested Lists: the &Flatten; Function</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <programlisting>
+ objects = [
+ Object('prog1.c'),
+ Object('prog2.c', CCFLAGS='-DFOO'),
+ ]
+ Program(objects)
+ </programlisting>
+
+ <para>
+
+ Because the Builder calls in &SCons;
+ flatten their input lists,
+ this works just fine to build the program:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o prog1.o -c prog1.c
+ cc -o prog2.o -c -DFOO prog2.c
+ cc -o prog1 prog1.o prog2.o
+ </screen>
+
+ <para>
+
+ But if you were debugging your build
+ and wanted to print the absolute path
+ of each object file in the
+ <varname>objects</varname> list,
+ you might try the following simple approach,
+ trying to print each Node's
+ <literal>abspath</literal>
+ attribute:
+
+ </para>
+
+ <programlisting>
+ objects = [
+ Object('prog1.c'),
+ Object('prog2.c', CCFLAGS='-DFOO'),
+ ]
+ Program(objects)
+
+ for object_file in objects:
+ print object_file.abspath
+ </programlisting>
+
+ <para>
+
+ This does not work as expected
+ because each call to <function>str</function>
+ is operating an embedded list returned by
+ each &Object; call,
+ not on the underlying Nodes within those lists:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ AttributeError: NodeList instance has no attribute 'abspath':
+ File "/home/my/project/SConstruct", line 8:
+ print object_file.abspath
+ </screen>
+
+ <para>
+
+ The solution is to use the &Flatten; function
+ so that you can pass each Node to
+ the <function>str</function> separately:
+
+ </para>
+
+ <programlisting>
+ objects = [
+ Object('prog1.c'),
+ Object('prog2.c', CCFLAGS='-DFOO'),
+ ]
+ Program(objects)
+
+ for object_file in Flatten(objects):
+ print object_file.abspath
+ </programlisting>
+
+ <!--
+
+ TODO: can't use this now because it displays the temporary path name
+
+ <scons_output example="Flatten3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ /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
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Finding the Invocation Directory: the &GetLaunchDir; Function</title>
+
+ <para>
+
+ If you need to find the directory from
+ which the user invoked the &scons; command,
+ you can use the &GetLaunchDir; function:
+
+ </para>
+
+ <programlisting>
+ env = Environment(
+ LAUNCHDIR = GetLaunchDir(),
+ )
+ env.Command('directory_build_info',
+ '$LAUNCHDIR/build_info'
+ Copy('$TARGET', '$SOURCE'))
+ </programlisting>
+
+ <para>
+
+ Because &SCons; is usually invoked from the top-level
+ directory in which the &SConstruct; file lives,
+ the Python <function>os.getcwd()</function>
+ is often equivalent.
+ However, the &SCons;
+ <literal>-u</literal>,
+ <literal>-U</literal>
+ and
+ <literal>-D</literal>
+ 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.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ Internally, &SCons; represents all of the files
+ and directories it knows about as &Nodes;.
+ These internal objects
+ (not object <emphasis>files</emphasis>)
+ can be used in a variety of ways
+ to make your &SConscript;
+ files portable and easy to read.
+
+ </para>
+
+ <section>
+ <title>Builder Methods Return Lists of Target Nodes</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ Object('hello.c', CCFLAGS='-DHELLO')
+ Object('goodbye.c', CCFLAGS='-DGOODBYE')
+ </sconstruct>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ Object('hello.c', CCFLAGS='-DHELLO')
+ Object('goodbye.c', CCFLAGS='-DGOODBYE')
+ Program(['hello.o', 'goodbye.o'])
+ </sconstruct>
+
+ <para>
+
+ 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;.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ hello_list = Object('hello.c', CCFLAGS='-DHELLO')
+ goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE')
+ Program(hello_list + goodbye_list)
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="goodbye.c">
+ int main() { printf("Goodbye, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This makes our &SConstruct; file portable again,
+ the build output on Linux looking like:
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And on Windows:
+
+ </para>
+
+ <scons_output example="ex1" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ We'll see examples of using the list of nodes
+ returned by builder methods throughout
+ the rest of this guide.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Explicitly Creating File and Directory Nodes</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="print">
+ <file name="SConstruct" printme="1">
+ hello_c = File('hello.c')
+ Program(hello_c)
+
+ classes = Dir('classes')
+ Java(classes, 'src')
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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.
+ <!--
+ (For an example of when you might
+ need to use &File; or &Dir; to
+ prevent ambiguous interpretation of a string
+ naming a file or directory, see
+ <xref linkend="chap-hierarchy">.)
+ -->
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <sconstruct>
+ xyzzy = Entry('xyzzy')
+ </sconstruct>
+
+ <para>
+
+ The returned <literal>xyzzy</literal> 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Printing &Node; File Names</title>
+
+ <para>
+
+ 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 <emphasis>list</emphasis> of Nodes,
+ you must use Python subscripts
+ to fetch individual Nodes from the list.
+ For example, the following &SConstruct; file:
+
+ </para>
+
+ <scons_example name="print">
+ <file name="SConstruct" printme="1">
+ 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]
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Would print the following file names on a POSIX system:
+
+ </para>
+
+ <scons_output example="print" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And the following file names on a Windows system:
+
+ </para>
+
+ <scons_output example="print" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that in the above example,
+ the <literal>object_list[0]</literal>
+ extracts an actual Node <emphasis>object</emphasis>
+ from the list,
+ and the Python <literal>print</literal> statement
+ converts the object to a string for printing.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Using a &Node;'s File Name as a String</title>
+
+ <para>
+
+ 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
+ <function>os.path.exists</function>
+ to figure out whether a file
+ exists while the &SConstruct; file
+ is being read and executed,
+ you can fetch the string as follows:
+
+ </para>
+
+ <scons_example name="exists">
+ <file name="SConstruct" printme="1">
+ 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!"
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which executes as follows on a POSIX system:
+
+ </para>
+
+ <scons_output example="exists" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Fetching the Contents of a &Node;</title>
+
+ <para>
+
+ XXX Describe using read() and readlines()
+ when we add that as a public interface.
+
+ </para>
+
+ <scons_example name="read">
+ <file name="SConstruct" printme="1">
+ hello_c = File('hello.c')
+ contents = hello_c.read()
+ print "contents are:"
+ print contents
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which executes as follows on a POSIX system:
+
+ </para>
+
+ <scons_output example="read" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+ <title>Python Value &Node;</title>
+
+ <para>
+
+ XXX Value()
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ Internally, &SCons; represents all of the files
+ and directories it knows about as &Nodes;.
+ These internal objects
+ (not object <emphasis>files</emphasis>)
+ can be used in a variety of ways
+ to make your &SConscript;
+ files portable and easy to read.
+
+ </para>
+
+ <section>
+ <title>Builder Methods Return Lists of Target Nodes</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Object('hello.c', CCFLAGS='-DHELLO')
+ Object('goodbye.c', CCFLAGS='-DGOODBYE')
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Object('hello.c', CCFLAGS='-DHELLO')
+ Object('goodbye.c', CCFLAGS='-DGOODBYE')
+ Program(['hello.o', 'goodbye.o'])
+ </programlisting>
+
+ <para>
+
+ 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;.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ hello_list = Object('hello.c', CCFLAGS='-DHELLO')
+ goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE')
+ Program(hello_list + goodbye_list)
+ </programlisting>
+
+ <para>
+
+ This makes our &SConstruct; file portable again,
+ the build output on Linux looking like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o goodbye.o -c -DGOODBYE goodbye.c
+ cc -o hello.o -c -DHELLO hello.c
+ cc -o hello hello.o goodbye.o
+ </screen>
+
+ <para>
+
+ And on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+
+ We'll see examples of using the list of nodes
+ returned by builder methods throughout
+ the rest of this guide.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Explicitly Creating File and Directory Nodes</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ hello_c = File('hello.c')
+ Program(hello_c)
+
+ classes = Dir('classes')
+ Java(classes, 'src')
+ </programlisting>
+
+ <para>
+
+ 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.
+ <!--
+ (For an example of when you might
+ need to use &File; or &Dir; to
+ prevent ambiguous interpretation of a string
+ naming a file or directory, see
+ <xref linkend="chap-hierarchy">.)
+ -->
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <programlisting>
+ xyzzy = Entry('xyzzy')
+ </programlisting>
+
+ <para>
+
+ The returned <literal>xyzzy</literal> 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Printing &Node; File Names</title>
+
+ <para>
+
+ 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 <emphasis>list</emphasis> of Nodes,
+ you must use Python subscripts
+ to fetch individual Nodes from the list.
+ For example, the following &SConstruct; file:
+
+ </para>
+
+ <programlisting>
+ 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]
+ </programlisting>
+
+ <para>
+
+ Would print the following file names on a POSIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ The object file is: hello.o
+ The program file is: hello
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ And the following file names on a Windows system:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+
+ Note that in the above example,
+ the <literal>object_list[0]</literal>
+ extracts an actual Node <emphasis>object</emphasis>
+ from the list,
+ and the Python <literal>print</literal> statement
+ converts the object to a string for printing.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Using a &Node;'s File Name as a String</title>
+
+ <para>
+
+ 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
+ <function>os.path.exists</function>
+ to figure out whether a file
+ exists while the &SConstruct; file
+ is being read and executed,
+ you can fetch the string as follows:
+
+ </para>
+
+ <programlisting>
+ 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!"
+ </programlisting>
+
+ <para>
+
+ Which executes as follows on a POSIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ hello does not exist!
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Fetching the Contents of a &Node;</title>
+
+ <para>
+
+ XXX Describe using read() and readlines()
+ when we add that as a public interface.
+
+ </para>
+
+ <scons_example name="read">
+ <file name="SConstruct" printme="1">
+ hello_c = File('hello.c')
+ contents = hello_c.read()
+ print "contents are:"
+ print contents
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which executes as follows on a POSIX system:
+
+ </para>
+
+ <scons_output example="read" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+ <title>Python Value &Node;</title>
+
+ <para>
+
+ XXX Value()
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Providing Build Help: the &Help; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ Help("""
+ Type: 'scons program' to build the production program,
+ 'scons debug' to build the debug version.
+ """)
+ </file>
+ </scons_example>
+
+ <para>
+
+ (Note the above use of the Python triple-quote syntax,
+ which comes in very handy for
+ specifying multi-line strings like help text.)
+
+ </para>
+
+ <para>
+
+ 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; <literal>-h</literal> option:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -h</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct" printme="1">
+ 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")
+ </file>
+ </scons_example>
+
+ <para>
+
+ Will display the complete help text on Windows:
+
+ </para>
+
+ <scons_output example="ex2" os="win32">
+ <scons_output_command>scons -h</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But only show the relevant option on a Linux or UNIX system:
+
+ </para>
+
+ <scons_output example="ex2" os="posix">
+ <scons_output_command>scons -h</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <literal>-H</literal> option is used.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables</title>
+
+ <para>
+
+ 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 <envar>$*COM</envar> variables
+ that specify the command lines
+ used to build various types of target files
+ have a corresponding <envar>$*COMSTR</envar> variable
+ that can be set to an alternative
+ string that will be displayed
+ when the target is built.
+
+ </para>
+
+ <para>
+
+ For example, suppose you want to
+ have &SCons; display a
+ <literal>"Compiling"</literal>
+ message whenever it's compiling an object file,
+ and a
+ <literal>"Linking"</literal>
+ when it's linking an executable.
+ You could write a &SConstruct; file
+ that looks like:
+
+ </para>
+
+ <scons_example name="COMSTR">
+ <file name="SConstruct" printme="1">
+ env = Environment(CCCOMSTR = "Compiling $TARGET",
+ LINKCOMSTR = "Linking $TARGET")
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which would then yield the output:
+
+ </para>
+
+ <!--
+
+ <scons_output example="COMSTR" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Compiling foo.o
+ Linking foo
+ </screen>
+
+ <para>
+
+ &SCons; performs complete variable substitution
+ on <envar>$*COMSTR</envar> 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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
+ <varname>VERBOSE</varname>
+ command-line variable to your &SConstruct; file.
+ A simple configuration for this might look like:
+
+ </para>
+
+ <scons_example name="COMSTR-VERBOSE">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ if ARGUMENTS.get('VERBOSE') != "1':
+ env['CCCOMSTR'] = "Compiling $TARGET"
+ env['LINKCOMSTR'] = "Linking $TARGET"
+ env.Program('foo.c')
+ </file>
+ <file name="foo.c">
+ foo.c
+ </file>
+ </scons_example>
+
+ <para>
+
+
+ By only setting the appropriate
+ <envar>$*COMSTR</envar> variables
+ if the user specifies
+ <literal>VERBOSE=1</literal>
+ on the command line,
+ the user has control
+ over how &SCons;
+ displays these particular command lines:
+
+ </para>
+
+ <!--
+
+ <scons_output example="COMSTR-VERBOSE" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q VERBOSE=1</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Compiling foo.o
+ Linking foo
+ % <userinput>scons -Q -c</userinput>
+ Removed foo.o
+ Removed foo
+ % <userinput>scons -Q VERBOSE=1</userinput>
+ cc -o foo.o -c foo.c
+ cc -o foo foo.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Providing Build Progress Output: the &Progress; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <scons_example name="Progress-TARGET">
+ <file name="SConstruct" printme="1">
+ Progress('Evaluating $TARGET\n')
+ Program('f1.c')
+ Program('f2.c')
+ </file>
+ <file name="f1.c">
+ f1.c
+ </file>
+ <file name="f2.c">
+ f2.c
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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
+ <literal>print</literal> statement),
+ and we must specify the
+ <literal>\n</literal>
+ that we want printed at the end of the configured string.
+ This configuration, then,
+ will have &SCons;
+ print that it is <literal>Evaluating</literal>
+ each file that it encounters
+ in turn as it traverses the dependency graph:
+
+ </para>
+
+ <scons_output example="Progress-TARGET" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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
+ (<literal>\r</literal>)
+ so that each file name gets re-printed on the same line.
+ Such a configuration would look like:
+
+ </para>
+
+ <sconstruct>
+ Progress('$TARGET\r',
+ file=open('/dev/tty', 'w'),
+ overwrite=True)
+ Program('f1.c')
+ Program('f2.c')
+ </sconstruct>
+
+ <para>
+
+ Note that we also specified the
+ <literal>overwrite=True</literal> 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
+ <literal>overwrite=True</literal> 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
+ <filename>/dev/tty</filename> file
+ for direct access (on POSIX) to
+ the user's screen.
+ On Windows, the equivalent would be to open
+ the <filename>con:</filename> file name.
+
+ </para>
+
+ <para>
+
+ Also, it's important to know that although you can use
+ <literal>$TARGET</literal> to substitute the name of
+ the node in the string,
+ the &Progress; function does <emphasis>not</emphasis>
+ perform general variable substitution
+ (because there's not necessarily a construction
+ environment involved in evaluating a node
+ like a source file, for example).
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5)
+ Program('f1.c')
+ Program('f2.c')
+ </sconstruct>
+
+ <para>
+
+ Note that here we have also used the
+ <literal>interval=</literal>
+ keyword argument to have &SCons;
+ only print a new "spinner" string
+ once every five evaluated nodes.
+ Using an <literal>interval=</literal> count,
+ even with strings that use <literal>$TARGET</literal> 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Progress-callable">
+ <file name="SConstruct" printme="1">
+ screen = open('/dev/tty', 'w')
+ count = 0
+ def progress_function(node)
+ count += 1
+ screen.write('Node %4d: %s\r' % (count, node))
+
+ Progress(progress_function)
+ </file>
+ </scons_example>
+
+ <para>
+
+ Of course, if you choose,
+ you could completely ignore the
+ <varname>node</varname> argument to the function,
+ and just print a count,
+ or anything else you wish.
+
+ </para>
+
+ <para>
+
+ (Note that there's an obvious follow-on question here:
+ how would you find the total number of nodes
+ that <emphasis>will be</emphasis>
+ 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.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Printing Detailed Build Status: the &GetBuildFailures; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ SCons provides a &GetBuildFailures; method that
+ you can use in a python <function>atexit</function> 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 <literal>-j</literal>. Here's a
+ simple example:
+
+ </para>
+
+ <scons_example name="gbf1">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ </scons_example>
+
+ <para>
+
+ The <function>atexit.register</function> call
+ registers <function>print_build_failures</function>
+ as an <function>atexit</function> 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
+ <literal>.node</literal>,
+ <literal>.errstr</literal>,
+ <literal>.filename</literal>, and
+ <literal>.command</literal>.
+ The <literal>filename</literal> is not necessarily
+ the same file as the <literal>node</literal>; the
+ <literal>node</literal> is the target that was
+ being built when the error occurred, while the
+ <literal>filename</literal>is 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.
+
+ </para>
+
+ <para>
+
+ Here is a more complete example showing how to
+ turn each element of &GetBuildFailures; into a string:
+
+ </para>
+
+ <scons_example name="gbf2">
+ <file name="SConstruct" printme="1">
+ # 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)
+ </file>
+ </scons_example>
+
+ <para>
+
+ When this runs, you'll see the appropriate output:
+
+ </para>
+
+ <scons_output example="gbf2">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q fail=1</scons_output_command>
+ </scons_output>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Providing Build Help: the &Help; Function</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Help("""
+ Type: 'scons program' to build the production program,
+ 'scons debug' to build the debug version.
+ """)
+ </programlisting>
+
+ <para>
+
+ (Note the above use of the Python triple-quote syntax,
+ which comes in very handy for
+ specifying multi-line strings like help text.)
+
+ </para>
+
+ <para>
+
+ 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; <literal>-h</literal> option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -h</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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")
+ </programlisting>
+
+ <para>
+
+ Will display the complete help text on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -h</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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.
+ </screen>
+
+ <para>
+
+ But only show the relevant option on a Linux or UNIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -h</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ 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 <literal>-H</literal> option is used.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables</title>
+
+ <para>
+
+ 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 <envar>$*COM</envar> variables
+ that specify the command lines
+ used to build various types of target files
+ have a corresponding <envar>$*COMSTR</envar> variable
+ that can be set to an alternative
+ string that will be displayed
+ when the target is built.
+
+ </para>
+
+ <para>
+
+ For example, suppose you want to
+ have &SCons; display a
+ <literal>"Compiling"</literal>
+ message whenever it's compiling an object file,
+ and a
+ <literal>"Linking"</literal>
+ when it's linking an executable.
+ You could write a &SConstruct; file
+ that looks like:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCCOMSTR = "Compiling $TARGET",
+ LINKCOMSTR = "Linking $TARGET")
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ Which would then yield the output:
+
+ </para>
+
+ <!--
+
+ <scons_output example="COMSTR" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Compiling foo.o
+ Linking foo
+ </screen>
+
+ <para>
+
+ &SCons; performs complete variable substitution
+ on <envar>$*COMSTR</envar> 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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
+ <varname>VERBOSE</varname>
+ command-line variable to your &SConstruct; file.
+ A simple configuration for this might look like:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ if ARGUMENTS.get('VERBOSE') != "1':
+ env['CCCOMSTR'] = "Compiling $TARGET"
+ env['LINKCOMSTR'] = "Linking $TARGET"
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+
+ By only setting the appropriate
+ <envar>$*COMSTR</envar> variables
+ if the user specifies
+ <literal>VERBOSE=1</literal>
+ on the command line,
+ the user has control
+ over how &SCons;
+ displays these particular command lines:
+
+ </para>
+
+ <!--
+
+ <scons_output example="COMSTR-VERBOSE" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q VERBOSE=1</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Compiling foo.o
+ Linking foo
+ % <userinput>scons -Q -c</userinput>
+ Removed foo.o
+ Removed foo
+ % <userinput>scons -Q VERBOSE=1</userinput>
+ cc -o foo.o -c foo.c
+ cc -o foo foo.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Providing Build Progress Output: the &Progress; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <programlisting>
+ Progress('Evaluating $TARGET\n')
+ Program('f1.c')
+ Program('f2.c')
+ </programlisting>
+
+ <para>
+
+ 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
+ <literal>print</literal> statement),
+ and we must specify the
+ <literal>\n</literal>
+ that we want printed at the end of the configured string.
+ This configuration, then,
+ will have &SCons;
+ print that it is <literal>Evaluating</literal>
+ each file that it encounters
+ in turn as it traverses the dependency graph:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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 .
+ </screen>
+
+ <para>
+
+ 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
+ (<literal>\r</literal>)
+ so that each file name gets re-printed on the same line.
+ Such a configuration would look like:
+
+ </para>
+
+ <programlisting>
+ Progress('$TARGET\r',
+ file=open('/dev/tty', 'w'),
+ overwrite=True)
+ Program('f1.c')
+ Program('f2.c')
+ </programlisting>
+
+ <para>
+
+ Note that we also specified the
+ <literal>overwrite=True</literal> 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
+ <literal>overwrite=True</literal> 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
+ <filename>/dev/tty</filename> file
+ for direct access (on POSIX) to
+ the user's screen.
+ On Windows, the equivalent would be to open
+ the <filename>con:</filename> file name.
+
+ </para>
+
+ <para>
+
+ Also, it's important to know that although you can use
+ <literal>$TARGET</literal> to substitute the name of
+ the node in the string,
+ the &Progress; function does <emphasis>not</emphasis>
+ perform general variable substitution
+ (because there's not necessarily a construction
+ environment involved in evaluating a node
+ like a source file, for example).
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5)
+ Program('f1.c')
+ Program('f2.c')
+ </programlisting>
+
+ <para>
+
+ Note that here we have also used the
+ <literal>interval=</literal>
+ keyword argument to have &SCons;
+ only print a new "spinner" string
+ once every five evaluated nodes.
+ Using an <literal>interval=</literal> count,
+ even with strings that use <literal>$TARGET</literal> 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ screen = open('/dev/tty', 'w')
+ count = 0
+ def progress_function(node)
+ count += 1
+ screen.write('Node %4d: %s\r' % (count, node))
+
+ Progress(progress_function)
+ </programlisting>
+
+ <para>
+
+ Of course, if you choose,
+ you could completely ignore the
+ <varname>node</varname> argument to the function,
+ and just print a count,
+ or anything else you wish.
+
+ </para>
+
+ <para>
+
+ (Note that there's an obvious follow-on question here:
+ how would you find the total number of nodes
+ that <emphasis>will be</emphasis>
+ 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.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Printing Detailed Build Status: the &GetBuildFailures; Function</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ SCons provides a &GetBuildFailures; method that
+ you can use in a python <function>atexit</function> 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 <literal>-j</literal>. Here's a
+ simple example:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ The <function>atexit.register</function> call
+ registers <function>print_build_failures</function>
+ as an <function>atexit</function> 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
+ <literal>.node</literal>,
+ <literal>.errstr</literal>,
+ <literal>.filename</literal>, and
+ <literal>.command</literal>.
+ The <literal>filename</literal> is not necessarily
+ the same file as the <literal>node</literal>; the
+ <literal>node</literal> is the target that was
+ being built when the error occurred, while the
+ <literal>filename</literal>is 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.
+
+ </para>
+
+ <para>
+
+ Here is a more complete example showing how to
+ turn each element of &GetBuildFailures; into a string:
+
+ </para>
+
+ <programlisting>
+ # 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)
+ </programlisting>
+
+ <para>
+
+ When this runs, you'll see the appropriate output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ Build succeeded.
+ % <userinput>scons -Q fail=1</userinput>
+ scons: *** [target] Source `source' not found, needed by target `target'.
+ FAILED!!!!
+ Failed building target: Source `source' not found, needed by target `target'.
+ </screen>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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 <filename>config</filename>
+ 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 <filename>lib</filename>
+ would be found by calling a utility named <filename>lib-config</filename>.
+
+ </para>
+
+ <para>
+
+ A more recent convention is that these options
+ are available from the generic <filename>pkg-config</filename> 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.
+
+ </para>
+
+ <para>
+
+ &SCons; construction environments have a &ParseConfig; method
+ that executes a <filename>*config</filename> utility
+ (either <filename>pkg-config</filename> 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.
+
+ </para>
+
+ <scons_example name="ParseConfig1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env['CPPPATH'] = ['/lib/compat']
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ print env['CPPPATH']
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; will execute the specified command string,
+ parse the resultant flags,
+ and add the flags to the appropriate environment variables.
+
+ </para>
+
+ <scons_output example="ParseConfig1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ In the example above, &SCons; has added the include directory to
+ <varname>CPPPATH</varname>.
+ (Depending upon what other flags are emitted by the
+ <filename>pkg-config</filename> command,
+ other variables may have been extended as well.)
+
+ </para>
+
+ <para>
+
+ Note that the options are merged with existing options using
+ the &MergeFlags; method,
+ so that each option only occurs once in the construction variable:
+
+ </para>
+
+ <scons_example name="ParseConfig2">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ print env['CPPPATH']
+ </file>
+ </scons_example>
+
+ <scons_output example="ParseConfig2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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 <filename>config</filename>
+ 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 <filename>lib</filename>
+ would be found by calling a utility named <filename>lib-config</filename>.
+
+ </para>
+
+ <para>
+
+ A more recent convention is that these options
+ are available from the generic <filename>pkg-config</filename> 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.
+
+ </para>
+
+ <para>
+
+ &SCons; construction environments have a &ParseConfig; method
+ that executes a <filename>*config</filename> utility
+ (either <filename>pkg-config</filename> 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.
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env['CPPPATH'] = ['/lib/compat']
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ print env['CPPPATH']
+ </programlisting>
+
+ <para>
+
+ &SCons; will execute the specified command string,
+ parse the resultant flags,
+ and add the flags to the appropriate environment variables.
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ In the example above, &SCons; has added the include directory to
+ <varname>CPPPATH</varname>.
+ (Depending upon what other flags are emitted by the
+ <filename>pkg-config</filename> command,
+ other variables may have been extended as well.)
+
+ </para>
+
+ <para>
+
+ Note that the options are merged with existing options using
+ the &MergeFlags; method,
+ so that each option only occurs once in the construction variable:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ print env['CPPPATH']
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ &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.)
+
+ </para>
+
+ <scons_example name="ParseFlags1">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="f1.c">
+ int main() { return 0; }
+ </file>
+ </scons_example>
+
+ <scons_output example="ParseFlags1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that if the options are limited to generic types
+ like those above,
+ they will be correctly translated for other platform types:
+
+ </para>
+
+ <scons_output example="ParseFlags1" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ParseFlags2">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="f1.c">
+ int main() { return 0; }
+ </file>
+ </scons_example>
+
+ <scons_output example="ParseFlags2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ &ParseFlags; will also accept a (recursive) list of strings as input;
+ the list is flattened before the strings are processed:
+
+ </para>
+
+ <scons_example name="ParseFlags3">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="f1.c">
+ int main() { return 0; }
+ </file>
+ </scons_example>
+
+ <scons_output example="ParseFlags3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ParseFlags4">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <file name="f1.c">
+ int main() { return 0; }
+ </file>
+ </scons_example>
+
+ <scons_output example="ParseFlags4">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ &ParseFlags; is regularly updated for new options;
+ consult the man page for details about those currently recognized.
+
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <para>
+
+ &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.)
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Note that if the options are limited to generic types
+ like those above,
+ they will be correctly translated for other platform types:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ CCFLAGS -whatever
+ cc -o f1.o -c -whatever f1.c
+ cc -o f1 f1.o
+ </screen>
+
+ <para>
+
+ &ParseFlags; will also accept a (recursive) list of strings as input;
+ the list is flattened before the strings are processed:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ &ParseFlags; is regularly updated for new options;
+ consult the man page for details about those currently recognized.
+
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ The most distinctive thing about &SCons;
+ is that its configuration files are
+ actually <emphasis>scripts</emphasis>,
+ 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.
+
+ </para>
+
+ <para>
+
+ Paradoxically,
+ using Python as the configuration file format
+ makes &SCons;
+ <emphasis>easier</emphasis>
+ 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.
+
+ </para>
+
+ <!--
+
+ <section>
+ <title>Why &SCons;?</title>
+
+ <para>
+
+ &SCons; is a response to a perennial problem:
+ building software is harder than it should be.
+ In a nutshell: the old, reliable model of the
+ venerable and ubiquitous &Make; program
+ has had a hard time keeping up with
+ how complicated building software has become.
+ The fact that &Make; has kept up as well as it has is impressive,
+ and a testament to how the simplicity.
+ But anyone who has wrestled with &Automake; and &Autoconf;
+ to try to guarantee that a bit of software
+ will build correctly on multiple platforms
+ can tell you that it takes a lot of work to get right.
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>&SCons; Principles</title>
+
+ <para>
+
+ There are a few overriding principles
+ we try to live up to in designing and implementing &SCons:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Correctness</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Performance</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Convenience</term>
+
+ <listitem>
+ <para>
+
+ &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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ In a nutshell, we try hard to make &SCons; just
+ "do the right thing" and build software correctly,
+ with a minimum of hassles.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>History</title>
+
+ <para>
+
+ &SCons; originated with a design
+ that was submitted to the Software Carpentry
+ design competition in 2000.
+
+ </para>
+
+ <para>
+
+ &SCons; is the direct descendant
+ of a Perl utility called &Cons;.
+ &Cons; in turn based some of its ideas on &Jam;,
+ a build tool from Perforce Systems.
+
+ </para>
+
+ <para>
+
+ XXX history of SCons
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+ <title>Conventions</title>
+
+ <para>
+
+ XXX conventions used in this manual
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>A Caveat About This Guide's Completeness</title>
+
+ <para>
+
+ 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?)
+
+ </para>
+
+ <para>
+
+ 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...?
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Acknowledgements</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <!--
+
+ <para>
+
+ &SCons; has received contributions
+ from many other people, of course:
+ Matt Balvin (extending long command-line support on Windows),
+ Allen Bierbaum (extensions and fixes to Options),
+ Steve Christensen (help text sorting and function action signature fixes),
+ Michael Cook (avoiding losing signal bits from executed commands),
+ Derrick 'dman' Hudson (),
+ Alex Jacques (work on the Windows scons.bat file),
+ Stephen Kennedy (performance enhancements),
+ Lachlan O'Dea (SharedObject() support for masm
+ and normalized paths for the WhereIs() function),
+ Damyan Pepper (keeping output like Make),
+ Jeff Petkau (significant fixes for CacheDir and other areas),
+ Stefan Reichor (Ghostscript support),
+ Zed Shaw (Append() and Replace() environment methods),
+ Terrel Shumway (build and test fixes, as well as the SCons Wiki)
+ and
+ sam th (dynamic checks for utilities).
+
+ </para>
+
+ -->
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Contact</title>
+
+ <para>
+
+ The best way to contact people involved with SCons,
+ including the author,
+ is through the SCons mailing lists.
+
+ </para>
+
+ <para>
+
+ If you want to ask general questions about how to use &SCons;
+ send email to &scons-users;.
+
+ </para>
+
+ <para>
+
+ If you want to contact the &SCons; development community directly,
+ send email to &scons-devel;.
+
+ </para>
+
+ <para>
+
+ If you want to receive announcements about &SCons,
+ join the low-volume &scons-announce; mailing list.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ The most distinctive thing about &SCons;
+ is that its configuration files are
+ actually <emphasis>scripts</emphasis>,
+ 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.
+
+ </para>
+
+ <para>
+
+ Paradoxically,
+ using Python as the configuration file format
+ makes &SCons;
+ <emphasis>easier</emphasis>
+ 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.
+
+ </para>
+
+ <!--
+
+ <section>
+ <title>Why &SCons;?</title>
+
+ <para>
+
+ &SCons; is a response to a perennial problem:
+ building software is harder than it should be.
+ In a nutshell: the old, reliable model of the
+ venerable and ubiquitous &Make; program
+ has had a hard time keeping up with
+ how complicated building software has become.
+ The fact that &Make; has kept up as well as it has is impressive,
+ and a testament to how the simplicity.
+ But anyone who has wrestled with &Automake; and &Autoconf;
+ to try to guarantee that a bit of software
+ will build correctly on multiple platforms
+ can tell you that it takes a lot of work to get right.
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>&SCons; Principles</title>
+
+ <para>
+
+ There are a few overriding principles
+ we try to live up to in designing and implementing &SCons;:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Correctness</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Performance</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Convenience</term>
+
+ <listitem>
+ <para>
+
+ &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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ In a nutshell, we try hard to make &SCons; just
+ "do the right thing" and build software correctly,
+ with a minimum of hassles.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>History</title>
+
+ <para>
+
+ &SCons; originated with a design
+ that was submitted to the Software Carpentry
+ design competition in 2000.
+
+ </para>
+
+ <para>
+
+ &SCons; is the direct descendant
+ of a Perl utility called &Cons;.
+ &Cons; in turn based some of its ideas on &Jam;,
+ a build tool from Perforce Systems.
+
+ </para>
+
+ <para>
+
+ XXX history of SCons
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+ <title>Conventions</title>
+
+ <para>
+
+ XXX conventions used in this manual
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>A Caveat About This Guide's Completeness</title>
+
+ <para>
+
+ 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?)
+
+ </para>
+
+ <para>
+
+ 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...?
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Acknowledgements</title>
+
+ <para>
+
+ &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:
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <!--
+
+ <para>
+
+ &SCons; has received contributions
+ from many other people, of course:
+ Matt Balvin (extending long command-line support on Windows),
+ Allen Bierbaum (extensions and fixes to Options),
+ Steve Christensen (help text sorting and function action signature fixes),
+ Michael Cook (avoiding losing signal bits from executed commands),
+ Derrick 'dman' Hudson (),
+ Alex Jacques (work on the Windows scons.bat file),
+ Stephen Kennedy (performance enhancements),
+ Lachlan O'Dea (SharedObject() support for masm
+ and normalized paths for the WhereIs() function),
+ Damyan Pepper (keeping output like Make),
+ Jeff Petkau (significant fixes for CacheDir and other areas),
+ Stefan Reichor (Ghostscript support),
+ Zed Shaw (Append() and Replace() environment methods),
+ Terrel Shumway (build and test fixes, as well as the SCons Wiki)
+ and
+ sam th (dynamic checks for utilities).
+
+ </para>
+
+ -->
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Contact</title>
+
+ <para>
+
+ The best way to contact people involved with SCons,
+ including the author,
+ is through the SCons mailing lists.
+
+ </para>
+
+ <para>
+
+ If you want to ask general questions about how to use &SCons;
+ send email to &scons-users;.
+
+ </para>
+
+ <para>
+
+ If you want to contact the &SCons; development community directly,
+ send email to &scons-devel;.
+
+ </para>
+
+ <para>
+
+ If you want to receive announcements about &SCons;,
+ join the low-volume &scons-announce; mailing list.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <!--
+
+ <section>
+ <title>Python Overview</title>
+
+ <para>
+
+ This section will provide a brief overview of
+ the Python programming language.
+ Skip this section if you are already familiar with Python
+ (or you're really intent on diving into &SCons;
+ and just picking up things as you go).
+
+ </para>
+
+ <para>
+
+ Python has a lot of good
+ documentation freely available on-line
+ to help you get started.
+ The standard tutorial is available at XXX.
+
+
+ </para>
+
+ <para>
+
+ Python is very easy to pick up.
+
+ </para>
+
+ <para>
+
+ Python variables must be assigned to before they can be referenced.
+
+ </para>
+
+ <para>
+
+ Assignment is like most programming languages:
+
+ x = 1 + 2
+ z = 3 * x
+
+ </para>
+
+ <para>
+
+ Function calls look like most language function calls:
+
+ a = f(g)
+
+ </para>
+
+ <para>
+
+ Define functions like so:
+
+ def func(arg1, arg2):
+ return arg1 * arg 2
+
+ The number of parameters
+
+ </para>
+
+ <para>
+
+ Strings can be enclosed in single quotes or double quotes,
+ backslashes are used to escape characters,
+ triple-quote syntax lets you include quotes and newlines,
+ raw strings begin with 'r'.
+
+ </para>
+
+ <para>
+
+ Lists are enclosed in square brackets,
+ list items are separated by commas.
+ List references use square brackets and integer index values,
+ slice notation lets you select, delete or replace a range.
+
+ </para>
+
+ <para>
+
+ Dictionaries (hashes) are enclosed in curly brackets,
+ : separates keys from values,
+ , separates items.
+ Dictionary values are referenced using square brackets.
+
+ </para>
+
+ <para>
+
+ Access class attributes (including methods) using a '.'.
+
+ </para>
+
+ <para>
+
+ if: statements look like
+
+ elif: statements look like
+
+ else: statements look like
+
+ </para>
+
+ <para>
+
+ for: statements look like
+
+ while: statements look like
+
+ break statements look like
+
+ continue statements look like
+
+ </para>
+
+ <para>
+
+ pass
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <!--
+
+ <section>
+ <title>Python Overview</title>
+
+ <para>
+
+ This section will provide a brief overview of
+ the Python programming language.
+ Skip this section if you are already familiar with Python
+ (or you're really intent on diving into &SCons;
+ and just picking up things as you go).
+
+ </para>
+
+ <para>
+
+ Python has a lot of good
+ documentation freely available on-line
+ to help you get started.
+ The standard tutorial is available at XXX.
+
+
+ </para>
+
+ <para>
+
+ Python is very easy to pick up.
+
+ </para>
+
+ <para>
+
+ Python variables must be assigned to before they can be referenced.
+
+ </para>
+
+ <para>
+
+ Assignment is like most programming languages:
+
+ x = 1 + 2
+ z = 3 * x
+
+ </para>
+
+ <para>
+
+ Function calls look like most language function calls:
+
+ a = f(g)
+
+ </para>
+
+ <para>
+
+ Define functions like so:
+
+ def func(arg1, arg2):
+ return arg1 * arg 2
+
+ The number of parameters
+
+ </para>
+
+ <para>
+
+ Strings can be enclosed in single quotes or double quotes,
+ backslashes are used to escape characters,
+ triple-quote syntax lets you include quotes and newlines,
+ raw strings begin with 'r'.
+
+ </para>
+
+ <para>
+
+ Lists are enclosed in square brackets,
+ list items are separated by commas.
+ List references use square brackets and integer index values,
+ slice notation lets you select, delete or replace a range.
+
+ </para>
+
+ <para>
+
+ Dictionaries (hashes) are enclosed in curly brackets,
+ : separates keys from values,
+ , separates items.
+ Dictionary values are referenced using square brackets.
+
+ </para>
+
+ <para>
+
+ Access class attributes (including methods) using a '.'.
+
+ </para>
+
+ <para>
+
+ if: statements look like
+
+ elif: statements look like
+
+ else: statements look like
+
+ </para>
+
+ <para>
+
+ for: statements look like
+
+ while: statements look like
+
+ break statements look like
+
+ continue statements look like
+
+ </para>
+
+ <para>
+
+ pass
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>The &Repository; Method</title>
+
+ <!--
+
+ The repository directories specified may contain source files, derived files
+ (objects, libraries and executables), or both. If there is no local file
+ (source or derived) under the directory in which Cons is executed, then the
+ first copy of a same-named file found under a repository directory will be
+ used to build any local derived files.
+
+ -->
+
+ <para>
+
+ 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.)
+ <!--
+ For information about using &SCons;
+ with these systems, see the section,
+ "Fetching Files From Source Code Management Systems,"
+ below.)
+ -->
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding source files in repositories</title>
+
+ <para>
+
+ The above example
+ specifies that &SCons;
+ will first search for files under
+ the <filename>/usr/repository1</filename> tree
+ and next under the <filename>/usr/repository2</filename> 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 <filename>/usr/repository1/hello.c</filename> file
+ and then for a <filename>/usr/repository2/hello.c</filename> file
+ to use in its place.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ If, however, there is no local &hello_c; file,
+ but one exists in <filename>/usr/repository1</filename>,
+ &SCons; will recompile the &hello; program
+ from the source file it finds in the repository:
+
+ </para>
+
+ <scons_example name="ex2">
+ <file name="SConstruct">
+ env = Environment()
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
+ </file>
+ <file name="__ROOT__/usr/repository1/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And similarly, if there is no local &hello_c; file
+ and no <filename>/usr/repository1/hello.c</filename>,
+ but one exists in <filename>/usr/repository2</filename>:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct">
+ env = Environment()
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
+ </file>
+ <file name="__ROOT__/usr/repository2/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="ex3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ We've already seen that SCons will scan the contents of
+ a source file for <literal>#include</literal> file names
+ and realize that targets built from that source file
+ also depend on the <literal>#include</literal> 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
+ <literal>#include</literal> files that it finds
+ in repository directory.
+
+ </para>
+
+ <para>
+
+ Unless the C compiler also knows about these directories
+ in the repository trees, though,
+ it will be unable to find the <literal>#include</literal> 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ hello.c:1: hello.h: No such file or directory
+ </screen>
+
+ <para>
+
+ In order to inform the C compiler about the repositories,
+ &SCons; will add appropriate
+ <literal>-I</literal> 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:
+
+ </para>
+
+ <scons_example name="CPPPATH">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then re-executing &SCons; yields:
+
+ </para>
+
+ <scons_output example="CPPPATH">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The order of the <literal>-I</literal> 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 <literal>-I</literal> flags.
+ If, for example, the &cv-CPPPATH; contains three directories
+ (and shorter repository path names!):
+
+ </para>
+
+ <scons_example name="CPPPATH3">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3'])
+ env.Program('hello.c')
+ Repository('__ROOT__/r1', '__ROOT__/r2')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then we'll end up with nine <literal>-I</literal> options
+ on the command line,
+ three (for each of the &cv-CPPPATH; directories)
+ times three (for the local directory plus the two repositories):
+
+ </para>
+
+ <scons_output example="CPPPATH3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+<!--
+
+Cons classic did the following, does SCons?
+
+In order to shorten the command lines as much as possible, Cons will
+remove C<-I> flags for any directories, locally or in the repositories,
+which do not actually exist. (Note that the C<-I> flags are not included
+in the MD5 signature calculation for the target file, so the target will
+not be recompiled if the compilation command changes due to a directory
+coming into existence.)
+
+-->
+
+ <section>
+ <title>Limitations on <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ &SCons; relies on the C compiler's
+ <literal>-I</literal> options to control the order in which
+ the preprocessor will search the repository directories
+ for <literal>#include</literal> files.
+ This causes a problem, however, with how the C preprocessor
+ handles <literal>#include</literal> lines with
+ the file name included in double-quotes.
+
+ </para>
+
+ <para>
+
+ 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 <literal>#include</literal> line with the file name in
+ double quotes:
+
+ </para>
+
+ <programlisting>
+ #include "hello.h"
+ int
+ main(int argc, char *argv[])
+ {
+ printf(HELLO_MESSAGE);
+ return (0);
+ }
+ </programlisting>
+
+ <para>
+
+ Then the C preprocessor will <emphasis>always</emphasis>
+ 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
+ <literal>-I</literal> as the first option:
+
+ </para>
+
+ <scons_example name="quote1">
+ <file name="SConstruct">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1')
+ </file>
+ <file name="__ROOT__/usr/repository1/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="quote1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ This behavior of the C preprocessor--always search
+ for a <literal>#include</literal> file in double-quotes
+ first in the same directory as the source file,
+ and only then search the <literal>-I</literal>--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:
+
+ </para>
+
+ <orderedlist>
+
+ <listitem>
+ <para>
+
+ 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!
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Change all occurrences of <literal>#include "file.h"</literal>
+ to <literal>#include &amp;lt;file.h&amp;gt;</literal>.
+ Use of <literal>#include</literal> with angle brackets
+ does not have the same behavior--the <literal>-I</literal>
+ directories are searched first
+ for <literal>#include</literal> files--which
+ gives &SCons; direct control over the list of
+ directories the C preprocessor will search.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </orderedlist>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Finding the &SConstruct; file in repositories</title>
+
+ <para>
+
+ &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 <literal>-Y</literal> option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -Y /usr/repository1 -Y /usr/repository2</userinput>
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding derived files in repositories</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct">
+ env = Environment()
+ env.Program(['hello.c', 'file1.c', 'file2.c'])
+ Repository('/usr/repository1', '/usr/repository2')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="file1.c">
+ int f1() { printf("file1\n"); }
+ </file>
+ <file name="file2.c">
+ int f2() { printf("file2.c\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="ex4">
+ <scons_output_command>cd /usr/repository1</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ (Note that this is safe even if the &SConstruct; file
+ lists <filename>/usr/repository1</filename> as a repository,
+ because &SCons; will remove the current build directory
+ from its repository list for that invocation.)
+
+ </para>
+
+ <para>
+
+ 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 <literal>-Y</literal> option to
+ tell &SCons; to fetch any other files it needs
+ from the repository:
+
+ </para>
+
+ <!--
+ <scons_output example="ex4">
+ <scons_output_command>cd $HOME/build</scons_output_command>
+ <scons_output_command>edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q -Y __ROOT__/usr/repository1</scons_output_command>
+ </scons_output>
+ -->
+ <screen>
+ % <userinput>cd $HOME/build</userinput>
+ % <userinput>edit hello.c</userinput>
+ % <userinput>scons -Q -Y /usr/repository1</userinput>
+ cc -c -o hello.o hello.c
+ cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o
+ </screen>
+
+ <para>
+
+ Notice that &SCons; realizes that it does not need to
+ rebuild local copies <filename>file1.o</filename> and <filename>file2.o</filename> files,
+ but instead uses the already-compiled files
+ from the repository.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Guaranteeing local copies of files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>mkdir $HOME/build2</userinput>
+ % <userinput>cd $HOME/build2</userinput>
+ % <userinput>scons -Q -Y /usr/all/repository hello</userinput>
+ scons: `hello' is up-to-date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ hello = env.Program('hello.c')
+ Local(hello)
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Y /usr/all/repository hello</userinput>
+ Local copy of hello from /usr/all/repository/hello
+ scons: `hello' is up-to-date.
+ </screen>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>The &Repository; Method</title>
+
+ <!--
+
+ The repository directories specified may contain source files, derived files
+ (objects, libraries and executables), or both. If there is no local file
+ (source or derived) under the directory in which Cons is executed, then the
+ first copy of a same-named file found under a repository directory will be
+ used to build any local derived files.
+
+ -->
+
+ <para>
+
+ 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.)
+ <!--
+ For information about using &SCons;
+ with these systems, see the section,
+ "Fetching Files From Source Code Management Systems,"
+ below.)
+ -->
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Program('hello.c')
+ Repository('/usr/repository1', '/usr/repository2')
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding source files in repositories</title>
+
+ <para>
+
+ The above example
+ specifies that &SCons;
+ will first search for files under
+ the <filename>/usr/repository1</filename> tree
+ and next under the <filename>/usr/repository2</filename> 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 <filename>/usr/repository1/hello.c</filename> file
+ and then for a <filename>/usr/repository2/hello.c</filename> file
+ to use in its place.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ If, however, there is no local &hello_c; file,
+ but one exists in <filename>/usr/repository1</filename>,
+ &SCons; will recompile the &hello; program
+ from the source file it finds in the repository:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c /usr/repository1/hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ And similarly, if there is no local &hello_c; file
+ and no <filename>/usr/repository1/hello.c</filename>,
+ but one exists in <filename>/usr/repository2</filename>:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c /usr/repository2/hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ We've already seen that SCons will scan the contents of
+ a source file for <literal>#include</literal> file names
+ and realize that targets built from that source file
+ also depend on the <literal>#include</literal> 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
+ <literal>#include</literal> files that it finds
+ in repository directory.
+
+ </para>
+
+ <para>
+
+ Unless the C compiler also knows about these directories
+ in the repository trees, though,
+ it will be unable to find the <literal>#include</literal> 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ hello.c:1: hello.h: No such file or directory
+ </screen>
+
+ <para>
+
+ In order to inform the C compiler about the repositories,
+ &SCons; will add appropriate
+ <literal>-I</literal> 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CPPPATH = ['.'])
+ env.Program('hello.c')
+ Repository('/usr/repository1')
+ </programlisting>
+
+ <para>
+
+ Then re-executing &SCons; yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -I. -I/usr/repository1 hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ The order of the <literal>-I</literal> 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 <literal>-I</literal> flags.
+ If, for example, the &cv-CPPPATH; contains three directories
+ (and shorter repository path names!):
+
+ </para>
+
+ <programlisting>
+ env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3'])
+ env.Program('hello.c')
+ Repository('/r1', '/r2')
+ </programlisting>
+
+ <para>
+
+ Then we'll end up with nine <literal>-I</literal> options
+ on the command line,
+ three (for each of the &cv-CPPPATH; directories)
+ times three (for the local directory plus the two repositories):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+<!--
+
+Cons classic did the following, does SCons?
+
+In order to shorten the command lines as much as possible, Cons will
+remove C<-I> flags for any directories, locally or in the repositories,
+which do not actually exist. (Note that the C<-I> flags are not included
+in the MD5 signature calculation for the target file, so the target will
+not be recompiled if the compilation command changes due to a directory
+coming into existence.)
+
+-->
+
+ <section>
+ <title>Limitations on <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ &SCons; relies on the C compiler's
+ <literal>-I</literal> options to control the order in which
+ the preprocessor will search the repository directories
+ for <literal>#include</literal> files.
+ This causes a problem, however, with how the C preprocessor
+ handles <literal>#include</literal> lines with
+ the file name included in double-quotes.
+
+ </para>
+
+ <para>
+
+ 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 <literal>#include</literal> line with the file name in
+ double quotes:
+
+ </para>
+
+ <programlisting>
+ #include "hello.h"
+ int
+ main(int argc, char *argv[])
+ {
+ printf(HELLO_MESSAGE);
+ return (0);
+ }
+ </programlisting>
+
+ <para>
+
+ Then the C preprocessor will <emphasis>always</emphasis>
+ 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
+ <literal>-I</literal> as the first option:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -I. -I/usr/repository1 /usr/repository1/hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ This behavior of the C preprocessor--always search
+ for a <literal>#include</literal> file in double-quotes
+ first in the same directory as the source file,
+ and only then search the <literal>-I</literal>--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:
+
+ </para>
+
+ <orderedlist>
+
+ <listitem>
+ <para>
+
+ 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!
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Change all occurrences of <literal>#include "file.h"</literal>
+ to <literal>#include &lt;file.h&gt;</literal>.
+ Use of <literal>#include</literal> with angle brackets
+ does not have the same behavior--the <literal>-I</literal>
+ directories are searched first
+ for <literal>#include</literal> files--which
+ gives &SCons; direct control over the list of
+ directories the C preprocessor will search.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+
+ </orderedlist>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Finding the &SConstruct; file in repositories</title>
+
+ <para>
+
+ &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 <literal>-Y</literal> option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -Y /usr/repository1 -Y /usr/repository2</userinput>
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding derived files in repositories</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>cd /usr/repository1</userinput>
+ % <userinput>scons -Q</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ (Note that this is safe even if the &SConstruct; file
+ lists <filename>/usr/repository1</filename> as a repository,
+ because &SCons; will remove the current build directory
+ from its repository list for that invocation.)
+
+ </para>
+
+ <para>
+
+ 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 <literal>-Y</literal> option to
+ tell &SCons; to fetch any other files it needs
+ from the repository:
+
+ </para>
+
+ <!--
+ <scons_output example="ex4">
+ <scons_output_command>cd $HOME/build</scons_output_command>
+ <scons_output_command>edit hello.c</scons_output_command>
+ <scons_output_command>scons -Q -Y __ROOT__/usr/repository1</scons_output_command>
+ </scons_output>
+ -->
+ <screen>
+ % <userinput>cd $HOME/build</userinput>
+ % <userinput>edit hello.c</userinput>
+ % <userinput>scons -Q -Y /usr/repository1</userinput>
+ cc -c -o hello.o hello.c
+ cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o
+ </screen>
+
+ <para>
+
+ Notice that &SCons; realizes that it does not need to
+ rebuild local copies <filename>file1.o</filename> and <filename>file2.o</filename> files,
+ but instead uses the already-compiled files
+ from the repository.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Guaranteeing local copies of files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>mkdir $HOME/build2</userinput>
+ % <userinput>cd $HOME/build2</userinput>
+ % <userinput>scons -Q -Y /usr/all/repository hello</userinput>
+ scons: `hello' is up-to-date.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ hello = env.Program('hello.c')
+ Local(hello)
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Y /usr/all/repository hello</userinput>
+ Local copy of hello from /usr/all/repository/hello
+ scons: `hello' is up-to-date.
+ </screen>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Invoking Cons
+
+The C<cons> command is usually invoked from the root of the build tree. A
+F<Construct> file must exist in that directory. If the C<-f> argument is
+used, then an alternate F<Construct> file may be used (and, possibly, an
+alternate root, since C<cons> will cd to F<Construct> file's containing
+directory).
+
+If C<cons> is invoked from a child of the root of the build tree with
+the C<-t> argument, it will walk up the directory hierarchy looking for a
+F<Construct> file. (An alternate name may still be specified with C<-f>.)
+The targets supplied on the command line will be modified to be relative
+to the discovered F<Construct> file. For example, from a directory
+containing a top-level F<Construct> file, the following invocation:
+
+ % cd libfoo/subdir
+ % cons -t target
+
+is exactly equivalent to:
+
+ % cons libfoo/subdir/target
+
+If there are any C<Default> targets specified in the directory hierarchy's
+F<Construct> or F<Conscript> files, only the default targets at or below
+the directory from which C<cons -t> was invoked will be built.
+
+The command is invoked as follows:
+
+ cons <arguments> , <construct-args>
+
+where I<arguments> can be any of the following, in any order:
+
+=over 10
+
+=item I<target>
+
+Build the specified target. If I<target> is a directory, then recursively
+build everything within that directory.
+
+=item I<+pattern>
+
+Limit the F<Conscript> files considered to just those that match I<pattern>,
+which is a Perl regular expression. Multiple C<+> arguments are accepted.
+
+=item I<name>=<val>
+
+Sets I<name> to value I<val> in the C<ARG> hash passed to the top-level
+F<Construct> file.
+
+=item C<-cc>
+
+Show command that would have been executed, when retrieving from cache. No
+indication that the file has been retrieved is given; this is useful for
+generating build logs that can be compared with real build logs.
+
+=item C<-cd>
+
+Disable all caching. Do not retrieve from cache nor flush to cache.
+
+=item C<-cr>
+
+Build dependencies in random order. This is useful when building multiple
+similar trees with caching enabled.
+
+=item C<-cs>
+
+Synchronize existing build targets that are found to be up-to-date with
+cache. This is useful if caching has been disabled with -cc or just recently
+enabled with UseCache.
+
+=item C<-d>
+
+Enable dependency debugging.
+
+=item C<-f> <file>
+
+Use the specified file instead of F<Construct> (but first change to
+containing directory of I<file>).
+
+=item C<-h>
+
+Show a help message local to the current build if one such is defined, and
+exit.
+
+=item C<-k>
+
+Keep going as far as possible after errors.
+
+=item C<-o> <file>
+
+Read override file I<file>.
+
+=item C<-p>
+
+Show construction products in specified trees. No build is attempted.
+
+=item C<-pa>
+
+Show construction products and associated actions. No build is attempted.
+
+=item C<-pw>
+
+Show products and where they are defined. No build is attempted.
+
+=item C<-q>
+
+Make the build quiet. Multiple C<-q> options may be specified.
+
+A single C<-q> options suppress messages about Installing and Removing
+targets.
+
+Two C<-q> options suppress build command lines and target up-to-date
+messages.
+
+=item C<-r>
+
+Remove construction products associated with <targets>. No build is
+attempted.
+
+=item C<-R> <repos>
+
+Search for files in I<repos>. Multiple B<-R> I<repos> directories are
+searched in the order specified.
+
+=item C<-S> <pkg>
+
+Use the sig::<pkg> package to calculate. Supported <pkg> values
+include "md5" for MD5 signature calculation and "md5::debug" for debug
+information about MD5 signature calculation.
+
+If the specified package ends in <::debug>, signature debug information
+will be printed to the file name specified in the C<CONS_SIG_DEBUG>
+environment variable, or to standard output if the environment variable
+is not set.
+
+=item C<-t>
+
+Traverse up the directory hierarchy looking for a F<Construct> file,
+if none exists in the current directory. Targets will be modified to
+be relative to the F<Construct> file.
+
+Internally, C<cons> will change its working directory to the directory
+which contains the top-level F<Construct> file and report:
+
+ cons: Entering directory `top-level-directory'
+
+This message indicates to an invoking editor (such as emacs) or build
+environment that Cons will now report all file names relative to the
+top-level directory. This message can not be suppressed with the C<-q>
+option.
+
+=item C<-v>
+
+Show C<cons> version and continue processing.
+
+=item C<-V>
+
+Show C<cons> version and exit.
+
+=item C<-wf> <file>
+
+Write all filenames considered into I<file>.
+
+=item C<-x>
+
+Show a help message similar to this one, and exit.
+
+=back
+
+And I<construct-args> can be any arguments that you wish to process in the
+F<Construct> file. Note that there should be a B<-,-> separating the arguments
+to cons and the arguments that you wish to process in the F<Construct> file.
+
+Processing of I<construct-args> can be done by any standard package like
+B<Getopt> or its variants, or any user defined package. B<cons> will pass in
+the I<construct-args> as B<@ARGV> and will not attempt to interpret anything
+after the B<-,->.
+
+ % cons -R /usr/local/repository -d os=solaris +driver -,- -c test -f DEBUG
+
+would pass the following to cons
+
+ -R /usr/local/repository -d os=solaris +driver
+
+and the following, to the top level F<Construct> file as B<@ARGV>
+
+ -c test -f DEBUG
+
+Note that C<cons -r .> is equivalent to a full recursive C<make clean>,
+but requires no support in the F<Construct> file or any F<Conscript>
+files. This is most useful if you are compiling files into source
+directories (if you separate the F<build> and F<export> directories,
+then you can just remove the directories).
+
+The options C<-p>, C<-pa>, and C<-pw> are extremely useful for use as an aid
+in reading scripts or debugging them. If you want to know what script
+installs F<export/include/foo.h>, for example, just type:
+
+ % cons -pw export/include/foo.h
+
+=head1 Selective builds
+
+Cons provides two methods for reducing the size of given build. The first is
+by specifying targets on the command line, and the second is a method for
+pruning the build tree. We'll consider target specification first.
+
+
+=head2 Selective targeting
+
+Like make, Cons allows the specification of ``targets'' on the command
+line. Cons targets may be either files or directories. When a directory is
+specified, this is simply a short-hand notation for every derivable
+product-,-that Cons knows about-,-in the specified directory and below. For
+example:
+
+ % cons build/hello/hello.o
+
+means build F<hello.o> and everything that F<hello.o> might need. This is
+from a previous version of the B<Hello, World!> program in which F<hello.o>
+depended upon F<export/include/world.h>. If that file is not up-to-date
+(because someone modified F<src/world/world.h)>, then it will be rebuilt,
+even though it is in a directory remote from F<build/hello>.
+
+In this example:
+
+ % cons build
+
+Everything in the F<build> directory is built, if necessary. Again, this may
+cause more files to be built. In particular, both F<export/include/world.h>
+and F<export/lib/libworld.a> are required by the F<build/hello> directory,
+and so they will be built if they are out-of-date.
+
+If we do, instead:
+
+ % cons export
+
+then only the files that should be installed in the export directory will be
+rebuilt, if necessary, and then installed there. Note that C<cons build>
+might build files that C<cons export> doesn't build, and vice-versa.
+
+
+=head1 Build Pruning
+
+In conjunction with target selection, B<build pruning> can be used to reduce
+the scope of the build. In the previous peAcH and baNaNa example, we have
+already seen how script-driven build pruning can be used to make only half
+of the potential build available for any given invocation of C<cons>. Cons
+also provides, as a convenience, a command line convention that allows you
+to specify which F<Conscript> files actually get ``built''-,-that is,
+incorporated into the build tree. For example:
+
+ % cons build +world
+
+The C<+> argument introduces a Perl regular expression. This must, of
+course, be quoted at the shell level if there are any shell meta-characters
+within the expression. The expression is matched against each F<Conscript>
+file which has been mentioned in a C<Build> statement, and only those
+scripts with matching names are actually incorporated into the build
+tree. Multiple such arguments are allowed, in which case a match against any
+of them is sufficient to cause a script to be included.
+
+In the example, above, the F<hello> program will not be built, since Cons
+will have no knowledge of the script F<hello/Conscript>. The F<libworld.a>
+archive will be built, however, if need be.
+
+There are a couple of uses for build pruning via the command line. Perhaps
+the most useful is the ability to make local changes, and then, with
+sufficient knowledge of the consequences of those changes, restrict the size
+of the build tree in order to speed up the rebuild time. A second use for
+build pruning is to actively prevent the recompilation of certain files that
+you know will recompile due to, for example, a modified header file. You may
+know that either the changes to the header file are immaterial, or that the
+changes may be safely ignored for most of the tree, for testing
+purposes.With Cons, the view is that it is pragmatic to admit this type of
+behavior, with the understanding that on the next full build everything that
+needs to be rebuilt will be. There is no equivalent to a ``make touch''
+command, to mark files as permanently up-to-date. So any risk that is
+incurred by build pruning is mitigated. For release quality work, obviously,
+we recommend that you do not use build pruning (it's perfectly OK to use
+during integration, however, for checking compilation, etc. Just be sure to
+do an unconstrained build before committing the integration).
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Command-Line Options</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Getting at Command-Line Arguments</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Selective Builds</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Build Pruning</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Overriding Construction Variables</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Invoking Cons
+
+The C<cons> command is usually invoked from the root of the build tree. A
+F<Construct> file must exist in that directory. If the C<-f> argument is
+used, then an alternate F<Construct> file may be used (and, possibly, an
+alternate root, since C<cons> will cd to F<Construct> file's containing
+directory).
+
+If C<cons> is invoked from a child of the root of the build tree with
+the C<-t> argument, it will walk up the directory hierarchy looking for a
+F<Construct> file. (An alternate name may still be specified with C<-f>.)
+The targets supplied on the command line will be modified to be relative
+to the discovered F<Construct> file. For example, from a directory
+containing a top-level F<Construct> file, the following invocation:
+
+ % cd libfoo/subdir
+ % cons -t target
+
+is exactly equivalent to:
+
+ % cons libfoo/subdir/target
+
+If there are any C<Default> targets specified in the directory hierarchy's
+F<Construct> or F<Conscript> files, only the default targets at or below
+the directory from which C<cons -t> was invoked will be built.
+
+The command is invoked as follows:
+
+ cons <arguments> , <construct-args>
+
+where I<arguments> can be any of the following, in any order:
+
+=over 10
+
+=item I<target>
+
+Build the specified target. If I<target> is a directory, then recursively
+build everything within that directory.
+
+=item I<+pattern>
+
+Limit the F<Conscript> files considered to just those that match I<pattern>,
+which is a Perl regular expression. Multiple C<+> arguments are accepted.
+
+=item I<name>=<val>
+
+Sets I<name> to value I<val> in the C<ARG> hash passed to the top-level
+F<Construct> file.
+
+=item C<-cc>
+
+Show command that would have been executed, when retrieving from cache. No
+indication that the file has been retrieved is given; this is useful for
+generating build logs that can be compared with real build logs.
+
+=item C<-cd>
+
+Disable all caching. Do not retrieve from cache nor flush to cache.
+
+=item C<-cr>
+
+Build dependencies in random order. This is useful when building multiple
+similar trees with caching enabled.
+
+=item C<-cs>
+
+Synchronize existing build targets that are found to be up-to-date with
+cache. This is useful if caching has been disabled with -cc or just recently
+enabled with UseCache.
+
+=item C<-d>
+
+Enable dependency debugging.
+
+=item C<-f> <file>
+
+Use the specified file instead of F<Construct> (but first change to
+containing directory of I<file>).
+
+=item C<-h>
+
+Show a help message local to the current build if one such is defined, and
+exit.
+
+=item C<-k>
+
+Keep going as far as possible after errors.
+
+=item C<-o> <file>
+
+Read override file I<file>.
+
+=item C<-p>
+
+Show construction products in specified trees. No build is attempted.
+
+=item C<-pa>
+
+Show construction products and associated actions. No build is attempted.
+
+=item C<-pw>
+
+Show products and where they are defined. No build is attempted.
+
+=item C<-q>
+
+Make the build quiet. Multiple C<-q> options may be specified.
+
+A single C<-q> options suppress messages about Installing and Removing
+targets.
+
+Two C<-q> options suppress build command lines and target up-to-date
+messages.
+
+=item C<-r>
+
+Remove construction products associated with <targets>. No build is
+attempted.
+
+=item C<-R> <repos>
+
+Search for files in I<repos>. Multiple B<-R> I<repos> directories are
+searched in the order specified.
+
+=item C<-S> <pkg>
+
+Use the sig::<pkg> package to calculate. Supported <pkg> values
+include "md5" for MD5 signature calculation and "md5::debug" for debug
+information about MD5 signature calculation.
+
+If the specified package ends in <::debug>, signature debug information
+will be printed to the file name specified in the C<CONS_SIG_DEBUG>
+environment variable, or to standard output if the environment variable
+is not set.
+
+=item C<-t>
+
+Traverse up the directory hierarchy looking for a F<Construct> file,
+if none exists in the current directory. Targets will be modified to
+be relative to the F<Construct> file.
+
+Internally, C<cons> will change its working directory to the directory
+which contains the top-level F<Construct> file and report:
+
+ cons: Entering directory `top-level-directory'
+
+This message indicates to an invoking editor (such as emacs) or build
+environment that Cons will now report all file names relative to the
+top-level directory. This message can not be suppressed with the C<-q>
+option.
+
+=item C<-v>
+
+Show C<cons> version and continue processing.
+
+=item C<-V>
+
+Show C<cons> version and exit.
+
+=item C<-wf> <file>
+
+Write all filenames considered into I<file>.
+
+=item C<-x>
+
+Show a help message similar to this one, and exit.
+
+=back
+
+And I<construct-args> can be any arguments that you wish to process in the
+F<Construct> file. Note that there should be a B<-,-> separating the arguments
+to cons and the arguments that you wish to process in the F<Construct> file.
+
+Processing of I<construct-args> can be done by any standard package like
+B<Getopt> or its variants, or any user defined package. B<cons> will pass in
+the I<construct-args> as B<@ARGV> and will not attempt to interpret anything
+after the B<-,->.
+
+ % cons -R /usr/local/repository -d os=solaris +driver -,- -c test -f DEBUG
+
+would pass the following to cons
+
+ -R /usr/local/repository -d os=solaris +driver
+
+and the following, to the top level F<Construct> file as B<@ARGV>
+
+ -c test -f DEBUG
+
+Note that C<cons -r .> is equivalent to a full recursive C<make clean>,
+but requires no support in the F<Construct> file or any F<Conscript>
+files. This is most useful if you are compiling files into source
+directories (if you separate the F<build> and F<export> directories,
+then you can just remove the directories).
+
+The options C<-p>, C<-pa>, and C<-pw> are extremely useful for use as an aid
+in reading scripts or debugging them. If you want to know what script
+installs F<export/include/foo.h>, for example, just type:
+
+ % cons -pw export/include/foo.h
+
+=head1 Selective builds
+
+Cons provides two methods for reducing the size of given build. The first is
+by specifying targets on the command line, and the second is a method for
+pruning the build tree. We'll consider target specification first.
+
+
+=head2 Selective targeting
+
+Like make, Cons allows the specification of ``targets'' on the command
+line. Cons targets may be either files or directories. When a directory is
+specified, this is simply a short-hand notation for every derivable
+product-,-that Cons knows about-,-in the specified directory and below. For
+example:
+
+ % cons build/hello/hello.o
+
+means build F<hello.o> and everything that F<hello.o> might need. This is
+from a previous version of the B<Hello, World!> program in which F<hello.o>
+depended upon F<export/include/world.h>. If that file is not up-to-date
+(because someone modified F<src/world/world.h)>, then it will be rebuilt,
+even though it is in a directory remote from F<build/hello>.
+
+In this example:
+
+ % cons build
+
+Everything in the F<build> directory is built, if necessary. Again, this may
+cause more files to be built. In particular, both F<export/include/world.h>
+and F<export/lib/libworld.a> are required by the F<build/hello> directory,
+and so they will be built if they are out-of-date.
+
+If we do, instead:
+
+ % cons export
+
+then only the files that should be installed in the export directory will be
+rebuilt, if necessary, and then installed there. Note that C<cons build>
+might build files that C<cons export> doesn't build, and vice-versa.
+
+
+=head1 Build Pruning
+
+In conjunction with target selection, B<build pruning> can be used to reduce
+the scope of the build. In the previous peAcH and baNaNa example, we have
+already seen how script-driven build pruning can be used to make only half
+of the potential build available for any given invocation of C<cons>. Cons
+also provides, as a convenience, a command line convention that allows you
+to specify which F<Conscript> files actually get ``built''-,-that is,
+incorporated into the build tree. For example:
+
+ % cons build +world
+
+The C<+> argument introduces a Perl regular expression. This must, of
+course, be quoted at the shell level if there are any shell meta-characters
+within the expression. The expression is matched against each F<Conscript>
+file which has been mentioned in a C<Build> statement, and only those
+scripts with matching names are actually incorporated into the build
+tree. Multiple such arguments are allowed, in which case a match against any
+of them is sufficient to cause a script to be included.
+
+In the example, above, the F<hello> program will not be built, since Cons
+will have no knowledge of the script F<hello/Conscript>. The F<libworld.a>
+archive will be built, however, if need be.
+
+There are a couple of uses for build pruning via the command line. Perhaps
+the most useful is the ability to make local changes, and then, with
+sufficient knowledge of the consequences of those changes, restrict the size
+of the build tree in order to speed up the rebuild time. A second use for
+build pruning is to actively prevent the recompilation of certain files that
+you know will recompile due to, for example, a modified header file. You may
+know that either the changes to the header file are immaterial, or that the
+changes may be safely ignored for most of the tree, for testing
+purposes.With Cons, the view is that it is pragmatic to admit this type of
+behavior, with the understanding that on the next full build everything that
+needs to be rebuilt will be. There is no equivalent to a ``make touch''
+command, to mark files as permanently up-to-date. So any risk that is
+incurred by build pruning is mitigated. For release quality work, obviously,
+we recommend that you do not use build pruning (it's perfectly OK to use
+during integration, however, for checking compilation, etc. Just be sure to
+do an unconstrained build before committing the integration).
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Command-Line Options</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Getting at Command-Line Arguments</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Selective Builds</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Build Pruning</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Overriding Construction Variables</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Using and writing dependency scanners
+
+QuickScan allows simple target-independent scanners to be set up for
+source files. Only one QuickScan scanner may be associated with any given
+source file and environment, although the same scanner may (and should)
+be used for multiple files of a given type.
+
+A QuickScan scanner is only ever invoked once for a given source file,
+and it is only invoked if the file is used by some target in the tree
+(i.e., there is a dependency on the source file).
+
+QuickScan is invoked as follows:
+
+ QuickScan CONSENV CODEREF, FILENAME [, PATH]
+
+The subroutine referenced by CODEREF is expected to return a list of
+filenames included directly by FILE. These filenames will, in turn, be
+scanned. The optional PATH argument supplies a lookup path for finding
+FILENAME and/or files returned by the user-supplied subroutine. The PATH
+may be a reference to an array of lookup-directory names, or a string of
+names separated by the system's separator character (':' on UNIX systems,
+';' on Windows NT).
+
+The subroutine is called once for each line in the file, with $_ set to the
+current line. If the subroutine needs to look at additional lines, or, for
+that matter, the entire file, then it may read them itself, from the
+filehandle SCAN. It may also terminate the loop, if it knows that no further
+include information is available, by closing the filehandle.
+
+Whether or not a lookup path is provided, QuickScan first tries to lookup
+the file relative to the current directory (for the top-level file
+supplied directly to QuickScan), or from the directory containing the
+file which referenced the file. This is not very general, but seems good
+enough, especially if you have the luxury of writing your own utilities
+and can control the use of the search path in a standard way.
+
+Here's a real example, taken from a F<Construct> file here:
+
+ sub cons::SMFgen {
+ my($env, @tables) = @_;
+ foreach $t (@tables) {
+ $env->QuickScan(sub { /\b\S*?\.smf\b/g }, "$t.smf",
+ $env->{SMF_INCLUDE_PATH});
+ $env->Command(["$t.smdb.cc","$t.smdb.h","$t.snmp.cc",
+ "$t.ami.cc", "$t.http.cc"], "$t.smf",
+ q(smfgen %( %SMF_INCLUDE_OPT %) %<));
+ }
+ }
+
+The subroutine above finds all names of the form <name>.smf in the
+file. It will return the names even if they're found within comments,
+but that's OK (the mechanism is forgiving of extra files; they're just
+ignored on the assumption that the missing file will be noticed when
+the program, in this example, smfgen, is actually invoked).
+
+[NOTE that the form C<$env-E<gt>QuickScan ...> and C<$env-E<gt>Command
+...> should not be necessary, but, for some reason, is required
+for this particular invocation. This appears to be a bug in Perl or
+a misunderstanding on my part; this invocation style does not always
+appear to be necessary.]
+
+Here is another way to build the same scanner. This one uses an
+explicit code reference, and also (unnecessarily, in this case) reads
+the whole file itself:
+
+ sub myscan {
+ my(@includes);
+ do {
+ push(@includes, /\b\S*?\.smf\b/g);
+ } while <SCAN>;
+ @includes
+ }
+
+Note that the order of the loop is reversed, with the loop test at the
+end. This is because the first line is already read for you. This scanner
+can be attached to a source file by:
+
+ QuickScan $env \&myscan, "$_.smf";
+
+This final example, which scans a different type of input file, takes
+over the file scanning rather than being called for each input line:
+
+ $env->QuickScan(
+ sub { my(@includes) = ();
+ do {
+ push(@includes, $3)
+ if /^(#include|import)\s+(\")(.+)(\")/ && $3
+ } while <SCAN>;
+ @includes
+ },
+ "$idlFileName",
+ "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces"
+ );
+
+-->
+
+ <para>
+
+ &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 <filename>.h</filename> files that are specified
+ using <literal>#include</literal> 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."
+
+ </para>
+
+ <section>
+ <title>A Simple Scanner Example</title>
+
+ <para>
+
+ Suppose, for example, that we want to create a simple scanner
+ for <filename>.foo</filename> files.
+ A <filename>.foo</filename> file contains some text that
+ will be processed,
+ and can include other files on lines that begin
+ with <literal>include</literal>
+ followed by a file name:
+
+ </para>
+
+ <programlisting>
+ include filename.foo
+ </programlisting>
+
+ <para>
+
+ Scanning a file will be handled by a Python function
+ that you must supply.
+ Here is a function that will use the Python
+ <filename>re</filename> module
+ to scan for the <literal>include</literal> lines in our example:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>node</term>
+
+ <listitem>
+ <para>
+
+ 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 <literal>str()</literal> function,
+ or an internal &SCons; <literal>get_text_contents()</literal>
+ object method can be used to fetch the contents.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ The construction environment in effect for this scan.
+ The scanner function may choose to use construction
+ variables from this environment to affect its behavior.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>path</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>arg</term>
+
+ <listitem>
+ <para>
+
+ An optional argument that you can choose to
+ have passed to this scanner function by
+ various scanner instances.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ A Scanner object is created using the &Scanner; function,
+ which typically takes an <literal>skeys</literal> 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:
+
+ </para>
+
+ <programlisting>
+ kscan = Scanner(function = kfile_scan,
+ skeys = ['.k'])
+ env.Append(SCANNERS = kscan)
+ </programlisting>
+
+ <para>
+
+ When we put it all together, it looks like:
+
+ </para>
+
+ <scons_example name="scan">
+ <file name="SConstruct" printme="1">
+ 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 &lt; $SOURCES &gt; $TARGET')
+ </file>
+ <file name="foo.k">
+ include other_file
+ </file>
+ <file name="other_file">
+ other_file
+ </file>
+ <directory name="__ROOT__/usr"></directory>
+ <directory name="__ROOT__/usr/local"></directory>
+ <directory name="__ROOT__/usr/local/bin"></directory>
+ <file name="__ROOT_/usr/local/bin/kprocess" chmod="755">
+ cat
+ </file>
+ </scons_example>
+
+ <!--
+
+ <para>
+
+ Now if we run &scons;
+ and then re-run it after changing the contents of
+ <filename>other_file</filename>,
+ the <filename>foo</filename>
+ target file will be automatically rebuilt:
+
+ </para>
+
+ <scons_output example="scan">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF other_file]">edit other_file</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Using and writing dependency scanners
+
+QuickScan allows simple target-independent scanners to be set up for
+source files. Only one QuickScan scanner may be associated with any given
+source file and environment, although the same scanner may (and should)
+be used for multiple files of a given type.
+
+A QuickScan scanner is only ever invoked once for a given source file,
+and it is only invoked if the file is used by some target in the tree
+(i.e., there is a dependency on the source file).
+
+QuickScan is invoked as follows:
+
+ QuickScan CONSENV CODEREF, FILENAME [, PATH]
+
+The subroutine referenced by CODEREF is expected to return a list of
+filenames included directly by FILE. These filenames will, in turn, be
+scanned. The optional PATH argument supplies a lookup path for finding
+FILENAME and/or files returned by the user-supplied subroutine. The PATH
+may be a reference to an array of lookup-directory names, or a string of
+names separated by the system's separator character (':' on UNIX systems,
+';' on Windows NT).
+
+The subroutine is called once for each line in the file, with $_ set to the
+current line. If the subroutine needs to look at additional lines, or, for
+that matter, the entire file, then it may read them itself, from the
+filehandle SCAN. It may also terminate the loop, if it knows that no further
+include information is available, by closing the filehandle.
+
+Whether or not a lookup path is provided, QuickScan first tries to lookup
+the file relative to the current directory (for the top-level file
+supplied directly to QuickScan), or from the directory containing the
+file which referenced the file. This is not very general, but seems good
+enough, especially if you have the luxury of writing your own utilities
+and can control the use of the search path in a standard way.
+
+Here's a real example, taken from a F<Construct> file here:
+
+ sub cons::SMFgen {
+ my($env, @tables) = @_;
+ foreach $t (@tables) {
+ $env->QuickScan(sub { /\b\S*?\.smf\b/g }, "$t.smf",
+ $env->{SMF_INCLUDE_PATH});
+ $env->Command(["$t.smdb.cc","$t.smdb.h","$t.snmp.cc",
+ "$t.ami.cc", "$t.http.cc"], "$t.smf",
+ q(smfgen %( %SMF_INCLUDE_OPT %) %<));
+ }
+ }
+
+The subroutine above finds all names of the form <name>.smf in the
+file. It will return the names even if they're found within comments,
+but that's OK (the mechanism is forgiving of extra files; they're just
+ignored on the assumption that the missing file will be noticed when
+the program, in this example, smfgen, is actually invoked).
+
+[NOTE that the form C<$env-E<gt>QuickScan ...> and C<$env-E<gt>Command
+...> should not be necessary, but, for some reason, is required
+for this particular invocation. This appears to be a bug in Perl or
+a misunderstanding on my part; this invocation style does not always
+appear to be necessary.]
+
+Here is another way to build the same scanner. This one uses an
+explicit code reference, and also (unnecessarily, in this case) reads
+the whole file itself:
+
+ sub myscan {
+ my(@includes);
+ do {
+ push(@includes, /\b\S*?\.smf\b/g);
+ } while <SCAN>;
+ @includes
+ }
+
+Note that the order of the loop is reversed, with the loop test at the
+end. This is because the first line is already read for you. This scanner
+can be attached to a source file by:
+
+ QuickScan $env \&myscan, "$_.smf";
+
+This final example, which scans a different type of input file, takes
+over the file scanning rather than being called for each input line:
+
+ $env->QuickScan(
+ sub { my(@includes) = ();
+ do {
+ push(@includes, $3)
+ if /^(#include|import)\s+(\")(.+)(\")/ && $3
+ } while <SCAN>;
+ @includes
+ },
+ "$idlFileName",
+ "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces"
+ );
+
+-->
+
+ <para>
+
+ &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 <filename>.h</filename> files that are specified
+ using <literal>#include</literal> 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."
+
+ </para>
+
+ <section>
+ <title>A Simple Scanner Example</title>
+
+ <para>
+
+ Suppose, for example, that we want to create a simple scanner
+ for <filename>.foo</filename> files.
+ A <filename>.foo</filename> file contains some text that
+ will be processed,
+ and can include other files on lines that begin
+ with <literal>include</literal>
+ followed by a file name:
+
+ </para>
+
+ <programlisting>
+ include filename.foo
+ </programlisting>
+
+ <para>
+
+ Scanning a file will be handled by a Python function
+ that you must supply.
+ Here is a function that will use the Python
+ <filename>re</filename> module
+ to scan for the <literal>include</literal> lines in our example:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>node</term>
+
+ <listitem>
+ <para>
+
+ 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 <literal>str()</literal> function,
+ or an internal &SCons; <literal>get_text_contents()</literal>
+ object method can be used to fetch the contents.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ The construction environment in effect for this scan.
+ The scanner function may choose to use construction
+ variables from this environment to affect its behavior.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>path</term>
+
+ <listitem>
+ <para>
+
+ 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.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>arg</term>
+
+ <listitem>
+ <para>
+
+ An optional argument that you can choose to
+ have passed to this scanner function by
+ various scanner instances.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ A Scanner object is created using the &Scanner; function,
+ which typically takes an <literal>skeys</literal> 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:
+
+ </para>
+
+ <programlisting>
+ kscan = Scanner(function = kfile_scan,
+ skeys = ['.k'])
+ env.Append(SCANNERS = kscan)
+ </programlisting>
+
+ <para>
+
+ When we put it all together, it looks like:
+
+ </para>
+
+ <programlisting>
+ 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 &lt; $SOURCES &gt; $TARGET')
+ </programlisting>
+
+ <!--
+
+ <para>
+
+ Now if we run &scons;
+ and then re-run it after changing the contents of
+ <filename>other_file</filename>,
+ the <filename>foo</filename>
+ target file will be automatically rebuilt:
+
+ </para>
+
+ <scons_output example="scan">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF other_file]">edit other_file</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <note>
+ <para>
+ 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.
+ </para>
+ </note>
+
+ <section>
+ <title>&Configure_Contexts;</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ conf = Configure(env)
+ # Checks for libraries, header files, etc. go here!
+ env = conf.Finish()
+ </sconstruct>
+
+ <para>
+
+ &SCons; provides a number of basic checks,
+ as well as a mechanism for adding your own custom checks.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ The next sections describe
+ the basic checks that &SCons; supports,
+ as well as how to add your own custom checks.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Checking for the Existence of Header Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ 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()
+ </sconstruct>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ If you need to check for the existence
+ a C++ header file,
+ use the &CheckCXXHeader; method:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ conf = Configure(env)
+ if not conf.CheckCXXHeader('vector.h'):
+ print 'vector.h must be installed!'
+ Exit(1)
+ env = conf.Finish()
+ </sconstruct>
+
+ </section>
+
+ <section>
+ <title>Checking for the Availability of a Function</title>
+
+ <para>
+
+ Check for the availability of a specific function
+ using the &CheckFunc; method:
+
+ </para>
+
+ <sconstruct>
+ 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()
+ </sconstruct>
+
+ </section>
+
+ <section>
+ <title>Checking for the Availability of a Library</title>
+
+ <para>
+
+ 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 <literal>lib</literal>
+ prefix or a <literal>.a</literal> or <literal>.lib</literal> suffix:
+
+ </para>
+
+ <sconstruct>
+ 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()
+ </sconstruct>
+
+ <para>
+
+ 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
+ <emphasis>and</emphasis> a header file
+ at the same time by using the
+ &CheckLibWithHeader; method:
+
+ </para>
+
+ <sconstruct>
+ 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()
+ </sconstruct>
+
+ <para>
+
+ This is essentially shorthand for
+ separate calls to the &CheckHeader; and &CheckLib;
+ functions.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Checking for the Availability of a &typedef;</title>
+
+ <para>
+
+ Check for the availability of a &typedef;
+ by using the &CheckType; method:
+
+ </para>
+
+ <sconstruct>
+ 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()
+ </sconstruct>
+
+ <para>
+
+ 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;:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ conf = Configure(env)
+ if not conf.CheckType('off_t', '#include &amp;lt;sys/types.h&amp;gt;\n'):
+ print 'Did not find off_t typedef, assuming int'
+ conf.env.Append(CCFLAGS = '-Doff_t=int')
+ env = conf.Finish()
+ </sconstruct>
+
+ </section>
+
+ <section>
+ <title>Adding Your Own Custom Checks</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ mylib_test_source_file = """
+ #include &amp;lt;mylib.h&amp;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
+ </sconstruct>
+
+ <para>
+
+ 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
+ <literal>yes</literal> if the check succeeds and
+ <literal>no</literal> if it doesn't.
+ The &TryLink; method
+ actually tests for whether the
+ specified program text
+ will successfully link.
+
+ </para>
+
+ <para>
+
+ (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 <literal>context.env</literal> attribute.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary})
+ </sconstruct>
+
+ <para>
+
+ You'll typically want to make
+ the check and the function name the same,
+ as we've done here,
+ to avoid potential confusion.
+
+ </para>
+
+ <para>
+
+ We can then put these pieces together
+ and actually call the <literal>CheckMyLibrary</literal> check
+ as follows:
+
+ </para>
+
+ <sconstruct>
+ mylib_test_source_file = """
+ #include &amp;lt;mylib.h&amp;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.
+ </sconstruct>
+
+ <para>
+
+ If MyLibrary is not installed on the system,
+ the output will look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript file ...
+ Checking for MyLibrary... failed
+ MyLibrary is not installed!
+ </screen>
+
+ <para>
+
+ If MyLibrary is installed,
+ the output will look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript file ...
+ Checking for MyLibrary... failed
+ scons: done reading SConscript
+ scons: Building targets ...
+ .
+ .
+ .
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Not Configuring When Cleaning Targets</title>
+
+ <para>
+
+ Using multi-platform configuration
+ as described in the previous sections
+ will run the configuration commands
+ even when invoking
+ <userinput>scons -c</userinput>
+ to clean targets:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -c</userinput>
+ Checking for MyLibrary... yes
+ Removed foo.o
+ Removed foo
+ </screen>
+
+ <para>
+
+ 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 <option>-c</option> (clean)
+ option has been invoked on the command line:
+
+ </para>
+
+ <sconstruct>
+ 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()
+ </sconstruct>
+
+ <screen>
+ % <userinput>scons -Q -c</userinput>
+ Removed foo.o
+ Removed foo
+ </screen>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Controlling Configuration: the &config; Option</title>
+
+ <para>
+
+ XXX -D, -u and -U
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ &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.
+
+ </para>
+
+ <note>
+ <para>
+ 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.
+ </para>
+ </note>
+
+ <section>
+ <title>&Configure_Contexts;</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ conf = Configure(env)
+ # Checks for libraries, header files, etc. go here!
+ env = conf.Finish()
+ </programlisting>
+
+ <para>
+
+ &SCons; provides a number of basic checks,
+ as well as a mechanism for adding your own custom checks.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ The next sections describe
+ the basic checks that &SCons; supports,
+ as well as how to add your own custom checks.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Checking for the Existence of Header Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ If you need to check for the existence
+ a C++ header file,
+ use the &CheckCXXHeader; method:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ conf = Configure(env)
+ if not conf.CheckCXXHeader('vector.h'):
+ print 'vector.h must be installed!'
+ Exit(1)
+ env = conf.Finish()
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Checking for the Availability of a Function</title>
+
+ <para>
+
+ Check for the availability of a specific function
+ using the &CheckFunc; method:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Checking for the Availability of a Library</title>
+
+ <para>
+
+ 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 <literal>lib</literal>
+ prefix or a <literal>.a</literal> or <literal>.lib</literal> suffix:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ <para>
+
+ 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
+ <emphasis>and</emphasis> a header file
+ at the same time by using the
+ &CheckLibWithHeader; method:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ <para>
+
+ This is essentially shorthand for
+ separate calls to the &CheckHeader; and &CheckLib;
+ functions.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Checking for the Availability of a &typedef;</title>
+
+ <para>
+
+ Check for the availability of a &typedef;
+ by using the &CheckType; method:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ <para>
+
+ 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;:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ </section>
+
+ <section>
+ <title>Adding Your Own Custom Checks</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ 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
+ </programlisting>
+
+ <para>
+
+ 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
+ <literal>yes</literal> if the check succeeds and
+ <literal>no</literal> if it doesn't.
+ The &TryLink; method
+ actually tests for whether the
+ specified program text
+ will successfully link.
+
+ </para>
+
+ <para>
+
+ (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 <literal>context.env</literal> attribute.)
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary})
+ </programlisting>
+
+ <para>
+
+ You'll typically want to make
+ the check and the function name the same,
+ as we've done here,
+ to avoid potential confusion.
+
+ </para>
+
+ <para>
+
+ We can then put these pieces together
+ and actually call the <literal>CheckMyLibrary</literal> check
+ as follows:
+
+ </para>
+
+ <programlisting>
+ 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.
+ </programlisting>
+
+ <para>
+
+ If MyLibrary is not installed on the system,
+ the output will look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript file ...
+ Checking for MyLibrary... failed
+ MyLibrary is not installed!
+ </screen>
+
+ <para>
+
+ If MyLibrary is installed,
+ the output will look like:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript file ...
+ Checking for MyLibrary... failed
+ scons: done reading SConscript
+ scons: Building targets ...
+ .
+ .
+ .
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Not Configuring When Cleaning Targets</title>
+
+ <para>
+
+ Using multi-platform configuration
+ as described in the previous sections
+ will run the configuration commands
+ even when invoking
+ <userinput>scons -c</userinput>
+ to clean targets:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q -c</userinput>
+ Checking for MyLibrary... yes
+ Removed foo.o
+ Removed foo
+ </screen>
+
+ <para>
+
+ 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 <option>-c</option> (clean)
+ option has been invoked on the command line:
+
+ </para>
+
+ <programlisting>
+ 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()
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q -c</userinput>
+ Removed foo.o
+ Removed foo
+ </screen>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Controlling Configuration: the &config; Option</title>
+
+ <para>
+
+ XXX -D, -u and -U
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Separating source and build trees
+
+It's often desirable to keep any derived files from the build completely
+separate from the source files. This makes it much easier to keep track of
+just what is a source file, and also makes it simpler to handle B<variant>
+builds, especially if you want the variant builds to co-exist.
+
+=head2 Separating build and source directories using the Link command
+
+Cons provides a simple mechanism that handles all of these requirements. The
+C<Link> command is invoked as in this example:
+
+ Link 'build' => 'src';
+
+The specified directories are ``linked'' to the specified source
+directory. Let's suppose that you setup a source directory, F<src>, with the
+sub-directories F<world> and F<hello> below it, as in the previous
+example. You could then substitute for the original build lines the
+following:
+
+ Build qw(
+ build/world/Conscript
+ build/hello/Conscript
+ );
+
+Notice that you treat the F<Conscript> file as if it existed in the build
+directory. Now if you type the same command as before, you will get the
+following results:
+
+ % cons export
+ Install build/world/world.h as export/include/world.h
+ cc -Iexport/include -c build/hello/hello.c -o build/hello/hello.o
+ cc -Iexport/include -c build/world/world.c -o build/world/world.o
+ ar r build/world/libworld.a build/world/world.o
+ ar: creating build/world/libworld.a
+ ranlib build/world/libworld.a
+ Install build/world/libworld.a as export/lib/libworld.a
+ cc -o build/hello/hello build/hello/hello.o -Lexport/lib -lworld
+ Install build/hello/hello as export/bin/hello
+
+Again, Cons has taken care of the details for you. In particular, you will
+notice that all the builds are done using source files and object files from
+the build directory. For example, F<build/world/world.o> is compiled from
+F<build/world/world.c>, and F<export/include/world.h> is installed from
+F<build/world/world.h>. This is accomplished on most systems by the simple
+expedient of ``hard'' linking the required files from each source directory
+into the appropriate build directory.
+
+The links are maintained correctly by Cons, no matter what you do to the
+source directory. If you modify a source file, your editor may do this ``in
+place'' or it may rename it first and create a new file. In the latter case,
+any hard link will be lost. Cons will detect this condition the next time
+the source file is needed, and will relink it appropriately.
+
+You'll also notice, by the way, that B<no> changes were required to the
+underlying F<Conscript> files. And we can go further, as we shall see in the
+next section.
+
+=head2 Explicit references to the source directory
+
+When using the C<Link> command on some operating systems or with some
+tool chains, it's sometimes useful to have a command actually use
+the path name to the source directory, not the build directory. For
+example, on systems that must copy, not "hard link," the F<src/> and
+F<build/> copies of C<Linked> files, using the F<src/> path of a file
+name might make an editor aware that a syntax error must be fixed in the
+source directory, not the build directory.
+
+You can tell Cons that you want to use the "source path" for a file by
+preceding the file name with a ``!'' (exclamation point). For example,
+if we add a ``!'' to the beginning of a source file:
+
+ Program $env "foo", "!foo.c"; # Notice initial ! on foo.c
+
+Cons will compile the target as follows:
+
+ cc -c src/foo.c -o build/foo.o
+ cc -o build/foo build/foo.o
+
+Notice that Cons has compiled the program from the the F<src/foo.c>
+source file. Without the initial ``!'', Cons would have compiled the
+program using the F<build/foo.c> path name.
+
+-->
+
+ <para>
+
+ 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
+ <emphasis>variant directory trees</emphasis>
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Specifying a Variant Directory Tree as Part of an &SConscript; Call</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ SConscript('src/SConscript', variant_dir='build')
+ </file>
+ <file name="src/SConscript">
+ env = Environment()
+ env.Program('hello.c')
+ </file>
+ <file name="src/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ &SCons; will then build all of the files in
+ the &build; subdirectory:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>ls src</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>ls build</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ But wait a minute--what's going on here?
+ &SCons; created the object file
+ <filename>build/hello.o</filename>
+ in the &build; subdirectory,
+ as expected.
+ But even though our &hello_c; file lives in the &src; subdirectory,
+ &SCons; has actually compiled a
+ <filename>build/hello.c</filename> file
+ to create the object file.
+
+ </para>
+
+ <para>
+
+ What's happened is that &SCons; has <emphasis>duplicated</emphasis>
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Why &SCons; Duplicates Source Files in a Variant Directory Tree</title>
+
+ <para>
+
+ &SCons; duplicates source files in variant directory trees
+ because it's the most straightforward way to guarantee a correct build
+ <emphasis>regardless of include-file directory paths,
+ relative references between files,
+ or tool support for putting files in different locations</emphasis>,
+ and the &SCons; philosophy is to, by default,
+ guarantee a correct build in all cases.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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 <literal>#include</literal>
+ mechanism with double quotes, not angle brackets:
+
+ </para>
+
+ <sconstruct>
+ #include "file.h"
+ </sconstruct>
+
+ <para>
+
+ The <emphasis>de facto</emphasis> standard behavior
+ for most C compilers in this case
+ is to first look in the same directory
+ as the source file that contains the <literal>#include</literal> 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.
+
+ </para>
+
+ <para>
+
+ Although source-file duplication guarantees a correct build
+ even in these end-cases,
+ it <emphasis>can</emphasis> usually be safely disabled.
+ The next section describes
+ how you can disable the duplication of source files
+ in the variant directory.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree</title>
+
+ <para>
+
+ In most cases and with most tool sets,
+ &SCons; can place its target files in a build subdirectory
+ <emphasis>without</emphasis>
+ duplicating the source files
+ and everything will work just fine.
+ You can disable the default &SCons; behavior
+ by specifying <literal>duplicate=0</literal>
+ when you call the &SConscript; function:
+
+ </para>
+
+ <sconstruct>
+ SConscript('src/SConscript', variant_dir='build', duplicate=0)
+ </sconstruct>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ SConscript
+ hello.c
+ % <userinput>scons -Q</userinput>
+ cc -c src/hello.c -o build/hello.o
+ cc -o build/hello build/hello.o
+ % <userinput>ls build</userinput>
+ hello
+ hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &VariantDir; Function</title>
+
+ <para>
+
+ Use the &VariantDir; function to establish that target
+ files should be built in a separate directory
+ from the source files:
+
+ </para>
+
+ <scons_example name="ex_builddir">
+ <file name="SConstruct" printme="1">
+ VariantDir('build', 'src')
+ env = Environment()
+ env.Program('build/hello.c')
+ </file>
+ <file name="src/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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 <filename>build/hello.c</filename>
+ file that &SCons; will duplicate in the
+ &build; subdirectory.
+
+ </para>
+
+ <para>
+
+ When using the &VariantDir; function directly,
+ &SCons; still duplicates the source files
+ in the variant directory by default:
+
+ </para>
+
+ <scons_output example="ex_builddir">
+ <scons_output_command>ls src</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>ls build</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can specify the same <literal>duplicate=0</literal> argument
+ that you can specify for an &SConscript; call:
+
+ </para>
+
+ <scons_example name="ex_duplicate_0">
+ <file name="SConstruct" printme="1">
+ VariantDir('build', 'src', duplicate=0)
+ env = Environment()
+ env.Program('build/hello.c')
+ </file>
+ <file name="src/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ In which case &SCons;
+ will disable duplication of the source files:
+
+ </para>
+
+ <scons_output example="ex_duplicate_0">
+ <scons_output_command>ls src</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>ls build</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Using &VariantDir; With an &SConscript; File</title>
+
+ <para>
+
+ Even when using the &VariantDir; function,
+ it's much more natural to use it with
+ a subsidiary &SConscript; file.
+ For example, if the
+ <filename>src/SConscript</filename>
+ looks like this:
+
+ </para>
+
+ <scons_example name="example_builddir_sconscript">
+ <file name="SConstruct">
+ VariantDir('build', 'src')
+ SConscript('build/SConscript')
+ </file>
+ <file name="src/SConscript" printme="1">
+ env = Environment()
+ env.Program('hello.c')
+ </file>
+ <file name="src/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then our &SConstruct; file could look like:
+
+ </para>
+
+ <scons_example_file example="example_builddir_sconscript" name="SConstruct">
+ </scons_example_file>
+
+ <para>
+
+ Yielding the following output:
+
+ </para>
+
+ <scons_output example="example_builddir_sconscript">
+ <scons_output_command>ls src</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>ls build</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice that this is completely equivalent
+ to the use of &SConscript; that we
+ learned about in the previous section.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Using &Glob; with &VariantDir;</title>
+
+ <para>
+
+ The &Glob; file name pattern matching function
+ works just as usual when using &VariantDir;.
+ For example, if the
+ <filename>src/SConscript</filename>
+ looks like this:
+
+ </para>
+
+ <scons_example name="example_glob_builddir_sconscript">
+ <file name="SConstruct">
+ VariantDir('build', 'src')
+ SConscript('build/SConscript')
+ </file>
+ <file name="src/SConscript" printme="1">
+ env = Environment()
+ env.Program('hello', Glob('*.c'))
+ </file>
+ <file name="src/f1.c">
+ #include "f2.h"
+ int main() { printf(f2()); }
+ </file>
+ <file name="src/f2.c">
+ const char * f2() { return("Hello, world!\n"); }
+ </file>
+ <file name="src/f2.h">
+ const char * f2();
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then with the same &SConstruct; file as in the previous section,
+ and source files <filename>f1.c</filename>
+ and <filename>f2.c</filename> in src,
+ we would see the following output:
+
+ </para>
+
+ <scons_output example="example_glob_builddir_sconscript">
+ <scons_output_command>ls src</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>ls build</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The &Glob; function returns Nodes in the
+ <filename>build/</filename> tree, as you'd expect.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Why You'd Want to Call &VariantDir; Instead of &SConscript;</title>
+
+ <para>
+
+ XXX why call VariantDir() instead of SConscript(variant_dir=)
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Separating source and build trees
+
+It's often desirable to keep any derived files from the build completely
+separate from the source files. This makes it much easier to keep track of
+just what is a source file, and also makes it simpler to handle B<variant>
+builds, especially if you want the variant builds to co-exist.
+
+=head2 Separating build and source directories using the Link command
+
+Cons provides a simple mechanism that handles all of these requirements. The
+C<Link> command is invoked as in this example:
+
+ Link 'build' => 'src';
+
+The specified directories are ``linked'' to the specified source
+directory. Let's suppose that you setup a source directory, F<src>, with the
+sub-directories F<world> and F<hello> below it, as in the previous
+example. You could then substitute for the original build lines the
+following:
+
+ Build qw(
+ build/world/Conscript
+ build/hello/Conscript
+ );
+
+Notice that you treat the F<Conscript> file as if it existed in the build
+directory. Now if you type the same command as before, you will get the
+following results:
+
+ % cons export
+ Install build/world/world.h as export/include/world.h
+ cc -Iexport/include -c build/hello/hello.c -o build/hello/hello.o
+ cc -Iexport/include -c build/world/world.c -o build/world/world.o
+ ar r build/world/libworld.a build/world/world.o
+ ar: creating build/world/libworld.a
+ ranlib build/world/libworld.a
+ Install build/world/libworld.a as export/lib/libworld.a
+ cc -o build/hello/hello build/hello/hello.o -Lexport/lib -lworld
+ Install build/hello/hello as export/bin/hello
+
+Again, Cons has taken care of the details for you. In particular, you will
+notice that all the builds are done using source files and object files from
+the build directory. For example, F<build/world/world.o> is compiled from
+F<build/world/world.c>, and F<export/include/world.h> is installed from
+F<build/world/world.h>. This is accomplished on most systems by the simple
+expedient of ``hard'' linking the required files from each source directory
+into the appropriate build directory.
+
+The links are maintained correctly by Cons, no matter what you do to the
+source directory. If you modify a source file, your editor may do this ``in
+place'' or it may rename it first and create a new file. In the latter case,
+any hard link will be lost. Cons will detect this condition the next time
+the source file is needed, and will relink it appropriately.
+
+You'll also notice, by the way, that B<no> changes were required to the
+underlying F<Conscript> files. And we can go further, as we shall see in the
+next section.
+
+=head2 Explicit references to the source directory
+
+When using the C<Link> command on some operating systems or with some
+tool chains, it's sometimes useful to have a command actually use
+the path name to the source directory, not the build directory. For
+example, on systems that must copy, not "hard link," the F<src/> and
+F<build/> copies of C<Linked> files, using the F<src/> path of a file
+name might make an editor aware that a syntax error must be fixed in the
+source directory, not the build directory.
+
+You can tell Cons that you want to use the "source path" for a file by
+preceding the file name with a ``!'' (exclamation point). For example,
+if we add a ``!'' to the beginning of a source file:
+
+ Program $env "foo", "!foo.c"; # Notice initial ! on foo.c
+
+Cons will compile the target as follows:
+
+ cc -c src/foo.c -o build/foo.o
+ cc -o build/foo build/foo.o
+
+Notice that Cons has compiled the program from the the F<src/foo.c>
+source file. Without the initial ``!'', Cons would have compiled the
+program using the F<build/foo.c> path name.
+
+-->
+
+ <para>
+
+ 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
+ <emphasis>variant directory trees</emphasis>
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Specifying a Variant Directory Tree as Part of an &SConscript; Call</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ SConscript('src/SConscript', variant_dir='build')
+ </programlisting>
+
+ <para>
+
+ &SCons; will then build all of the files in
+ the &build; subdirectory:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ SConscript hello.c
+ % <userinput>scons -Q</userinput>
+ cc -o build/hello.o -c build/hello.c
+ cc -o build/hello build/hello.o
+ % <userinput>ls build</userinput>
+ SConscript hello hello.c hello.o
+ </screen>
+
+ <para>
+
+ But wait a minute--what's going on here?
+ &SCons; created the object file
+ <filename>build/hello.o</filename>
+ in the &build; subdirectory,
+ as expected.
+ But even though our &hello_c; file lives in the &src; subdirectory,
+ &SCons; has actually compiled a
+ <filename>build/hello.c</filename> file
+ to create the object file.
+
+ </para>
+
+ <para>
+
+ What's happened is that &SCons; has <emphasis>duplicated</emphasis>
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Why &SCons; Duplicates Source Files in a Variant Directory Tree</title>
+
+ <para>
+
+ &SCons; duplicates source files in variant directory trees
+ because it's the most straightforward way to guarantee a correct build
+ <emphasis>regardless of include-file directory paths,
+ relative references between files,
+ or tool support for putting files in different locations</emphasis>,
+ and the &SCons; philosophy is to, by default,
+ guarantee a correct build in all cases.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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 <literal>#include</literal>
+ mechanism with double quotes, not angle brackets:
+
+ </para>
+
+ <programlisting>
+ #include "file.h"
+ </programlisting>
+
+ <para>
+
+ The <emphasis>de facto</emphasis> standard behavior
+ for most C compilers in this case
+ is to first look in the same directory
+ as the source file that contains the <literal>#include</literal> 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.
+
+ </para>
+
+ <para>
+
+ Although source-file duplication guarantees a correct build
+ even in these end-cases,
+ it <emphasis>can</emphasis> usually be safely disabled.
+ The next section describes
+ how you can disable the duplication of source files
+ in the variant directory.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree</title>
+
+ <para>
+
+ In most cases and with most tool sets,
+ &SCons; can place its target files in a build subdirectory
+ <emphasis>without</emphasis>
+ duplicating the source files
+ and everything will work just fine.
+ You can disable the default &SCons; behavior
+ by specifying <literal>duplicate=0</literal>
+ when you call the &SConscript; function:
+
+ </para>
+
+ <programlisting>
+ SConscript('src/SConscript', variant_dir='build', duplicate=0)
+ </programlisting>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ SConscript
+ hello.c
+ % <userinput>scons -Q</userinput>
+ cc -c src/hello.c -o build/hello.o
+ cc -o build/hello build/hello.o
+ % <userinput>ls build</userinput>
+ hello
+ hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>The &VariantDir; Function</title>
+
+ <para>
+
+ Use the &VariantDir; function to establish that target
+ files should be built in a separate directory
+ from the source files:
+
+ </para>
+
+ <programlisting>
+ VariantDir('build', 'src')
+ env = Environment()
+ env.Program('build/hello.c')
+ </programlisting>
+
+ <para>
+
+ 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 <filename>build/hello.c</filename>
+ file that &SCons; will duplicate in the
+ &build; subdirectory.
+
+ </para>
+
+ <para>
+
+ When using the &VariantDir; function directly,
+ &SCons; still duplicates the source files
+ in the variant directory by default:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ hello.c
+ % <userinput>scons -Q</userinput>
+ cc -o build/hello.o -c build/hello.c
+ cc -o build/hello build/hello.o
+ % <userinput>ls build</userinput>
+ hello hello.c hello.o
+ </screen>
+
+ <para>
+
+ You can specify the same <literal>duplicate=0</literal> argument
+ that you can specify for an &SConscript; call:
+
+ </para>
+
+ <programlisting>
+ VariantDir('build', 'src', duplicate=0)
+ env = Environment()
+ env.Program('build/hello.c')
+ </programlisting>
+
+ <para>
+
+ In which case &SCons;
+ will disable duplication of the source files:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ hello.c
+ % <userinput>scons -Q</userinput>
+ cc -o build/hello.o -c src/hello.c
+ cc -o build/hello build/hello.o
+ % <userinput>ls build</userinput>
+ hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Using &VariantDir; With an &SConscript; File</title>
+
+ <para>
+
+ Even when using the &VariantDir; function,
+ it's much more natural to use it with
+ a subsidiary &SConscript; file.
+ For example, if the
+ <filename>src/SConscript</filename>
+ looks like this:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Program('hello.c')
+ </programlisting>
+
+ <para>
+
+ Then our &SConstruct; file could look like:
+
+ </para>
+
+
+ <programlisting>
+ VariantDir('build', 'src')
+ SConscript('build/SConscript')
+ </programlisting>
+
+ <para>
+
+ Yielding the following output:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ SConscript hello.c
+ % <userinput>scons -Q</userinput>
+ cc -o build/hello.o -c build/hello.c
+ cc -o build/hello build/hello.o
+ % <userinput>ls build</userinput>
+ SConscript hello hello.c hello.o
+ </screen>
+
+ <para>
+
+ Notice that this is completely equivalent
+ to the use of &SConscript; that we
+ learned about in the previous section.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Using &Glob; with &VariantDir;</title>
+
+ <para>
+
+ The &Glob; file name pattern matching function
+ works just as usual when using &VariantDir;.
+ For example, if the
+ <filename>src/SConscript</filename>
+ looks like this:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.Program('hello', Glob('*.c'))
+ </programlisting>
+
+ <para>
+
+ Then with the same &SConstruct; file as in the previous section,
+ and source files <filename>f1.c</filename>
+ and <filename>f2.c</filename> in src,
+ we would see the following output:
+
+ </para>
+
+ <screen>
+ % <userinput>ls src</userinput>
+ SConscript f1.c f2.c f2.h
+ % <userinput>scons -Q</userinput>
+ 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
+ % <userinput>ls build</userinput>
+ SConscript f1.c f1.o f2.c f2.h f2.o hello
+ </screen>
+
+ <para>
+
+ The &Glob; function returns Nodes in the
+ <filename>build/</filename> tree, as you'd expect.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Why You'd Want to Call &VariantDir; Instead of &SConscript;</title>
+
+ <para>
+
+ XXX why call VariantDir() instead of SConscript(variant_dir=)
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <!--
+
+ <para>
+
+ If &SCons; is unaware that a build step produces an extra file,
+ the &SideEffect; method can be used to identify it,
+ so that the file can be used as a dependency in subsequent build steps.
+ However, the primary use for the &SideEffect; method
+ is to prevent two build steps from simultaneously modifying the same file.
+
+ </para>
+
+ TODO: currently doesn't work due to issue #2154:
+ http://scons.tigris.org/issues/show_bug.cgi?id=2154
+
+ <para>
+
+ If more than one build step creates or manipulates the same file,
+ it can cause unpleasant results if both build steps are run at the same time.
+ The shared file is declared as a side-effect of building the primary targets
+ and &SCons; will prevent the two build steps from running in parallel.
+
+ </para>
+
+ <para>
+
+ In this example, the <filename>SConscript</filename> uses
+ &SideEffect; to inform &SCons; about the additional output file.
+
+ </para>
+
+ <scons_example name="SideEffectSimple">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ f2 = env.Command('file2', 'log', Copy('$TARGET', '$SOURCE'))
+ f1 = env.Command('file1', [],
+ 'echo >$TARGET data1; echo >log updated file1'))
+ env.SideEffect('log', env.Command('file1', [],
+ 'echo >$TARGET data1; echo >log updated file1'))
+ </file>
+ </scons_example>
+
+ <para>
+
+ Even when run in parallel mode, &SCons; will run the two steps in order:
+
+ </para>
+
+ <scons_output example="SideEffectSimple">
+ <scons_output_command>scons -Q --jobs=2</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <para>
+
+ 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 <application>build</application>
+ (in the local directory)
+ with command-line arguments that write
+ log information to a common
+ <filename>logfile.txt</filename> file:
+
+ </para>
+
+ <screen>
+ 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')
+ <screen>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ We can make sure that &SCons; does not
+ run these <application>build</application>
+ commands at the same time
+ by using the &SideEffect; function
+ to specify that updating
+ the <filename>logfile.txt</filename> file
+ is a side effect of building the specified
+ <filename>file1</filename>
+ and
+ <filename>file2</filename>
+ target files:
+
+ </para>
+
+ <scons_example name="SideEffectShared">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ <file name="file1.in">file1.in</file>
+ <file name="file2.in">file2.in</file>
+ <file name="build" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <para>
+
+ </para>
+
+ <para>
+
+ This makes sure the the two
+ <application>./build</application> steps are run sequentially,
+ even withthe <filename>--jobs=2</filename> in the command line:
+
+ </para>
+
+ <scons_output example="SideEffectShared">
+ <scons_output_command>scons -Q --jobs=2</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="SideEffectParallel">
+ <file name="SConstruct" printme="1">
+ 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)
+ </file>
+ </scons_example>
+
+ <scons_output example="SideEffectParallel">
+ <scons_output_command>scons -Q --jobs=2</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <filename>foo.ilk</filename>
+ alongside compiling <filename>foo.obj</filename> file.
+ Specifying <filename>foo.ilk</filename> as a
+ side-effect of <filename>foo.obj</filename>
+ is <emphasis>not</emphasis> 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.
+
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <!--
+
+ <para>
+
+ If &SCons; is unaware that a build step produces an extra file,
+ the &SideEffect; method can be used to identify it,
+ so that the file can be used as a dependency in subsequent build steps.
+ However, the primary use for the &SideEffect; method
+ is to prevent two build steps from simultaneously modifying the same file.
+
+ </para>
+
+ TODO: currently doesn't work due to issue #2154:
+ http://scons.tigris.org/issues/show_bug.cgi?id=2154
+
+ <para>
+
+ If more than one build step creates or manipulates the same file,
+ it can cause unpleasant results if both build steps are run at the same time.
+ The shared file is declared as a side-effect of building the primary targets
+ and &SCons; will prevent the two build steps from running in parallel.
+
+ </para>
+
+ <para>
+
+ In this example, the <filename>SConscript</filename> uses
+ &SideEffect; to inform &SCons; about the additional output file.
+
+ </para>
+
+ <scons_example name="SideEffectSimple">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ f2 = env.Command('file2', 'log', Copy('$TARGET', '$SOURCE'))
+ f1 = env.Command('file1', [],
+ 'echo >$TARGET data1; echo >log updated file1'))
+ env.SideEffect('log', env.Command('file1', [],
+ 'echo >$TARGET data1; echo >log updated file1'))
+ </file>
+ </scons_example>
+
+ <para>
+
+ Even when run in parallel mode, &SCons; will run the two steps in order:
+
+ </para>
+
+ <scons_output example="SideEffectSimple">
+ <scons_output_command>scons -Q --jobs=2</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <para>
+
+ 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 <application>build</application>
+ (in the local directory)
+ with command-line arguments that write
+ log information to a common
+ <filename>logfile.txt</filename> file:
+
+ </para>
+
+ <screen>
+ 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')
+ <screen>
+
+ <para>
+
+ 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).
+
+ </para>
+
+ <para>
+
+ We can make sure that &SCons; does not
+ run these <application>build</application>
+ commands at the same time
+ by using the &SideEffect; function
+ to specify that updating
+ the <filename>logfile.txt</filename> file
+ is a side effect of building the specified
+ <filename>file1</filename>
+ and
+ <filename>file2</filename>
+ target files:
+
+ </para>
+
+ <programlisting>
+ 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)
+ </programlisting>
+
+ <para>
+
+ </para>
+
+ <para>
+
+ This makes sure the the two
+ <application>./build</application> steps are run sequentially,
+ even withthe <filename>--jobs=2</filename> in the command line:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --jobs=2</userinput>
+ ./build --log logfile.txt file1.in file1.out
+ ./build --log logfile.txt file2.in file2.out
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ f1 = env.Command('file1.out', [], 'echo &gt;$TARGET data1')
+ env.SideEffect('not_really_updated', f1)
+ f2 = env.Command('file2.out', [], 'echo &gt;$TARGET data2')
+ env.SideEffect('not_really_updated', f2)
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q --jobs=2</userinput>
+ echo &gt; file1.out data1
+ echo &gt; file2.out data2
+ </screen>
+
+ <para>
+
+ 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 <filename>foo.ilk</filename>
+ alongside compiling <filename>foo.obj</filename> file.
+ Specifying <filename>foo.ilk</filename> as a
+ side-effect of <filename>foo.obj</filename>
+ is <emphasis>not</emphasis> 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.
+
+ </para>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Building Simple C / C++ Programs</title>
+
+ <para>
+
+ Here's the famous "Hello, World!" program in C:
+
+ </para>
+
+ <programlisting>
+ int
+ main()
+ {
+ printf("Hello, world!\n");
+ }
+ </programlisting>
+
+ <para>
+
+ And here's how to build it using &SCons;.
+ Enter the following into a file named &SConstruct;:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct" printme="1">
+ Program('hello.c')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ 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 <filename>hello.c</filename> file).
+ &b-link-Program; is a <firstterm>builder_method</firstterm>,
+ a Python call that tells &SCons; that you want to build an
+ executable program.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="ex1" os="posix">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ On a Windows system with the Microsoft Visual C++ compiler,
+ you'll see something like:
+
+ </para>
+
+ <scons_output example="ex1" os="win32">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ Second, notice that the same input &SConstruct; file,
+ without any changes,
+ generates the correct output file names on both systems:
+ <filename>hello.o</filename> and <filename>hello</filename>
+ on POSIX systems,
+ <filename>hello.obj</filename> and <filename>hello.exe</filename>
+ on Windows systems.
+ This is a simple example of how &SCons;
+ makes it extremely easy to
+ write portable software builds.
+
+ </para>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Object Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Object">
+ <file name="SConstruct" printme="1">
+ Object('hello.c')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Now when you run the &scons; command to build the program,
+ it will build just the &hello_o; object file on a POSIX system:
+
+ </para>
+
+ <scons_output example="Object" os="posix">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And just the &hello_obj; object file
+ on a Windows system (with the Microsoft Visual C++ compiler):
+
+ </para>
+
+ <scons_output example="Object" os="win32">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Simple Java Builds</title>
+
+ <para>
+
+ &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 <filename>.java</filename> files live:
+
+ </para>
+
+ <scons_example name="java">
+ <file name="SConstruct" printme="1">
+ Java('classes', 'src')
+ </file>
+ <file name="src/hello.java">
+ public class Example1
+ {
+ public static void main(String[] args)
+ {
+ System.out.println("Hello Java world!\n");
+ }
+ }
+ </file>
+ </scons_example>
+
+ <para>
+
+ If the <filename>src</filename> directory
+ contains a single <filename>hello.java</filename> file,
+ then the output from running the &scons; command
+ would look something like this
+ (on a POSIX system):
+
+ </para>
+
+ <scons_output example="java" os="posix">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ We'll cover Java builds in more detail,
+ including building Java archive (<filename>.jar</filename>)
+ and other types of file,
+ in <xref linkend="chap-java"></xref>.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Cleaning Up After a Build</title>
+
+ <para>
+
+ When using &SCons;, it is unnecessary to add special
+ commands or target names to clean up after a build.
+ Instead, you simply use the
+ <literal>-c</literal> or <literal>--clean</literal>
+ option when you invoke &SCons;,
+ and &SCons; removes the appropriate built files.
+ So if we build our example above
+ and then invoke <literal>scons -c</literal>
+ afterwards, the output on POSIX looks like:
+
+ </para>
+
+ <scons_example name="clean">
+ <file name="SConstruct">
+ Program('hello.c')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="clean" os="posix">
+ <scons_output_command>scons</scons_output_command>
+ <scons_output_command>scons -c</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And the output on Windows looks like:
+
+ </para>
+
+ <scons_output example="clean" os="win32">
+ <scons_output_command>scons</scons_output_command>
+ <scons_output_command>scons -c</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice that &SCons; changes its output to tell you that it
+ is <literal>Cleaning targets ...</literal> and
+ <literal>done cleaning targets.</literal>
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &SConstruct; File</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>&SConstruct; Files Are Python Scripts</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ # Arrange to build the "hello" program.
+ Program('hello.c') # "hello.c" is the source file.
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>&SCons; Functions Are Order-Independent</title>
+
+ <para>
+
+ 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 <emphasis>not</emphasis>
+ affect the order in which &SCons;
+ actually builds the programs and object files
+ you want it to build.<footnote>
+ <para>In programming parlance,
+ the &SConstruct; file is
+ <emphasis>declarative</emphasis>,
+ meaning you tell &SCons; what you want done
+ and let it figure out the order in which to do it,
+ rather than strictly <emphasis>imperative</emphasis>,
+ where you specify explicitly the order in
+ which to do things.
+ </para>
+ </footnote>
+ 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 <xref linkend="chap-depends"></xref>, below.)
+
+ </para>
+
+ <para>
+
+ &SCons; reflects this distinction between
+ <emphasis>calling a builder method like</emphasis> &b-Program;
+ and <emphasis>actually building the program</emphasis>
+ 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.
+
+ </para>
+
+ <para>
+
+ Let's clarify this with an example.
+ Python has a <literal>print</literal> statement that
+ prints a string of characters to the screen.
+ If we put <literal>print</literal> statements around
+ our calls to the &b-Program; builder method:
+
+ </para>
+
+ <scons_example name="declarative">
+ <file name="SConstruct" printme="1">
+ print "Calling Program('hello.c')"
+ Program('hello.c')
+ print "Calling Program('goodbye.c')"
+ Program('goodbye.c')
+ print "Finished calling Program()"
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ <file name="goodbye.c">
+ int main() { printf("Goodbye, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then when we execute &SCons;,
+ we see the output from the <literal>print</literal>
+ statements in between the messages about
+ reading the &SConscript; files,
+ indicating that that is when the
+ Python statements are being executed:
+
+ </para>
+
+ <scons_output example="declarative" os="posix">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Notice also that &SCons; built the &goodbye; program first,
+ even though the "reading &SConscript" output
+ shows that we called <literal>Program('hello.c')</literal>
+ first in the &SConstruct; file.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Making the &SCons; Output Less Verbose</title>
+
+ <para>
+
+ You've already seen how &SCons; prints
+ some messages about what it's doing,
+ surrounding the actual commands used to build the software:
+
+ </para>
+
+ <scons_output example="ex1" os="win32">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ One drawback, of course, is that these messages clutter the output.
+ Fortunately, they're easily disabled by using
+ the &Q; option when invoking &SCons;:
+
+ </para>
+
+ <scons_output example="ex1" os="win32">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Building Simple C / C++ Programs</title>
+
+ <para>
+
+ Here's the famous "Hello, World!" program in C:
+
+ </para>
+
+ <programlisting>
+ int
+ main()
+ {
+ printf("Hello, world!\n");
+ }
+ </programlisting>
+
+ <para>
+
+ And here's how to build it using &SCons;.
+ Enter the following into a file named &SConstruct;:
+
+ </para>
+
+ <programlisting>
+ Program('hello.c')
+ </programlisting>
+
+ <para>
+
+ 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 <filename>hello.c</filename> file).
+ &b-link-Program; is a <firstterm>builder_method</firstterm>,
+ a Python call that tells &SCons; that you want to build an
+ executable program.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ On a Windows system with the Microsoft Visual C++ compiler,
+ you'll see something like:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ Second, notice that the same input &SConstruct; file,
+ without any changes,
+ generates the correct output file names on both systems:
+ <filename>hello.o</filename> and <filename>hello</filename>
+ on POSIX systems,
+ <filename>hello.obj</filename> and <filename>hello.exe</filename>
+ on Windows systems.
+ This is a simple example of how &SCons;
+ makes it extremely easy to
+ write portable software builds.
+
+ </para>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Building Object Files</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ Object('hello.c')
+ </programlisting>
+
+ <para>
+
+ Now when you run the &scons; command to build the program,
+ it will build just the &hello_o; object file on a POSIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript files ...
+ scons: done reading SConscript files.
+ scons: Building targets ...
+ cc -o hello.o -c hello.c
+ scons: done building targets.
+ </screen>
+
+ <para>
+
+ And just the &hello_obj; object file
+ on a Windows system (with the Microsoft Visual C++ compiler):
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ scons: done reading SConscript files.
+ scons: Building targets ...
+ cl /Fohello.obj /c hello.c /nologo
+ scons: done building targets.
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Simple Java Builds</title>
+
+ <para>
+
+ &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 <filename>.java</filename> files live:
+
+ </para>
+
+ <programlisting>
+ Java('classes', 'src')
+ </programlisting>
+
+ <para>
+
+ If the <filename>src</filename> directory
+ contains a single <filename>hello.java</filename> file,
+ then the output from running the &scons; command
+ would look something like this
+ (on a POSIX system):
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript files ...
+ scons: done reading SConscript files.
+ scons: Building targets ...
+ javac -d classes -sourcepath src src/hello.java
+ scons: done building targets.
+ </screen>
+
+ <para>
+
+ We'll cover Java builds in more detail,
+ including building Java archive (<filename>.jar</filename>)
+ and other types of file,
+ in <xref linkend="chap-java"></xref>.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Cleaning Up After a Build</title>
+
+ <para>
+
+ When using &SCons;, it is unnecessary to add special
+ commands or target names to clean up after a build.
+ Instead, you simply use the
+ <literal>-c</literal> or <literal>--clean</literal>
+ option when you invoke &SCons;,
+ and &SCons; removes the appropriate built files.
+ So if we build our example above
+ and then invoke <literal>scons -c</literal>
+ afterwards, the output on POSIX looks like:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ % <userinput>scons -c</userinput>
+ scons: Reading SConscript files ...
+ scons: done reading SConscript files.
+ scons: Cleaning targets ...
+ Removed hello.o
+ Removed hello
+ scons: done cleaning targets.
+ </screen>
+
+ <para>
+
+ And the output on Windows looks like:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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:\><userinput>scons -c</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ scons: done reading SConscript files.
+ scons: Cleaning targets ...
+ Removed hello.obj
+ Removed hello.exe
+ scons: done cleaning targets.
+ </screen>
+
+ <para>
+
+ Notice that &SCons; changes its output to tell you that it
+ is <literal>Cleaning targets ...</literal> and
+ <literal>done cleaning targets.</literal>
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>The &SConstruct; File</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>&SConstruct; Files Are Python Scripts</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ # Arrange to build the "hello" program.
+ Program('hello.c') # "hello.c" is the source file.
+ </programlisting>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>&SCons; Functions Are Order-Independent</title>
+
+ <para>
+
+ 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 <emphasis>not</emphasis>
+ affect the order in which &SCons;
+ actually builds the programs and object files
+ you want it to build.<footnote>
+ <para>In programming parlance,
+ the &SConstruct; file is
+ <emphasis>declarative</emphasis>,
+ meaning you tell &SCons; what you want done
+ and let it figure out the order in which to do it,
+ rather than strictly <emphasis>imperative</emphasis>,
+ where you specify explicitly the order in
+ which to do things.
+ </para>
+ </footnote>
+ 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 <xref linkend="chap-depends"></xref>, below.)
+
+ </para>
+
+ <para>
+
+ &SCons; reflects this distinction between
+ <emphasis>calling a builder method like</emphasis> &b-Program;
+ and <emphasis>actually building the program</emphasis>
+ 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.
+
+ </para>
+
+ <para>
+
+ Let's clarify this with an example.
+ Python has a <literal>print</literal> statement that
+ prints a string of characters to the screen.
+ If we put <literal>print</literal> statements around
+ our calls to the &b-Program; builder method:
+
+ </para>
+
+ <programlisting>
+ print "Calling Program('hello.c')"
+ Program('hello.c')
+ print "Calling Program('goodbye.c')"
+ Program('goodbye.c')
+ print "Finished calling Program()"
+ </programlisting>
+
+ <para>
+
+ Then when we execute &SCons;,
+ we see the output from the <literal>print</literal>
+ statements in between the messages about
+ reading the &SConscript; files,
+ indicating that that is when the
+ Python statements are being executed:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ Notice also that &SCons; built the &goodbye; program first,
+ even though the "reading &SConscript;" output
+ shows that we called <literal>Program('hello.c')</literal>
+ first in the &SConstruct; file.
+
+ </para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Making the &SCons; Output Less Verbose</title>
+
+ <para>
+
+ You've already seen how &SCons; prints
+ some messages about what it's doing,
+ surrounding the actual commands used to build the software:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ One drawback, of course, is that these messages clutter the output.
+ Fortunately, they're easily disabled by using
+ the &Q; option when invoking &SCons;:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ cl /Fohello.obj /c hello.c /nologo
+ link /nologo /OUT:hello.exe hello.obj
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ </section>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Fetching Source Code From BitKeeper</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <scons_example name="ex_bitkeeper">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.SourceCode('.', env.BitKeeper())
+ env.Program('hello.c')
+ </file>
+ <file name="s.hello.c">
+ s.hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_bitkeeper">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Fetching Source Code From CVS</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <scons_example name="ex_cvs">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.SourceCode('.', env.CVS('/usr/local/CVS'))
+ env.Program('hello.c')
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_cvs">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Fetching Source Code From RCS</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <scons_example name="ex_rcs">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.SourceCode('.', env.RCS())
+ env.Program('hello.c')
+ </file>
+ <file name="hello.c,v">
+ hello.c,v
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_rcs">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Fetching Source Code From SCCS</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <scons_example name="ex_sccs">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.SourceCode('.', env.SCCS())
+ env.Program('hello.c')
+ </file>
+ <file name="s.hello.c">
+ s.hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_sccs">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Fetching Source Code From Subversion</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <scons_example name="ex_subversion">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.SourceCode('.', env.Subversion('XXX'))
+ env.Program('hello.c')
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_subversion">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Fetching Source Code From BitKeeper</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.SourceCode('.', env.BitKeeper())
+ env.Program('hello.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ bk get hello.c
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Fetching Source Code From CVS</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.SourceCode('.', env.CVS('/usr/local/CVS'))
+ env.Program('hello.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cvs -d /usr/local/CVS co hello.c
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Fetching Source Code From RCS</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.SourceCode('.', env.RCS())
+ env.Program('hello.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ co hello.c
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Fetching Source Code From SCCS</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ env.SourceCode('.', env.SCCS())
+ env.Program('hello.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ sccs get hello.c
+ cc -o hello.o -c hello.c
+ cc -o hello hello.o
+ </screen>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>Fetching Source Code From Subversion</title>
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <scons_example name="ex_subversion">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ env.SourceCode('.', env.Subversion('XXX'))
+ env.Program('hello.c')
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_subversion">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+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;.
+</para>
+
+<example>
+<title>Wildcard globbing to create a list of filenames</title>
+<programlisting>
+files = Glob(wildcard)
+</programlisting>
+</example>
+
+<example>
+<title>Filename extension substitution</title>
+<programlisting>
+import os.path
+filename = os.path.splitext(filename)[0]+extension
+</programlisting>
+</example>
+
+<example>
+<title>Appending a path prefix to a list of filenames</title>
+<programlisting>
+import os.path
+filenames = [os.path.join(prefix, x) for x in filenames]
+</programlisting>
+
+<simpara>or in Python 1.5.2:</simpara>
+
+<programlisting>
+import os.path
+new_filenames = []
+for x in filenames:
+ new_filenames.append(os.path.join(prefix, x))
+</programlisting>
+</example>
+
+<example>
+<title>Substituting a path prefix with another one</title>
+<programlisting>
+if filename.find(old_prefix) == 0:
+ filename = filename.replace(old_prefix, new_prefix)
+</programlisting>
+
+<simpara>or in Python 1.5.2:</simpara>
+
+<programlisting>
+import string
+if string.find(filename, old_prefix) == 0:
+ filename = string.replace(filename, old_prefix, new_prefix)
+</programlisting>
+</example>
+
+<example>
+<title>Filtering a filename list to exclude/retain only a specific set
+of extensions</title>
+<programlisting>
+import os.path
+filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions]
+</programlisting>
+
+<simpara>or in Python 1.5.2:</simpara>
+
+<programlisting>
+import os.path
+new_filenames = []
+for x in filenames:
+ if os.path.splitext(x)[1] in extensions:
+ new_filenames.append(x)
+</programlisting>
+</example>
+
+<example>
+<title>The "backtick function": run a shell command and capture the
+output</title>
+<programlisting>import os
+output = os.popen(command).read()
+</programlisting>
+</example>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+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;.
+</para>
+
+<example>
+<title>Wildcard globbing to create a list of filenames</title>
+<programlisting>
+files = Glob(wildcard)
+</programlisting>
+</example>
+
+<example>
+<title>Filename extension substitution</title>
+<programlisting>
+import os.path
+filename = os.path.splitext(filename)[0]+extension
+</programlisting>
+</example>
+
+<example>
+<title>Appending a path prefix to a list of filenames</title>
+<programlisting>
+import os.path
+filenames = [os.path.join(prefix, x) for x in filenames]
+</programlisting>
+
+<simpara>or in Python 1.5.2:</simpara>
+
+<programlisting>
+import os.path
+new_filenames = []
+for x in filenames:
+ new_filenames.append(os.path.join(prefix, x))
+</programlisting>
+</example>
+
+<example>
+<title>Substituting a path prefix with another one</title>
+<programlisting>
+if filename.find(old_prefix) == 0:
+ filename = filename.replace(old_prefix, new_prefix)
+</programlisting>
+
+<simpara>or in Python 1.5.2:</simpara>
+
+<programlisting>
+import string
+if string.find(filename, old_prefix) == 0:
+ filename = string.replace(filename, old_prefix, new_prefix)
+</programlisting>
+</example>
+
+<example>
+<title>Filtering a filename list to exclude/retain only a specific set
+of extensions</title>
+<programlisting>
+import os.path
+filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions]
+</programlisting>
+
+<simpara>or in Python 1.5.2:</simpara>
+
+<programlisting>
+import os.path
+new_filenames = []
+for x in filenames:
+ if os.path.splitext(x)[1] in extensions:
+ new_filenames.append(x)
+</programlisting>
+</example>
+
+<example>
+<title>The "backtick function": run a shell command and capture the
+output</title>
+<programlisting>import os
+output = os.popen(command).read()
+</programlisting>
+</example>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+
+This appendix contains descriptions of all of the
+Tools modules that are
+available "out of the box" in this version of SCons.
+
+</para>
+
+<variablelist>
+
+&tools-gen;
+
+</variablelist>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+
+This appendix contains descriptions of all of the
+Tools modules that are
+available "out of the box" in this version of SCons.
+
+</para>
+
+<variablelist>
+
+&tools-gen;
+
+</variablelist>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Why is That Target Being Rebuilt? the &debug-explain; Option</title>
+
+ <para>
+
+ Let's look at a simple example of
+ a misconfigured build
+ that causes a target to be rebuilt
+ every time &SCons; is run:
+
+ </para>
+
+ <scons_example name="explain1">
+ <file name="SConstruct" printme="1">
+ # Intentionally misspell the output file name in the
+ # command used to create the file:
+ Command('file.out', 'file.in', 'cp $SOURCE file.oout')
+ </file>
+ <file name="file.in">
+ file.in
+ </file>
+ </scons_example>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ <para>
+
+ Now if we run &SCons; multiple times on this example,
+ we see that it re-runs the &cp;
+ command every time:
+
+ </para>
+
+ <scons_output example="explain1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="explain1" os="posix">
+ <scons_output_command>scons -Q --debug=explain</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="explain2">
+ <file name="SConstruct">
+ Program('prog', ['file1.c', 'file2.c', 'file3.c'])
+ </file>
+ <file name="file1.c">
+ file1.c
+ </file>
+ <file name="file2.c">
+ file2.c
+ </file>
+ <file name="file3.c">
+ file3.c
+ </file>
+ </scons_example>
+
+ <scons_output example="explain2" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF file2.c]">edit file2.c</scons_output_command>
+ <scons_output_command>scons -Q --debug=explain</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ This becomes even more helpful
+ in identifying when a file is rebuilt
+ due to a change in an implicit dependency,
+ such as an incuded <filename>.h</filename> file.
+ If the <filename>file1.c</filename>
+ and <filename>file3.c</filename> 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:
+
+ </para>
+
+ <scons_example name="explain3">
+ <file name="SConstruct">
+ Program('prog', ['file1.c', 'file2.c', 'file3.c'], CPPPATH='.')
+ </file>
+ <file name="file1.c">
+ #include &lt;hello.h&gt;
+ file1.c
+ </file>
+ <file name="file2.c">
+ file2.c
+ </file>
+ <file name="file3.c">
+ #include &lt;hello.h&gt;
+ file3.c
+ </file>
+ <file name="hello.h">
+ #define string "world"
+ </file>
+ </scons_example>
+
+ <scons_output example="explain3" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command output=" [CHANGE THE CONTENTS OF hello.h]">edit hello.h</scons_output_command>
+ <scons_output_command>scons -Q --debug=explain</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ (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 <emphasis>not</emphasis>
+ to rebuild a target file,
+ which is often a more valuable question to answer.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>What's in That Construction Environment? the &Dump; Method</title>
+
+ <para>
+
+ 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
+ <emphasis>returns</emphasis>
+ the representation of the variables
+ in the environment
+ for you to print (or otherwise manipulate):
+
+ </para>
+
+ <scons_example name="Dump">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ print env.Dump()
+ </file>
+ </scons_example>
+
+ <para>
+
+ On a POSIX system with gcc installed,
+ this might generate:
+
+ </para>
+
+ <scons_output example="Dump" os="posix" tools="gcc">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ On a Windows system with Visual C++
+ the output might look like:
+
+ </para>
+
+ <scons_output example="Dump" os="win32" tools="msvc">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="Dump_ENV">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ print env.Dump('ENV')
+ </file>
+ </scons_example>
+
+ <para>
+
+ Which might display the following when executed on a POSIX system:
+
+ </para>
+
+ <scons_output example="Dump_ENV" os="posix">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ And the following when executed on a Windows system:
+
+ </para>
+
+ <scons_output example="Dump_ENV" os="win32">
+ <scons_output_command>scons</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+
+ <title>What Dependencies Does &SCons; Know About? the &tree; Option</title>
+
+ <para>
+
+ 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 <literal>--tree</literal> option
+ will display all or part of the
+ &SCons; dependency graph in an
+ "ASCII art" graphical format
+ that shows the dependency hierarchy.
+
+ </para>
+
+ <para>
+
+ For example, given the following input &SConstruct; file:
+
+ </para>
+
+ <scons_example name="tree1">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('prog', ['f1.c', 'f2.c', 'f3.c'])
+ </file>
+ <file name="f1.c">
+ #include "inc.h"
+ </file>
+ <file name="f2.c">
+ #include "inc.h"
+ </file>
+ <file name="f3.c">
+ #include "inc.h"
+ </file>
+ <file name="inc.h">
+ inc.h
+ </file>
+ </scons_example>
+
+ <para>
+
+ Running &SCons; with the <literal>--tree=all</literal>
+ option yields:
+
+ </para>
+
+ <scons_output example="tree1">
+ <scons_output_command>scons -Q --tree=all</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The tree will also be printed when the
+ <literal>-n</literal> (no execute) option is used,
+ which allows you to examine the dependency graph
+ for a configuration without actually
+ rebuilding anything in the tree.
+
+ </para>
+
+ <para>
+
+ The <literaL>--tree</literal> 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 <filename>f2.o</filename>
+ on the command line,
+ the <literaL>--tree</literal> option will only
+ print the dependency graph for that file:
+
+ </para>
+
+ <scons_output example="tree1">
+ <scons_output_command>scons -Q --tree=all f2.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="tree1">
+ <scons_output_command>scons -Q --tree=all f1.o f3.o</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The <literal>status</literal> argument may be used
+ to tell &SCons; to print status information about
+ each file in the dependency graph:
+
+ </para>
+
+ <scons_output example="tree1">
+ <scons_output_command>scons -Q --tree=status</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that <literal>--tree=all,status</literal> is equivalent;
+ the <literal>all</literal>
+ is assumed if only <literal>status</literal> is present.
+ As an alternative to <literal>all</literal>,
+ you can specify <literal>--tree=derived</literal>
+ to have &SCons; only print derived targets
+ in the tree output,
+ skipping source files
+ (like <filename>.c</filename> and <filename>.h</filename> files):
+
+ </para>
+
+ <scons_output example="tree1">
+ <scons_output_command>scons -Q --tree=derived</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can use the <literal>status</literal>
+ modifier with <literal>derived</literal> as well:
+
+ </para>
+
+ <scons_output example="tree1">
+ <scons_output_command>scons -Q --tree=derived,status</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that the order of the <literal>--tree=</literal>
+ arguments doesn't matter;
+ <literal>--tree=status,derived</literal> is
+ completely equivalent.
+
+ </para>
+
+ <para>
+
+ The default behavior of the <literal>--tree</literal> 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:
+
+ </para>
+
+ <scons_example name="tree2">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['.'],
+ LIBS = ['foo'],
+ LIBPATH = ['.'])
+ env.Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ env.Program('prog1.c')
+ env.Program('prog2.c')
+ </file>
+ <file name="prog1.c">
+ #include "inc.h"
+ </file>
+ <file name="prog2.c">
+ #include "inc.h"
+ </file>
+ <file name="f1.c">
+ #include "inc.h"
+ </file>
+ <file name="f2.c">
+ #include "inc.h"
+ </file>
+ <file name="f3.c">
+ #include "inc.h"
+ </file>
+ <file name="inc.h">
+ inc.h
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then there can be a <emphasis>lot</emphasis> of repetition in the
+ <literal>--tree=</literal> output:
+
+ </para>
+
+ <scons_output example="tree2">
+ <scons_output_command>scons -Q --tree=all</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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 <literal>prune</literal> 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 <literal>[square brackets]</literal>
+ as an indication that the dependencies
+ of the target file may be found
+ by looking farther up the tree:
+
+ </para>
+
+ <scons_output example="tree2">
+ <scons_output_command>scons -Q --tree=prune</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Like the <literal>status</literal> keyword,
+ the <literal>prune</literal> argument by itself
+ is equivalent to <literal>--tree=all,prune</literal>.
+
+ </para>
+
+ </section>
+
+ <section>
+
+ <title>How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_example name="presub">
+ <file name="SConstruct">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('prog', 'prog.c')
+ </file>
+ <file name="prog.c">
+ prog.c
+ </file>
+ </scons_example>
+
+ <!--
+
+ Have to capture output here, otherwise the - -debug=presub output
+ shows the Python functions from the sconsdoc.py execution wrapper
+ used to generate this manual, not the underlying command-line strings.
+
+ <scons_output example="presub">
+ <scons_output_command>scons -Q - -debug=presub</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q --debug=presub</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+
+ <title>Where is &SCons; Searching for Libraries? the &debug-findlibs; Option</title>
+
+ <para>
+
+ To get some insight into what library names
+ &SCons; is searching for,
+ and in which directories it is searching,
+ Use the <literal>--debug=findlibs</literal> option.
+ Given the following input &SConstruct; file:
+
+ </para>
+
+ <scons_example name="findlibs">
+ <file name="SConstruct" printme="1">
+ env = Environment(LIBPATH = ['libs1', 'libs2'])
+ env.Program('prog.c', LIBS=['foo', 'bar'])
+ </file>
+ <file name="prog.c">
+ prog.c
+ </file>
+ <file name="libs1/libfoo.a">
+ libs1/libfoo.a
+ </file>
+ <file name="libs2/libbar.a">
+ libs2/libbar.a
+ </file>
+ </scons_example>
+
+ <para>
+
+ And the libraries <filename>libfoo.a</filename>
+ and <filename>libbar.a</filename>
+ in <filename>libs1</filename> and <filename>libs2</filename>,
+ respectively,
+ use of the <literal>--debug=findlibs</literal> option yields:
+
+ </para>
+
+ <scons_output example="findlibs">
+ <scons_output_command>scons -Q --debug=findlibs</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <!--
+
+ <section>
+
+ <title>What Implicit Dependencies Did the &SCons; Scanner find? the &debug-includes; Option</title>
+
+ <para>
+
+ XXX explain the - - debug=includes option
+
+ </para>
+
+ <scons_example name="includes">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['inc1', 'inc2'])
+ env.Program('prog.c')
+ </file>
+ <file name="prog.c">
+ #include "file1.h"
+ #include "file2.h"
+ prog.c
+ </file>
+ <file name="inc1/file1.h">
+ inc1/file1.h
+ </file>
+ <file name="inc2/file2.h">
+ inc2/file2.h
+ </file>
+ </scons_example>
+
+ <scons_output example="includes">
+ <scons_output_command>scons -Q - - debug=includes prog</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
+
+ <section>
+
+ <title>Where is &SCons; Blowing Up? the &debug-stacktrace; Option</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ For example, the following &SConstruct file:
+
+ </para>
+
+ <scons_example name="stacktrace">
+ <file name="SConstruct" printme="1">
+ Program('prog.c')
+ </file>
+ </scons_example>
+
+ <para>
+
+ Generates the following error if the
+ <filename>prog.c</filename> file
+ does not exist:
+
+ </para>
+
+ <scons_output example="stacktrace">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <scons_output example="stacktrace">
+ <scons_output_command>scons -Q --debug=stacktrace</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ 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...
+
+ </para>
+
+ </section>
+
+ <section>
+
+ <title>How is &SCons; Making Its Decisions? the &taskmastertrace; Option</title>
+
+ <para>
+
+ The internal &SCons; subsystem that handles walking
+ the dependency graph
+ and controls the decision-making about what to rebuild
+ is the <literal>Taskmaster</literal>.
+ &SCons; supports a <literal>--taskmastertrace</literal>
+ 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.
+
+ </para>
+
+ <para>
+
+ The <literal>--taskmastertrace</literal> option
+ takes as an argument the name of a file in
+ which to put the trace output,
+ with <filename>-</filename> (a single hyphen)
+ indicating that the trace messages
+ should be printed to the standard output:
+
+ </para>
+
+ <scons_example name="taskmastertrace">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('prog.c')
+ </file>
+ <file name="prog.c">
+ #include "inc.h"
+ prog.c
+ </file>
+ <file name="inc.h">
+ #define STRING "one"
+ </file>
+ </scons_example>
+
+ <scons_output example="taskmastertrace" os="posix">
+ <scons_output_command>scons -Q --taskmastertrace=- prog</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The <literal>--taskmastertrace</literal> 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.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+
+ <title>Where Are My Build Bottlenecks? the &profile; Option</title>
+
+ <para>
+
+ XXX explain the - - profile= option
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+ <title>Troubleshooting Shared Caching: the &cache-debug; Option</title>
+
+ <para>
+
+ XXX describe the - - cache-debug option
+ XXX maybe point to the caching.in chapter?
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <section>
+ <title>Why is That Target Being Rebuilt? the &debug-explain; Option</title>
+
+ <para>
+
+ Let's look at a simple example of
+ a misconfigured build
+ that causes a target to be rebuilt
+ every time &SCons; is run:
+
+ </para>
+
+ <programlisting>
+ # Intentionally misspell the output file name in the
+ # command used to create the file:
+ Command('file.out', 'file.in', 'cp $SOURCE file.oout')
+ </programlisting>
+
+ <para>
+
+ (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.)
+
+ </para>
+
+ <para>
+
+ Now if we run &SCons; multiple times on this example,
+ we see that it re-runs the &cp;
+ command every time:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cp file.in file.oout
+ % <userinput>scons -Q</userinput>
+ cp file.in file.oout
+ % <userinput>scons -Q</userinput>
+ cp file.in file.oout
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --debug=explain</userinput>
+ scons: building `file.out' because it doesn't exist
+ cp file.in file.oout
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ % <userinput>edit file2.c</userinput>
+ [CHANGE THE CONTENTS OF file2.c]
+ % <userinput>scons -Q --debug=explain</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ This becomes even more helpful
+ in identifying when a file is rebuilt
+ due to a change in an implicit dependency,
+ such as an incuded <filename>.h</filename> file.
+ If the <filename>file1.c</filename>
+ and <filename>file3.c</filename> 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:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ 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
+ % <userinput>edit hello.h</userinput>
+ [CHANGE THE CONTENTS OF hello.h]
+ % <userinput>scons -Q --debug=explain</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ (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 <emphasis>not</emphasis>
+ to rebuild a target file,
+ which is often a more valuable question to answer.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>What's in That Construction Environment? the &Dump; Method</title>
+
+ <para>
+
+ 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
+ <emphasis>returns</emphasis>
+ the representation of the variables
+ in the environment
+ for you to print (or otherwise manipulate):
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ print env.Dump()
+ </programlisting>
+
+ <para>
+
+ On a POSIX system with gcc installed,
+ this might generate:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ scons: Reading SConscript files ...
+ { 'BUILDERS': {'_InternalInstall': &lt;function InstallBuilderWrapper at 0x700000&gt;, '_InternalInstallAs': &lt;function InstallAsBuilderWrapper at 0x700000&gt;},
+ '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': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'Dirs': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'},
+ 'ESCAPE': &lt;function escape at 0x700000&gt;,
+ 'File': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'HOST_ARCH': None,
+ 'HOST_OS': None,
+ 'IDLSUFFIXES': ['.idl', '.IDL'],
+ 'INSTALL': &lt;function copyFunc at 0x700000&gt;,
+ 'LIBPREFIX': 'lib',
+ 'LIBPREFIXES': ['$LIBPREFIX'],
+ 'LIBSUFFIX': '.a',
+ 'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'],
+ 'MAXLINELENGTH': 128072,
+ 'OBJPREFIX': '',
+ 'OBJSUFFIX': '.o',
+ 'PLATFORM': 'posix',
+ 'PROGPREFIX': '',
+ 'PROGSUFFIX': '',
+ 'PSPAWN': &lt;function piped_env_spawn at 0x700000&gt;,
+ 'RDirs': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'SCANNERS': [],
+ 'SHELL': 'sh',
+ 'SHLIBPREFIX': '$LIBPREFIX',
+ 'SHLIBSUFFIX': '.so',
+ 'SHOBJPREFIX': '$OBJPREFIX',
+ 'SHOBJSUFFIX': '$OBJSUFFIX',
+ 'SPAWN': &lt;function spawnvpe_spawn at 0x700000&gt;,
+ 'TARGET_ARCH': None,
+ 'TARGET_OS': None,
+ 'TEMPFILE': &lt;class SCons.Platform.TempFileMunge at 0x700000&gt;,
+ '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': &lt;function _concat at 0x700000&gt;,
+ '_defines': &lt;function _defines at 0x700000&gt;,
+ '_stripixes': &lt;function _stripixes at 0x700000&gt;}
+ scons: done reading SConscript files.
+ scons: Building targets ...
+ scons: `.' is up to date.
+ scons: done building targets.
+ </screen>
+
+ <para>
+
+ On a Windows system with Visual C++
+ the output might look like:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ { 'BUILDERS': {'_InternalInstall': &lt;function InstallBuilderWrapper at 0x700000&gt;, 'Object': &lt;SCons.Builder.CompositeBuilder instance at 0x700000&gt;, 'PCH': &lt;SCons.Builder.BuilderBase instance at 0x700000&gt;, 'RES': &lt;SCons.Builder.BuilderBase instance at 0x700000&gt;, 'SharedObject': &lt;SCons.Builder.CompositeBuilder instance at 0x700000&gt;, 'StaticObject': &lt;SCons.Builder.CompositeBuilder instance at 0x700000&gt;, '_InternalInstallAs': &lt;function InstallAsBuilderWrapper at 0x700000&gt;},
+ 'CC': 'cl',
+ 'CCCOM': &lt;SCons.Action.FunctionAction instance at 0x700000&gt;,
+ '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': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'Dirs': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'ENV': { 'PATH': 'C:/WINDOWS\\System32',
+ 'PATHEXT': '.COM;.EXE;.BAT;.CMD',
+ 'SystemRoot': 'C:/WINDOWS'},
+ 'ESCAPE': &lt;function escape at 0x700000&gt;,
+ 'File': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'HOST_ARCH': '',
+ 'HOST_OS': 'win32',
+ 'IDLSUFFIXES': ['.idl', '.IDL'],
+ 'INCPREFIX': '/I',
+ 'INCSUFFIX': '',
+ 'INSTALL': &lt;function copyFunc at 0x700000&gt;,
+ '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': &lt;function piped_spawn at 0x700000&gt;,
+ 'RC': 'rc',
+ 'RCCOM': &lt;SCons.Action.FunctionAction instance at 0x700000&gt;,
+ 'RCFLAGS': [],
+ 'RCSUFFIXES': ['.rc', '.rc2'],
+ 'RDirs': &lt;SCons.Defaults.Variable_Method_Caller instance at 0x700000&gt;,
+ 'SCANNERS': [],
+ 'SHCC': '$CC',
+ 'SHCCCOM': &lt;SCons.Action.FunctionAction instance at 0x700000&gt;,
+ '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': &lt;function spawn at 0x700000&gt;,
+ 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1,
+ 'TARGET_ARCH': '',
+ 'TARGET_OS': 'win32',
+ 'TEMPFILE': &lt;class SCons.Platform.TempFileMunge at 0x700000&gt;,
+ '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': &lt;function msvc_output_flag at 0x700000&gt;,
+ '_concat': &lt;function _concat at 0x700000&gt;,
+ '_defines': &lt;function _defines at 0x700000&gt;,
+ '_stripixes': &lt;function _stripixes at 0x700000&gt;}
+ scons: done reading SConscript files.
+ scons: Building targets ...
+ scons: `.' is up to date.
+ scons: done building targets.
+ </screen>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ print env.Dump('ENV')
+ </programlisting>
+
+ <para>
+
+ Which might display the following when executed on a POSIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons</userinput>
+ 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.
+ </screen>
+
+ <para>
+
+ And the following when executed on a Windows system:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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.
+ </screen>
+
+ </section>
+
+ <section>
+
+ <title>What Dependencies Does &SCons; Know About? the &tree; Option</title>
+
+ <para>
+
+ 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 <literal>--tree</literal> option
+ will display all or part of the
+ &SCons; dependency graph in an
+ "ASCII art" graphical format
+ that shows the dependency hierarchy.
+
+ </para>
+
+ <para>
+
+ For example, given the following input &SConstruct; file:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CPPPATH = ['.'])
+ env.Program('prog', ['f1.c', 'f2.c', 'f3.c'])
+ </programlisting>
+
+ <para>
+
+ Running &SCons; with the <literal>--tree=all</literal>
+ option yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=all</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ The tree will also be printed when the
+ <literal>-n</literal> (no execute) option is used,
+ which allows you to examine the dependency graph
+ for a configuration without actually
+ rebuilding anything in the tree.
+
+ </para>
+
+ <para>
+
+ The <literal>--tree</literal> 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 <filename>f2.o</filename>
+ on the command line,
+ the <literal>--tree</literal> option will only
+ print the dependency graph for that file:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=all f2.o</userinput>
+ cc -o f2.o -c -I. f2.c
+ +-f2.o
+ +-f2.c
+ +-inc.h
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=all f1.o f3.o</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ The <literal>status</literal> argument may be used
+ to tell &SCons; to print status information about
+ each file in the dependency graph:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=status</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Note that <literal>--tree=all,status</literal> is equivalent;
+ the <literal>all</literal>
+ is assumed if only <literal>status</literal> is present.
+ As an alternative to <literal>all</literal>,
+ you can specify <literal>--tree=derived</literal>
+ to have &SCons; only print derived targets
+ in the tree output,
+ skipping source files
+ (like <filename>.c</filename> and <filename>.h</filename> files):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=derived</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ You can use the <literal>status</literal>
+ modifier with <literal>derived</literal> as well:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=derived,status</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ Note that the order of the <literal>--tree=</literal>
+ arguments doesn't matter;
+ <literal>--tree=status,derived</literal> is
+ completely equivalent.
+
+ </para>
+
+ <para>
+
+ The default behavior of the <literal>--tree</literal> 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:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CPPPATH = ['.'],
+ LIBS = ['foo'],
+ LIBPATH = ['.'])
+ env.Library('foo', ['f1.c', 'f2.c', 'f3.c'])
+ env.Program('prog1.c')
+ env.Program('prog2.c')
+ </programlisting>
+
+ <para>
+
+ Then there can be a <emphasis>lot</emphasis> of repetition in the
+ <literal>--tree=</literal> output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=all</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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 <literal>prune</literal> 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 <literal>[square brackets]</literal>
+ as an indication that the dependencies
+ of the target file may be found
+ by looking farther up the tree:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --tree=prune</userinput>
+ 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]
+ </screen>
+
+ <para>
+
+ Like the <literal>status</literal> keyword,
+ the <literal>prune</literal> argument by itself
+ is equivalent to <literal>--tree=all,prune</literal>.
+
+ </para>
+
+ </section>
+
+ <section>
+
+ <title>How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option</title>
+
+ <para>
+
+ 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:
+
+ </para>
+
+
+
+ <!--
+
+ Have to capture output here, otherwise the - -debug=presub output
+ shows the Python functions from the sconsdoc.py execution wrapper
+ used to generate this manual, not the underlying command-line strings.
+
+ <scons_output example="presub">
+ <scons_output_command>scons -Q - -debug=presub</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q --debug=presub</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <section>
+
+ <title>Where is &SCons; Searching for Libraries? the &debug-findlibs; Option</title>
+
+ <para>
+
+ To get some insight into what library names
+ &SCons; is searching for,
+ and in which directories it is searching,
+ Use the <literal>--debug=findlibs</literal> option.
+ Given the following input &SConstruct; file:
+
+ </para>
+
+ <programlisting>
+ env = Environment(LIBPATH = ['libs1', 'libs2'])
+ env.Program('prog.c', LIBS=['foo', 'bar'])
+ </programlisting>
+
+ <para>
+
+ And the libraries <filename>libfoo.a</filename>
+ and <filename>libbar.a</filename>
+ in <filename>libs1</filename> and <filename>libs2</filename>,
+ respectively,
+ use of the <literal>--debug=findlibs</literal> option yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --debug=findlibs</userinput>
+ 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
+ </screen>
+
+ </section>
+
+ <!--
+
+ <section>
+
+ <title>What Implicit Dependencies Did the &SCons; Scanner find? the &debug-includes; Option</title>
+
+ <para>
+
+ XXX explain the - - debug=includes option
+
+ </para>
+
+ <scons_example name="includes">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['inc1', 'inc2'])
+ env.Program('prog.c')
+ </file>
+ <file name="prog.c">
+ #include "file1.h"
+ #include "file2.h"
+ prog.c
+ </file>
+ <file name="inc1/file1.h">
+ inc1/file1.h
+ </file>
+ <file name="inc2/file2.h">
+ inc2/file2.h
+ </file>
+ </scons_example>
+
+ <scons_output example="includes">
+ <scons_output_command>scons -Q - - debug=includes prog</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->
+
+ <section>
+
+ <title>Where is &SCons; Blowing Up? the &debug-stacktrace; Option</title>
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <para>
+
+ For example, the following &SConstruct; file:
+
+ </para>
+
+ <programlisting>
+ Program('prog.c')
+ </programlisting>
+
+ <para>
+
+ Generates the following error if the
+ <filename>prog.c</filename> file
+ does not exist:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'.
+ </screen>
+
+ <para>
+
+ 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:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q --debug=stacktrace</userinput>
+ 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
+ </screen>
+
+ <para>
+
+ 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...
+
+ </para>
+
+ </section>
+
+ <section>
+
+ <title>How is &SCons; Making Its Decisions? the &taskmastertrace; Option</title>
+
+ <para>
+
+ The internal &SCons; subsystem that handles walking
+ the dependency graph
+ and controls the decision-making about what to rebuild
+ is the <literal>Taskmaster</literal>.
+ &SCons; supports a <literal>--taskmastertrace</literal>
+ 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.
+
+ </para>
+
+ <para>
+
+ The <literal>--taskmastertrace</literal> option
+ takes as an argument the name of a file in
+ which to put the trace output,
+ with <filename>-</filename> (a single hyphen)
+ indicating that the trace messages
+ should be printed to the standard output:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CPPPATH = ['.'])
+ env.Program('prog.c')
+ </programlisting>
+
+ <screen>
+ % <userinput>scons -Q --taskmastertrace=- prog</userinput>
+
+ Taskmaster: Looking for a node to evaluate
+ Taskmaster: Considering node &lt;no_state 0 'prog'&gt; and its children:
+ Taskmaster: &lt;no_state 0 'prog.o'&gt;
+ Taskmaster: adjusted ref count: &lt;pending 1 'prog'&gt;, child 'prog.o'
+ Taskmaster: Considering node &lt;no_state 0 'prog.o'&gt; and its children:
+ Taskmaster: &lt;no_state 0 'prog.c'&gt;
+ Taskmaster: &lt;no_state 0 'inc.h'&gt;
+ Taskmaster: adjusted ref count: &lt;pending 1 'prog.o'&gt;, child 'prog.c'
+ Taskmaster: adjusted ref count: &lt;pending 2 'prog.o'&gt;, child 'inc.h'
+ Taskmaster: Considering node &lt;no_state 0 'prog.c'&gt; and its children:
+ Taskmaster: Evaluating &lt;pending 0 'prog.c'&gt;
+
+ Task.make_ready_current(): node &lt;pending 0 'prog.c'&gt;
+ Task.prepare(): node &lt;up_to_date 0 'prog.c'&gt;
+ Task.executed_with_callbacks(): node &lt;up_to_date 0 'prog.c'&gt;
+ Task.postprocess(): node &lt;up_to_date 0 'prog.c'&gt;
+ Task.postprocess(): removing &lt;up_to_date 0 'prog.c'&gt;
+ Task.postprocess(): adjusted parent ref count &lt;pending 1 'prog.o'&gt;
+
+ Taskmaster: Looking for a node to evaluate
+ Taskmaster: Considering node &lt;no_state 0 'inc.h'&gt; and its children:
+ Taskmaster: Evaluating &lt;pending 0 'inc.h'&gt;
+
+ Task.make_ready_current(): node &lt;pending 0 'inc.h'&gt;
+ Task.prepare(): node &lt;up_to_date 0 'inc.h'&gt;
+ Task.executed_with_callbacks(): node &lt;up_to_date 0 'inc.h'&gt;
+ Task.postprocess(): node &lt;up_to_date 0 'inc.h'&gt;
+ Task.postprocess(): removing &lt;up_to_date 0 'inc.h'&gt;
+ Task.postprocess(): adjusted parent ref count &lt;pending 0 'prog.o'&gt;
+
+ Taskmaster: Looking for a node to evaluate
+ Taskmaster: Considering node &lt;pending 0 'prog.o'&gt; and its children:
+ Taskmaster: &lt;up_to_date 0 'prog.c'&gt;
+ Taskmaster: &lt;up_to_date 0 'inc.h'&gt;
+ Taskmaster: Evaluating &lt;pending 0 'prog.o'&gt;
+
+ Task.make_ready_current(): node &lt;pending 0 'prog.o'&gt;
+ Task.prepare(): node &lt;executing 0 'prog.o'&gt;
+ Task.execute(): node &lt;executing 0 'prog.o'&gt;
+ cc -o prog.o -c -I. prog.c
+ Task.executed_with_callbacks(): node &lt;executing 0 'prog.o'&gt;
+ Task.postprocess(): node &lt;executed 0 'prog.o'&gt;
+ Task.postprocess(): removing &lt;executed 0 'prog.o'&gt;
+ Task.postprocess(): adjusted parent ref count &lt;pending 0 'prog'&gt;
+
+ Taskmaster: Looking for a node to evaluate
+ Taskmaster: Considering node &lt;pending 0 'prog'&gt; and its children:
+ Taskmaster: &lt;executed 0 'prog.o'&gt;
+ Taskmaster: Evaluating &lt;pending 0 'prog'&gt;
+
+ Task.make_ready_current(): node &lt;pending 0 'prog'&gt;
+ Task.prepare(): node &lt;executing 0 'prog'&gt;
+ Task.execute(): node &lt;executing 0 'prog'&gt;
+ cc -o prog prog.o
+ Task.executed_with_callbacks(): node &lt;executing 0 'prog'&gt;
+ Task.postprocess(): node &lt;executed 0 'prog'&gt;
+
+ Taskmaster: Looking for a node to evaluate
+ Taskmaster: No candidate anymore.
+ </screen>
+
+ <para>
+
+ The <literal>--taskmastertrace</literal> 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.
+
+ </para>
+
+ </section>
+
+ <!--
+
+ <section>
+
+ <title>Where Are My Build Bottlenecks? the &profile; Option</title>
+
+ <para>
+
+ XXX explain the - - profile= option
+
+ </para>
+
+ </section>
+
+ -->
+
+ <!--
+
+ <section>
+ <title>Troubleshooting Shared Caching: the &cache-debug; Option</title>
+
+ <para>
+
+ XXX describe the - - cache-debug option
+ XXX maybe point to the caching.in chapter?
+
+ </para>
+
+ </section>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+
+This appendix contains descriptions of all of the
+construction variables that are <emphasis>potentially</emphasis>
+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.
+
+</para>
+
+<para>
+
+In this appendix, we have
+appended the initial <envar>$</envar>
+(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.
+
+</para>
+
+<variablelist>
+
+&variables-gen;
+
+</variablelist>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<para>
+
+This appendix contains descriptions of all of the
+construction variables that are <emphasis>potentially</emphasis>
+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.
+
+</para>
+
+<para>
+
+In this appendix, we have
+appended the initial <envar>$</envar>
+(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.
+
+</para>
+
+<variablelist>
+
+&variables-gen;
+
+</variablelist>
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Variant builds
+
+=head2 Variations on a theme
+
+Other variations of this model are possible. For example, you might decide
+that you want to separate out your include files into platform dependent and
+platform independent files. In this case, you'd have to define an
+alternative to C<$INCLUDE> for platform-dependent files. Most F<Conscript>
+files, generating purely platform-independent include files, would not have
+to change.
+
+You might also want to be able to compile your whole system with debugging
+or profiling, for example, enabled. You could do this with appropriate
+command line options, such as C<DEBUG=on>. This would then be translated
+into the appropriate platform-specific requirements to enable debugging
+(this might include turning off optimization, for example). You could
+optionally vary the name space for these different types of systems, but, as
+we'll see in the next section, it's not B<essential> to do this, since Cons
+is pretty smart about rebuilding things when you change options.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <scons_example name="ex_variants">
+ <file name="SConstruct" printme="1">
+ 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')
+ </file>
+ <directory name="src"></directory>
+ <directory name="src/hello"></directory>
+ <directory name="src/world"></directory>
+ <file name="src/SConscript">
+ Import('env')
+ SConscript('hello/SConscript')
+ SConscript('world/SConscript')
+ </file>
+ <file name="src/hello/SConscript">
+ Import('env')
+ hello = env.Program('hello.c')
+ env.Install('$BINDIR', hello)
+ </file>
+ <file name="src/hello/hello.c">
+ #include "world.h"
+ int main(int argc, char *argv[]) { printf "hello.c\n"; world(); }
+ </file>
+ <file name="src/world/SConscript">
+ Import('env')
+ world = env.Library('world.c')
+ env.Install('$LIBDIR', world)
+ env.Install('$INCDIR', 'world.h')
+ </file>
+ <file name="src/world/world.h">
+ #define STRING "world.h"
+ extern int world();
+ </file>
+ <file name="src/world/world.c">
+ int world() { printf "world.c\n"; }
+ </file>
+ </scons_example>
+
+ <para>
+
+ This SConstruct file,
+ when run on a Linux system, yields:
+
+ </para>
+
+ <scons_output example="ex_variants" os="posix">
+ <scons_output_command>scons -Q OS=linux</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The same SConstruct file on Windows would build:
+
+ </para>
+
+ <scons_output example="ex_variants" os="win32">
+ <scons_output_command>scons -Q OS=windows</scons_output_command>
+ </scons_output>
+
+ <!--
+
+ <scons_example name="ex_var2">
+ <file name="SConstruct" printme="1">
+ env = Environment(OS = ARGUMENTS.get('OS'))
+ for os in ['newell', 'post']:
+ SConscript('src/SConscript', variant_dir='build/' + os)
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_var2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
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 @@
+<!--
+
+ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<!--
+
+=head1 Variant builds
+
+=head2 Variations on a theme
+
+Other variations of this model are possible. For example, you might decide
+that you want to separate out your include files into platform dependent and
+platform independent files. In this case, you'd have to define an
+alternative to C<$INCLUDE> for platform-dependent files. Most F<Conscript>
+files, generating purely platform-independent include files, would not have
+to change.
+
+You might also want to be able to compile your whole system with debugging
+or profiling, for example, enabled. You could do this with appropriate
+command line options, such as C<DEBUG=on>. This would then be translated
+into the appropriate platform-specific requirements to enable debugging
+(this might include turning off optimization, for example). You could
+optionally vary the name space for these different types of systems, but, as
+we'll see in the next section, it's not B<essential> to do this, since Cons
+is pretty smart about rebuilding things when you change options.
+
+-->
+
+ <para>
+
+ 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.
+
+ </para>
+
+ <programlisting>
+ 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')
+ </programlisting>
+
+ <para>
+
+ This SConstruct file,
+ when run on a Linux system, yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q OS=linux</userinput>
+ 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"
+ </screen>
+
+ <para>
+
+ The same SConstruct file on Windows would build:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -Q OS=windows</userinput>
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", 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"
+ </screen>
+
+ <!--
+
+ <scons_example name="ex_var2">
+ <file name="SConstruct" printme="1">
+ env = Environment(OS = ARGUMENTS.get('OS'))
+ for os in ['newell', 'post']:
+ SConscript('src/SConscript', variant_dir='build/' + os)
+ </file>
+ </scons_example>
+
+ <scons_output example="ex_var2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
new file mode 100644
index 0000000..eb2a5ad
--- /dev/null
+++ b/src/CHANGES.txt
@@ -0,0 +1,5176 @@
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+# src/CHANGES.txt 4577 2009/12/27 19:44:43 scons
+
+
+ SCons - a software construction tool
+
+ Change Log
+
+
+
+RELEASE 1.2.0.d20091224 - Thu, 24 Dec 2009 17:20:55 -08000
+
+ From Jim Randall:
+ - Fixed temp filename race condition on Windows with long cmd lines.
+
+ From David Cournapeau:
+ - Fixed tryRun when sconf directory is in a variant dir.
+ - Do not add -fPIC for ifort tool on non-posix platforms (darwin and
+ windows).
+ - Fix bug 2294 (spurious CheckCC failures).
+ - Fix scons bootstrap process on windows 64 (wrong wininst name)
+
+ From William Deegan:
+ - Final merge from vs_revamp branch to main
+
+ - Added definition and usage of HOST_OS, HOST_ARCH, TARGET_OS,
+ TARGET_ARCH, currently only defined/used by Visual Studio
+ Compilers. This will be rolled out to other platforms/tools
+ in the future.
+
+ - Add check for python >= 3.0.0 and exit gracefully.
+ For 1.3 python >= 1.5.2 and < 3.0.0 are supported
+
+ - Fix bug 1944 - Handle non-existent .i file in swig emitter, previously
+ it would crash with an IOError exception. Now it will try to make an
+ educated guess on the module name based on the filename.
+
+ From Lukas Erlinghagen:
+
+ - Have AddOption() remove variables from the list of
+ seen-but-unknown variables (which are reported later).
+
+ - An option name and aliases can now be specified as a tuple.
+
+ From Hartmut Goebel:
+
+ - Textfile builder.
+
+ From Jared Grubb:
+
+ - use "is/is not" in comparisons with None instead of "==" or "!=".
+
+ From Jim Hunziker:
+
+ - Avoid adding -gphobos to a command line multiple times
+ when initializing use of the DMD compiler.
+
+ From Jason Kenney:
+
+ - Sugguested HOST/TARGET OS/ARCH separation.
+
+ From Steven Knight:
+
+ - Fix the -n option when used with VariantDir(duplicate=1)
+ and the variant directory doesn't already exist.
+
+ - Fix scanning of Unicode files for both UTF-16 endian flavors.
+
+ - Fix a TypeError on #include of file names with Unicode characters.
+
+ - Fix an exception if a null command-line argument is passed in.
+
+ - Evaluate Requires() prerequisites before a Node's direct children
+ (sources and dependencies).
+
+ From Greg Noel:
+
+ - Remove redundant __metaclass__ initializations in Environment.py.
+
+ - Correct the documentation of text returned by sconf.Result().
+
+ - Document that filenames with '.' as the first character are
+ ignored by Glob() by default (matching UNIX glob semantics).
+
+ - Fix SWIG testing infrastructure to work on Mac OS X.
+
+ - Restructure a test that occasionally hung so that the test would
+ detect when it was stuck and fail instead.
+
+ - Substfile builder.
+
+ From Gary Oberbrunner:
+
+ - When reporting a target that SCons doesn't know how to make,
+ specify whether it's a File, Dir, etc.
+
+ From Ben Webb:
+
+ - Fix use of $SWIGOUTDIR when generating Python wrappers.
+
+ - Add $SWIGDIRECTORSUFFIX and $SWIGVERSION construction variables.
+
+ From Rob Managan:
+
+ - Add -recorder flag to Latex commands and updated internals to
+ use the output to find files TeX creates. This allows the MiKTeX
+ installations to find the created files
+
+ - Notify user of Latex errors that would get buried in the
+ Latex output
+
+ - Remove LATEXSUFFIXES from environments that don't initialize Tex.
+
+ - Add support for the glosaaries package for glossaries and acronyms
+
+ - Fix problem that pdftex, latex, and pdflatex tools by themselves did
+ not create the actions for bibtex, makeindex,... by creating them
+ and other environment settings in one routine called by all four
+ tex tools.
+
+ - Fix problem with filenames of sideeffects when the user changes
+ the name of the output file from the latex default
+
+ - Add scanning of files included in Latex by means of \lstinputlisting{}
+ Patch from Stefan Hepp.
+
+ - Change command line for epstopdf to use --outfile= instead of -o
+ since this works on all platforms.
+ Patch from Stefan Hepp.
+
+RELEASE 1.2.0.d20090223 - Mon, 23 Feb 2009 08:41:06 -0800
+
+ From Stanislav Baranov:
+
+ - Make suffix-matching for scanners case-insensitive on Windows.
+
+ From David Cournapeau:
+
+ - Change the way SCons finds versions of Visual C/C++ and Visual
+ Studio to find and use the Microsoft v*vars.bat files.
+
+ From Robert P. J. Day:
+
+ - User's Guide updates.
+
+ From Dan Eaton:
+
+ - Fix generation of Visual Studio 8 project files on x64 platforms.
+
+ From Allan Erskine:
+
+ - Set IncludeSearchPath and PreprocessorDefinitions in generated
+ Visual Studio 8 project files, to help IntelliSense work.
+
+ From Mateusz Gruca:
+
+ - Fix deletion of broken symlinks by the --clean option.
+
+ From Steven Knight:
+
+ - Fix the error message when use of a non-existent drive on Windows
+ is detected.
+
+ - Add sources for files whose targets don't exist in $CHANGED_SOURCES.
+
+ - Detect implicit dependencies on commands even when the command is
+ quoted.
+
+ - Fix interaction of $CHANGED_SOURCES with the --config=force option.
+
+ - Fix finding #include files when the string contains escaped
+ backslashes like "C:\\some\\include.h".
+
+ - Pass $CCFLAGS to Visual C/C++ precompiled header compilation.
+
+ - Remove unnecessary nested $( $) around $_LIBDIRFLAGS on link lines
+ for the Microsoft linker, the OS/2 ilink linker and the Phar Lap
+ linkloc linker.
+
+ - Spell the Windows environment variables consistently "SystemDrive"
+ and "SystemRoot" instead of "SYSTEMDRIVE" and "SYSTEMROOT".
+
+
+
+RELEASE 1.2.0.d20090113 - Tue, 13 Jan 2009 02:50:30 -0800
+
+ From Stanislav Baranov, Ted Johnson and Steven Knight:
+
+ - Add support for batch compilation of Visual Studio C/C++ source
+ files, controlled by a new $MSVC_BATCH construction variable.
+
+ From Steven Knight:
+
+ - Print the message, "scons: Build interrupted." on error output,
+ not standard output.
+
+ - Add a --warn=future-deprecated option for advance warnings about
+ deprecated features that still have warnings hidden by default.
+
+ - Fix use of $SOURCE and $SOURCES attributes when there are no
+ sources specified in the Builder call.
+
+ - Add support for new $CHANGED_SOURCES, $CHANGED_TARGETS,
+ $UNCHANGED_SOURCES and $UNCHANGED_TARGETS variables.
+
+ - Add general support for batch builds through new batch_key= and
+ targets= keywords to Action object creation.
+
+ From Arve Knudsen:
+
+ - Make linker tools differentiate properly between SharedLibrary
+ and LoadableModule.
+
+ - Document TestCommon.shobj_prefix variable.
+
+ - Support $SWIGOUTDIR values with spaces.
+
+ From Rob Managan:
+
+ - Don't automatically try to build .pdf graphics files for
+ .eps files in \includegraphics{} calls in TeX/LaTeX files
+ when building with the PDF builder (and thus using pdflatex).
+
+ From Gary Oberbrunner:
+
+ - Allow AppendENVPath() and PrependENVPath() to interpret '#'
+ for paths relative to the top-level SConstruct directory.
+
+ - Use the Borland ilink -e option to specify the output file name.
+
+ - Document that the msvc Tool module uses $PCH, $PCHSTOP and $PDB.
+
+ - Allow WINDOWS_INSERT_DEF=0 to disable --output-def when linking
+ under MinGW.
+
+ From Zia Sobhani:
+
+ - Fix typos in the User's Guide.
+
+ From Greg Spencer:
+
+ - Support implicit dependency scanning of files encoded in utf-8
+ and utf-16.
+
+ From Roberto de Vecchi:
+
+ - Remove $CCFLAGS from the the default definitions of $CXXFLAGS for
+ Visual C/C++ and MIPSpro C++ on SGI so, they match other tools
+ and avoid flag duplication on C++ command lines.
+
+ From Ben Webb:
+
+ - Handle quoted module names in SWIG source files.
+
+ - Emit *_wrap.h when SWIG generates header file for directors
+
+ From Matthew Wesley:
+
+ - Copy file attributes so we identify, and can link a shared library
+ from, shared object files in a Repository.
+
+
+
+RELEASE 1.2.0 - Sat, 20 Dec 2008 22:47:29 -0800
+
+ From Steven Knight:
+
+ - Don't fail if can't import a _subprocess module on Windows.
+
+ - Add warnings for use of the deprecated Options object.
+
+
+
+RELEASE 1.1.0.d20081207 - Sun, 07 Dec 2008 19:17:23 -0800
+
+ From Benoit Belley:
+
+ - Improve the robustness of GetBuildFailures() by refactoring
+ SCons exception handling (especially BuildError exceptions).
+
+ - Have the --taskmastertrace= option print information about
+ individual Task methods, not just the Taskmaster control flow.
+
+ - Eliminate some spurious dependency cycles by being more aggressive
+ about pruning pending children from the Taskmaster walk.
+
+ - Suppress mistaken reports of a dependency cycle when a child
+ left on the pending list is a single Node in EXECUTED state.
+
+ From David Cournapeau:
+
+ - Fix $FORTRANMODDIRPREFIX for the ifort (Intel Fortran) tool.
+
+ From Brad Fitzpatrick:
+
+ - Don't pre-generate an exception message (which will likely be
+ ignored anyway) when an EntryProxy re-raises an AttributeError.
+
+ From Jared Grubb:
+
+ - Clean up coding style and white space in Node/FS.py.
+
+ - Fix a typo in the documentation for $_CPPDEFFLAGS.
+
+ - Issue 2401: Fix usage of comparisons with None.
+
+ From Ludwig Hähne:
+
+ - Handle Java inner classes declared within a method.
+
+ From Steven Knight:
+
+ - Fix label placement by the "scons-time.py func" subcommand
+ when a profile value was close to (or equal to) 0.0.
+
+ - Fix env.Append() and env.Prepend()'s ability to add a string to
+ list-like variables like $CCFLAGS under Python 2.6.
+
+ - Other Python2.6 portability: don't use "as" (a Python 2.6 keyword).
+ Don't use the deprecated Exception.message attribute.
+
+ - Support using the -f option to search for a different top-level
+ file name when walking up with the -D, -U or -u options.
+
+ - Fix use of VariantDir when the -n option is used and doesn't,
+ therefore, actually create the variant directory.
+
+ - Fix a stack trace from the --debug=includes option when passed a
+ static or shared library as an argument.
+
+ - Speed up the internal find_file() function (used for searching
+ CPPPATH, LIBPATH, etc.).
+
+ - Add support for using the Python "in" keyword on construction
+ environments (for example, if "CPPPATH" in env: ...).
+
+ - Fix use of Glob() when a repository or source directory contains
+ an in-memory Node without a corresponding on-disk file or directory.
+
+ - Add a warning about future reservation of $CHANGED_SOURCES,
+ $CHANGED_TARGETS, $UNCHANGED_SOURCES and $UNCHANGED_TARGETS.
+
+ - Enable by default the existing warnings about setting the resource
+ $SOURCE, $SOURCES, $TARGET and $TARGETS variable.
+
+ From Rob Managan:
+
+ - Scan for TeX files in the paths specified in the $TEXINPUTS
+ construction variable and the $TEXINPUTS environment variable.
+
+ - Configure the PDF() and PostScript() Builders as single_source so
+ they know each source file generates a separate target file.
+
+ - Add $EPSTOPDF, $EPSTOPDFFLAGS and $EPSTOPDFCOM
+
+ - Add .tex as a valid extension for the PDF() builder.
+
+ - Add regular expressions to find \input, \include and
+ \includegraphics.
+
+ - Support generating a .pdf file from a .eps source.
+
+ - Recursive scan included input TeX files.
+
+ - Handle requiring searched-for TeX input graphics files to have
+ extensions (to avoid trying to build a .eps from itself, e.g.).
+
+ From Greg Noel:
+
+ - Make the Action() function handle positional parameters consistently.
+
+ - Clarify use of Configure.CheckType().
+
+ - Make the File.{Dir,Entry,File}() methods create their entries
+ relative to the calling File's directory, not the SConscript
+ directory.
+
+ - Use the Python os.devnull variable to discard error output when
+ looking for the $CC or $CXX version.
+
+ - Mention LoadableModule() in the SharedLibrary() documentation.
+
+ From Gary Oberbrunner:
+
+ - Update the User's Guide to clarify use of the site_scons/
+ directory and the site_init.py module.
+
+ - Make env.AppendUnique() and env.PrependUnique remove duplicates
+ within a passed-in list being added, too.
+
+ From Randall Spangler:
+
+ - Fix Glob() so an on-disk file or directory beginning with '#'
+ doesn't throw an exception.
+
+
+
+RELEASE 1.1.0 - Thu, 09 Oct 2008 08:33:47 -0700
+
+ From Chris AtLee
+
+ - Use the specified environment when checking for the GCC compiler
+ version.
+
+ From Ian P. Cardenas:
+
+ - Fix Glob() polluting LIBPATH by returning copy of list
+
+ From David Cournapeau:
+
+ - Add CheckCC, CheckCXX, CheckSHCC and CheckSHCXX tests to
+ configuration contexts.
+
+ - Have the --profile= argument use the much faster cProfile module
+ (if it's available in the running Python version).
+
+ - Reorder MSVC compilation arguments so the /Fo is first.
+
+ From Bill Deegan:
+
+ - Add scanning Windows resource (.rc) files for implicit dependencies.
+
+ From John Gozde:
+
+ - When scanning for a #include file, don't use a directory that
+ has the same name as the file.
+
+ From Ralf W. Grosse-Kunstleve
+
+ - Suppress error output when checking for the GCC compiler version.
+
+ From Jared Grubb:
+
+ - Fix VariantDir duplication of #included files in subdirectories.
+
+ From Ludwig Hähne:
+
+ - Reduce memory usage when a directory is used as a dependency of
+ another Node (such as an Alias) by returning a concatenation
+ of the children's signatures + names, not the children's contents,
+ as the directory contents.
+
+ - Raise AttributeError, not KeyError, when a Builder can't be found.
+
+ - Invalidate cached Node information (such as the contenst returned
+ by the get_contents() method) when calling actions with Execute().
+
+ - Avoid object reference cycles from frame objects.
+
+ - Reduce memory usage from Null Executor objects.
+
+ - Compute MD5 checksums of large files without reading the entire
+ file contents into memory. Add a new --md5-chunksize option to
+ control the size of each chunk read into memory.
+
+ From Steven Knight:
+
+ - Fix the ability of the add_src_builder() method to add a new
+ source builder to any other builder.
+
+ - Avoid an infinite loop on non-Windows systems trying to find the
+ SCons library directory if the Python library directory does not
+ begin with the string "python".
+
+ - Search for the SCons library directory in "scons-local" (with
+ no version number) after "scons-local-{VERSION}".
+
+ From Rob Managan:
+
+ - Fix the user's ability to interrupt the TeX build chain.
+
+ - Fix the TeX builder's allowing the user to specify the target name,
+ instead of always using its default output name based on the source.
+
+ - Iterate building TeX output files until all warning are gone
+ and the auxiliary files stop changing, or until we reach the
+ (configurable) maximum number of retries.
+
+ - Add TeX scanner support for: glossaries, nomenclatures, lists of
+ figures, lists of tables, hyperref and beamer.
+
+ - Use the $BIBINPUTS, $BSTINPUTS, $TEXINPUTS and $TEXPICTS construction
+ variables as search paths for the relevant types of input file.
+
+ - Fix building TeX with VariantDir(duplicate=0) in effect.
+
+ - Fix the LaTeX scanner to search for graphics on the TEXINPUTS path.
+
+ - Have the PDFLaTeX scanner search for .gif files as well.
+
+ From Greg Noel:
+
+ - Fix typos and format bugs in the man page.
+
+ - Add a first draft of a wrapper module for Python's subprocess
+ module.
+
+ - Refactor use of the SCons.compat module so other modules don't
+ have to import it individually.
+
+ - Add .sx as a suffix for assembly language files that use the
+ C preprocessor.
+
+ From Gary Oberbrunner:
+
+ - Make Glob() sort the returned list of Files or Nodes
+ to prevent spurious rebuilds.
+
+ - Add a delete_existing keyword argument to the AppendENVPath()
+ and PrependENVPath() Environment methods.
+
+ - Add ability to use "$SOURCE" when specifying a target to a builder
+
+ From Damyan Pepper:
+
+ - Add a test case to verify that SConsignFile() files can be
+ created in previously non-existent subdirectories.
+
+ From Jim Randall:
+
+ - Make the subdirectory in which the SConsignFile() file will
+ live, if the subdirectory doesn't already exist.
+
+ From Ali Tofigh:
+
+ - Add a test to verify duplication of files in VariantDir subdirectories.
+
+
+
+RELEASE 1.0.1 - Sat, 06 Sep 2008 07:29:34 -0700
+
+ From Greg Noel:
+
+ - Add a FindFile() section to the User's Guide.
+
+ - Fix the FindFile() documentation in the man page.
+
+ - Fix formatting errors in the Package() description in the man page.
+
+ - Escape parentheses that appear within variable names when spawning
+ command lines using os.system().
+
+
+
+RELEASE 1.0.0.d20080826 - Tue, 26 Aug 2008 09:12:40 -0700
+
+ From Gary Oberbrunner:
+
+ - Make Glob() sort the returned list of Files or Nodes
+ to prevent spurious rebuilds.
+
+ From Ian P. Cardenas:
+
+ - Fix Glob() polluting LIBPATH by returning copy of list
+
+
+
+RELEASE 1.0.0 - XXX
+
+ From Jared Grubb:
+
+ - Clear the Node state when turning a generic Entry into a Dir.
+
+ From Ludwig Hähne:
+
+ - Fix sporadic output-order failures in test/GetBuildFailures/parallel.py.
+
+ - Document the ParseDepends() function in the User's Guide.
+
+ From khomenko:
+
+ - Create a separate description and long_description for RPM packages.
+
+ From Steven Knight:
+
+ - Document the GetLaunchDir() function in the User's Guide.
+
+ - Have the env.Execute() method print an error message if the
+ executed command fails.
+
+ - Add a script for creating a standard SCons development system on
+ Ubuntu Hardy. Rewrite subsidiary scripts for install Python and
+ SCons versions in Python (from shell).
+
+ From Greg Noel:
+
+ - Handle yacc/bison on newer Mac OS X versions creating file.hpp,
+ not file.cpp.h.
+
+ - In RPCGEN tests, ignore stderr messages from older versions of
+ rpcgen on some versions of Mac OS X.
+
+ - Fix typos in man page descriptions of Tag() and Package(), and in
+ the scons-time man page.
+
+ - Fix documentation of SConf.CheckLibWithHeader and other SConf methods.
+
+ - Update documentation of SConscript(variant_dir) usage.
+
+ - Fix SWIG tests for (some versions of) Mac OS X.
+
+ From Jonas Olsson:
+
+ - Print the warning about -j on Windows being potentially unreliable if
+ the pywin32 extensions are unavailable or lack file handle operations.
+
+ From Jim Randall:
+
+ - Fix the env.WhereIs() method to expand construction variables.
+
+ From Rogier Schouten:
+
+ - Enable building of shared libraries with the Bordand ilink32 linker.
+
+
+
+RELEASE 1.0.0 - Sat, 09 Aug 2008 12:19:44 -0700
+
+ From Luca Falavigna:
+
+ - Fix SCons man page indentation under Debian's man page macros.
+
+ From Steven Knight:
+
+ - Clarify the man page description of the SConscript(src_dir) argument.
+
+ - User's Guide updates:
+
+ - Document the BUILD_TARGETS, COMMAND_LINE_TARGETS and
+ DEFAULT_TARGETS variables.
+
+ - Document the AddOption(), GetOption() and SetOption() functions.
+
+ - Document the Requires() function; convert to the Variables
+ object, its UnknownOptions() method, and its associated
+ BoolVariable(), EnumVariable(), ListVariable(), PackageVariable()
+ and PathVariable() functions.
+
+ - Document the Progress() function.
+
+ - Reorganize the chapter and sections describing the different
+ types of environments and how they interact. Document the
+ SetDefault() method. Document the PrependENVPath() and
+ AppendENVPath() functions.
+
+ - Reorganize the command-line arguments chapter. Document the
+ ARGLIST variable.
+
+ - Collect some miscellaneous sections into a chapter about
+ configuring build output.
+
+ - Man page updates:
+
+ - Document suggested use of the Visual C/C++ /FC option to fix
+ the ability to double-click on file names in compilation error
+ messages.
+
+ - Document the need to use Clean() for any SideEffect() files that
+ must be explicitly removed when their targets are removed.
+
+ - Explicitly document use of Node lists as input to Dependency().
+
+ From Greg Noel:
+
+ - Document MergeFlags(), ParseConfig(), ParseFlags() and SideEffect()
+ in the User's Guide.
+
+ From Gary Oberbrunner:
+
+ - Document use of the GetBuildFailures() function in the User's Guide.
+
+ From Adam Simpkins:
+
+ - Add man page text clarifying the behavior of AddPreAction() and
+ AddPostAction() when called with multiple targets.
+
+ From Alexey Zezukin:
+
+ - Fix incorrectly swapped man page descriptions of the --warn= options
+ for duplicate-environment and missing-sconscript.
+
+
+
+RELEASE 0.98.5 - Sat, 07 Jun 2008 08:20:35 -0700
+
+ From Benoit Belley:
+
+ - Fix the Intel C++ compiler ABI specification for EMT64 processors.
+
+ From David Cournapeau:
+
+ - Issue a (suppressable) warning, not an error, when trying to link
+ C++ and Fortran object files into the same executable.
+
+ From Steven Knight:
+
+ - Update the scons.bat file so that it returns the real exit status
+ from SCons, even though it uses setlocal + endlocal.
+
+ - Fix the --interactive post-build messages so it doesn't get stuck
+ mistakenly reporting failures after any individual build fails.
+
+ - Fix calling File() as a File object method in some circumstances.
+
+ - Fix setup.py installation on Mac OS X so SCons gets installed
+ under /usr/lcoal by default, not in the Mac OS X Python framework.
+
+
+
+RELEASE 0.98.4 - Sat, 17 May 2008 22:14:46 -0700
+
+ From Benoit Belley:
+
+ - Fix calculation of signatures for Python function actions with
+ closures in Python versions before 2.5.
+
+ From David Cournapeau:
+
+ - Fix the initialization of $SHF77FLAGS so it includes $F77FLAGS.
+
+ From Jonas Olsson:
+
+ - Fix a syntax error in the Intel C compiler support on Windows.
+
+ From Steven Knight:
+
+ - Change how we represent Python Value Nodes when printing and when
+ stored in .sconsign files (to avoid blowing out memory by storing
+ huge strings in .sconsign files after multiple runs using Configure
+ contexts cause the Value strings to be re-escaped each time).
+
+ - Fix a regression in not executing configuration checks after failure
+ of any configuration check that used the same compiler or other tool.
+
+ - Handle multiple destinations in Visual Studio 8 settings for the
+ analogues to the INCLUDE, LIBRARY and PATH variables.
+
+ From Greg Noel:
+
+ - Update man page text for VariantDir().
+
+
+
+RELEASE 0.98.3 - Tue, 29 Apr 2008 22:40:12 -0700
+
+ From Greg Noel:
+
+ - Fix use of $CXXFLAGS when building C++ shared object files.
+
+ From Steven Knight:
+
+ - Fix a regression when a Builder's source_scanner doesn't select
+ a more specific scanner for the suffix of a specified source file.
+
+ - Fix the Options object backwards compatibility so people can still
+ "import SCons.Options.{Bool,Enum,List,Package,Path}Option" submodules.
+
+ - Fix searching for implicit dependencies when an Entry Node shows up
+ in the search path list.
+
+ From Stefano:
+
+ - Fix expansion of $FORTRANMODDIR in the default Fortran command line(s)
+ when it's set to something like ${TARGET.dir}.
+
+
+
+RELEASE 0.98.2 - Sun, 20 Apr 2008 23:38:56 -0700
+
+ From Steven Knight:
+
+ - Fix a bug in Fortran suffix computation that would cause SCons to
+ run out of memory on Windows systems.
+
+ - Fix being able to specify --interactive mode command lines with
+ \ (backslash) path name separators on Windows.
+
+ From Gary Oberbrunner:
+
+ - Document Glob() in the User's Guide.
+
+
+
+RELEASE 0.98.1 - Fri, 18 Apr 2008 19:11:58 -0700
+
+ From Benoit Belley:
+
+ - Speed up the SCons.Util.to_string*() functions.
+
+ - Optimize various Node intialization and calculations.
+
+ - Optimize Executor scanning code.
+
+ - Optimize Taskmaster execution, including dependency-cycle checking.
+
+ - Fix the --debug=stree option so it prints its tree once, not twice.
+
+ From Johan BoulÃ:
+
+ - Fix the ability to use LoadableModule() under MinGW.
+
+ From David Cournapeau:
+
+ - Various missing Fortran-related construction variables have been added.
+
+ - SCons now uses the program specified in the $FORTRAN construction
+ variable to link Fortran object files.
+
+ - Fortran compilers on Linux (Intel, g77 and gfortran) now add the -fPIC
+ option by default when compilling shared objects.
+
+ - New 'sunf77', 'sunf90' and 'sunf95' Tool modules have been added to
+ support Sun Fortran compilers. On Solaris, the Sun Fortran compilers
+ are used in preference to other compilers by default.
+
+ - Fortran support now uses gfortran in preference to g77.
+
+ - Fortran file suffixes are now configurable through the
+ $F77FILESUFFIXES, $F90FILESUFFIXES, $F95FILESUFFIXES and
+ $FORTRANFILESUFFIXES variables.
+
+ From Steven Knight:
+
+ - Make the -d, -e, -w and --no-print-directory options "Ignored for
+ compatibility." (We're not going to implement them.)
+
+ - Fix a serious inefficiency in how SCons checks for whether any source
+ files are missing when a Builder call creates many targets from many
+ input source files.
+
+ - In Java projects, make the target .class files depend only on the
+ specific source .java files where the individual classes are defined.
+
+ - Don't store duplicate source file entries in the .sconsign file so
+ we don't endlessly rebuild the target(s) for no reason.
+
+ - Add a Variables object as the first step towards deprecating the
+ Options object name. Similarly, add BoolVariable(), EnumVariable(),
+ ListVariable(), PackageVariable() and PathVariable() functions
+ as first steps towards replacing BoolOption(), EnumOption(),
+ ListOption(), PackageOption() and PathOption().
+
+ - Change the options= keyword argument to the Environment() function
+ to variables=, to avoid confusion with SCons command-line options.
+ Continue supporting the options= keyword for backwards compatibility.
+
+ - When $SWIGFLAGS contains the -python flag, expect the generated .py
+ file to be in the same (sub)directory as the target.
+
+ - When compiling C++ files, allow $CCFLAGS settings to show up on the
+ command line even when $CXXFLAGS has been redefined.
+
+ - Fix --interactive with -u/-U/-D when a VariantDir() is used.
+
+ From Anatoly Techtonik:
+
+ - Have the scons.bat file add the script execution directory to its
+ local %PATH% on Windows, so the Python executable can be found.
+
+ From Mike Wake:
+
+ - Fix passing variable names as a list to the Return() function.
+
+ From Matthew Wesley:
+
+ - Add support for the GDC 'D' language compiler.
+
+
+
+RELEASE 0.98 - Sun, 30 Mar 2008 23:33:05 -0700
+
+ From Benoit Belley:
+
+ - Fix the --keep-going flag so it builds all possible targets even when
+ a later top-level target depends on a child that failed its build.
+
+ - Fix being able to use $PDB and $WINDWOWS_INSERT_MANIFEST together.
+
+ - Don't crash if un-installing the Intel C compiler leaves left-over,
+ dangling entries in the Windows registry.
+
+ - Improve support for non-standard library prefixes and suffixes by
+ stripping all prefixes/suffixes from file name string as appropriate.
+
+ - Reduce the default stack size for -j worker threads to 256 Kbytes.
+ Provide user control over this value by adding --stack-size and
+ --warn=stack-size options, and a SetOption('stack_size') function.
+
+ - Fix a crash on Linux systems when trying to use the Intel C compiler
+ and no /opt/intel_cc_* directories are found.
+
+ - Improve using Python functions as actions by incorporating into
+ a FunctionAction's signature:
+ - literal values referenced by the byte code.
+ - values of default arguments
+ - code of nested functions
+ - values of variables captured by closures
+ - names of referenced global variables and functions
+
+ - Fix the closing message when --clean and --keep-going are both
+ used and no errors occur.
+
+ - Add support for the Intel C compiler on Mac OS X.
+
+ - Speed up reading SConscript files by about 20% (for some
+ configurations) by: 1) optimizing the SCons.Util.is_*() and
+ SCons.Util.flatten() functions; 2) avoiding unnecessary os.stat()
+ calls by using a File's .suffix attribute directly instead of
+ stringifying it.
+
+ From Jérôme Berger:
+
+ - Have the D language scanner search for .di files as well as .d files.
+
+ - Add a find_include_names() method to the Scanner.Classic class to
+ abstract out how included names can be generated by subclasses.
+
+ - Allow the D language scanner to detect multiple modules imported by
+ a single statement.
+
+ From Konstantin Bozhikov:
+
+ - Support expansion of construction variables that contain or refer
+ to lists of other variables or Nodes within expansions like $CPPPATH.
+
+ - Change variable substitution (the env.subst() method) so that an
+ input sequence (list or tuple) is preserved as a list in the output.
+
+ From David Cournapeau:
+
+ - Add a CheckDeclaration() call to configure contexts.
+
+ - Improve the CheckTypeSize() code.
+
+ - Add a Define() call to configure contexts, to add arbitrary #define
+ lines to a generated configure header file.
+
+ - Add a "gfortran" Tool module for the GNU F95/F2003 compiler.
+
+ - Avoid use of -rpath with the Mac OS X linker.
+
+ - Add comment lines to the generated config.h file to describe what
+ the various #define/#undef lines are doing.
+
+ From Steven Knight:
+
+ - Support the ability to subclass the new-style "str" class as input
+ to Builders.
+
+ - Improve the performance of our type-checking by using isinstance()
+ with new-style classes.
+
+ - Fix #include (and other $*PATH variables searches) of files with
+ absolute path names. Don't die if they don't exist (due to being
+ #ifdef'ed out or the like).
+
+ - Fix --interactive mode when Default(None) is used.
+
+ - Fix --debug=memoizer to work around a bug in base Python 2.2 metaclass
+ initialization (by just not allowing Memoization in Python versions
+ that have the bug).
+
+ - Have the "scons-time time" subcommand handle empty log files, and
+ log files that contain no results specified by the --which option.
+
+ - Fix the max Y of vertical bars drawn by "scons-time --fmt=gnuplot".
+
+ - On Mac OS X, account for the fact that the header file generated
+ from a C++ file will be named (e.g.) file.cpp.h, not file.hpp.
+
+ - Fix floating-point numbers confusing the Java parser about
+ generated .class file names in some configurations.
+
+ - Document (nearly) all the values you can now fetch with GetOption().
+
+ - Fix use of file names containing strings of multiple spaces when
+ using ActionFactory instances like the Copy() or Move() function.
+
+ - Fix a 0.97 regression when using a variable expansion (like
+ $OBJSUFFIX) in a source file name to a builder with attached source
+ builders that match suffix (like Program()+Object()).
+
+ - Have the Java parser recognize generics (surrounded by angle brackets)
+ so they don't interfere with identifying anonymous inner classes.
+
+ - Avoid an infinite loop when trying to use saved copies of the
+ env.Install() or env.InstallAs() after replacing the method
+ attributes.
+
+ - Improve the performance of setting construction variables.
+
+ - When cloning a construction environment, avoid over-writing an
+ attribute for an added method if the user explicitly replaced it.
+
+ - Add a warning about deprecated support for Python 1.5, 2.0 and 2.1.
+
+ - Fix being able to SetOption('warn', ...) in SConscript files.
+
+ - Add a warning about env.Copy() being deprecated.
+
+ - Add warnings about the --debug={dtree,stree,tree} options
+ being deprecated.
+
+ - Add VariantDir() as the first step towards deprecating BuildDir().
+ Add the keyword argument "variant_dir" as the replacement for
+ "build_dir".
+
+ - Add warnings about the {Target,Source}Signatures() methods and
+ functions being deprecated.
+
+ From Rob Managan:
+
+ - Enhance TeX and LaTeX support to work with BuildDir(duplicate=0).
+
+ - Re-run LaTeX when it issues a package warning that it must be re-run.
+
+ From Leanid Nazdrynau:
+
+ - Have the Copy() action factory preserve file modes and times
+ when copying individual files.
+
+ From Jan Nijtmans:
+
+ - If $JARCHDIR isn't set explicitly, use the .java_classdir attribute
+ that was set when the Java() Builder built the .class files.
+
+ From Greg Noel:
+
+ - Document the Dir(), File() and Entry() methods of Dir and File Nodes.
+
+ - Add the parse_flags option when creating Environments
+
+ From Gary Oberbrunner:
+
+ - Make File(), Dir() and Entry() return a list of Nodes when passed
+ a list of names, instead of trying to make a string from the name
+ list and making a Node from that string.
+
+ - Fix the ability to build an Alias in --interactive mode.
+
+ - Fix the ability to hash the contents of actions for nested Python
+ functions on Python versions where the inability to pickle them
+ returns a TypeError (instead of the documented PicklingError).
+
+ From Jonas Olsson:
+
+ - Fix use of the Intel C compiler when the top compiler directory,
+ but not the compiler version, is specified.
+
+ - Handle Intel C compiler network license files (port@system).
+
+ From Jim Randall:
+
+ - Fix how Python Value Nodes are printed in --debug=explain output.
+
+ From Adam Simpkins:
+
+ - Add a --interactive option that starts a session for building (or
+ cleaning) targets without re-reading the SConscript files every time.
+
+ - Fix use of readline command-line editing in --interactive mode.
+
+ - Have the --interactive mode "build" command with no arguments
+ build the specified Default() targets.
+
+ - Fix the Chmod(), Delete(), Mkdir() and Touch() Action factories to
+ take a list (of Nodes or strings) as arguments.
+
+ From Vaclav Smilauer:
+
+ - Fix saving and restoring an Options value of 'all' on Python
+ versions where all() is a builtin function.
+
+ From Daniel Svensson:
+
+ - Code correction in SCons.Util.is_List().
+
+ From Ben Webb:
+
+ - Support the SWIG %module statement with following modifiers in
+ parenthese (e.g., '%module(directors="1")').
+
+
+
+RELEASE 0.97.0d20071212 - Wed, 12 Dec 2007 09:29:32 -0600
+
+ From Benoit Belley:
+
+ - Fix occasional spurious rebuilds and inefficiency when using
+ --implicit-cache and Builders that produce multiple targets.
+
+ - Allow SCons to not have to know about the builders of generated
+ files when BuildDir(duplicate=0) is used, potentially allowing some
+ SConscript files to be ignored for smaller builds.
+
+ From David Cournapeau:
+
+ - Add a CheckTypeSize() call to configure contexts.
+
+ From Ken Deeter:
+
+ - Make the "contents" of Alias Nodes a concatenation of the children's
+ content signatures (MD5 checksums), not a concatenation of the
+ children's contents, to avoid using large amounts of memory during
+ signature calculation.
+
+ From Malte Helmert:
+
+ - Fix a lot of typos in the man page and User's Guide.
+
+ From Geoffrey Irving:
+
+ - Speed up conversion of paths in .sconsign files to File or Dir Nodes.
+
+ From Steven Knight:
+
+ - Add an Options.UnknownOptions() method that returns any settings
+ (from the command line, or whatever dictionary was passed in)
+ that aren't known to the Options object.
+
+ - Add a Glob() function.
+
+ - When removing targets with the -c option, use the absolute path (to
+ avoid problems interpreting BuildDir() when the top-level directory
+ is the source directory).
+
+ - Fix problems with Install() and InstallAs() when called through a
+ clone (of a clone, ...) of a cloned construction environment.
+
+ - When executing a file containing Options() settings, add the file's
+ directory to sys.path (so modules can be imported from there) and
+ explicity set __name__ to the name of the file so the statement's
+ in the file can deduce the location if they need to.
+
+ - Fix an O(n^2) performance problem when adding sources to a target
+ through calls to a multi Builder (including Aliases).
+
+ - Redefine the $WINDOWSPROGMANIFESTSUFFIX and
+ $WINDOWSSHLIBMANIFESTSUFFIX variables so they pick up changes to
+ the underlying $SHLIBSUFFIX and $PROGSUFFIX variables.
+
+ - Add a GetBuildFailures() function that can be called from functions
+ registered with the Python atexit module to print summary information
+ about any failures encountered while building.
+
+ - Return a NodeList object, not a Python list, when a single_source
+ Builder like Object() is called with more than one file.
+
+ - When searching for implicit dependency files in the directories
+ in a $*PATH list, don't create Dir Nodes for directories that
+ don't actually exist on-disk.
+
+ - Add a Requires() function to allow the specification of order-only
+ prerequisites, which will be updated before specified "downstream"
+ targets but which don't actually cause the target to be rebuilt.
+
+ - Restore the FS.{Dir,File,Entry}.rel_path() method.
+
+ - Make the default behavior of {Source,Target}Signatures('timestamp')
+ be equivalent to 'timestamp-match', not 'timestamp-newer'.
+
+ - Fix use of CacheDir with Decider('timestamp-newer') by updating
+ the modification time when copying files from the cache.
+
+ - Fix random issues with parallel (-j) builds on Windows when Python
+ holds open file handles (especially for SCons temporary files,
+ or targets built by Python function actions) across process creation.
+
+ From Maxim Kartashev:
+
+ - Fix test scripts when run on Solaris.
+
+ From Gary Oberbrunner:
+
+ - Fix Glob() when a pattern is in an explicitly-named subdirectory.
+
+ From Philipp Scholl:
+
+ - Fix setting up targets if multiple Package builders are specified
+ at once.
+
+
+
+RELEASE 0.97.0d20070918 - Tue, 18 Sep 2007 10:51:27 -0500
+
+ From Steven Knight:
+
+ - Fix the wix Tool module to handle null entries in $PATH variables.
+
+ - Move the documentation of Install() and InstallAs() from the list
+ of functions to the list of Builders (now that they're implemented
+ as such).
+
+ - Allow env.CacheDir() to be set per construction environment. The
+ global CacheDir() function now sets an overridable global default.
+
+ - Add an env.Decider() method and a Node.Decider() method that allow
+ flexible specification of an arbitrary function to decide if a given
+ dependency has changed since the last time a target was built.
+
+ - Don't execute Configure actions (while reading SConscript files)
+ when cleaning (-c) or getting help (-h or -H).
+
+ - Add to each target an implicit dependency on the external command(s)
+ used to build the target, as found by searching env['ENV']['PATH']
+ for the first argument on each executed command line.
+
+ - Add support for a $IMPLICIT_COMMAND_DEPENDENCIES construction
+ variabe that can be used to disable the automatic implicit
+ dependency on executed commands.
+
+ - Add an "ensure_suffix" keyword to Builder() definitions that, when
+ true, will add the configured suffix to the targets even if it looks
+ like they already have a different suffix.
+
+ - Add a Progress() function that allows for calling a function or string
+ (or list of strings) to display progress while walking the DAG.
+
+ - Allow ParseConfig(), MergeFlags() and ParseFlags() to handle output
+ from a *config command with quoted path names that contain spaces.
+
+ - Make the Return() function stop processing the SConscript file and
+ return immediately. Add a "stop=" keyword argument that can be set
+ to False to preserve the old behavior.
+
+ - Fix use of exitstatfunc on an Action.
+
+ - Introduce all man page function examples with "Example:" or "Examples:".
+
+ - When a file gets added to a directory, make sure the directory gets
+ re-scanned for the new implicit dependency.
+
+ - Fix handling a file that's specified multiple times in a target
+ list so that it doesn't cause dependent Nodes to "disappear" from
+ the dependency graph walk.
+
+ From Carsten Koch:
+
+ - Avoid race conditions with same-named files and directory creation
+ when pushing copies of files to CacheDir().
+
+ From Tzvetan Mikov:
+
+ - Handle $ in Java class names.
+
+ From Gary Oberbrunner:
+
+ - Add support for the Intel C compiler on Windows64.
+
+ - On SGI IRIX, have $SHCXX use $CXX by default (like other platforms).
+
+ From Sohail Somani:
+
+ - When Cloning a construction environment, set any variables before
+ applying tools (so the tool module can access the configured settings)
+ and re-set them after (so they end up matching what the user set).
+
+ From Matthias Troffaes:
+
+ - Make sure extra auxiliary files generated by some LaTeX packages
+ and not ending in .aux also get deleted by scons -c.
+
+ From Greg Ward:
+
+ - Add a $JAVABOOTCLASSPATH variable for directories to be passed to the
+ javac -bootclasspath option.
+
+ From Christoph Wiedemann:
+
+ - Add implicit dependencies on the commands used to build a target.
+
+
+
+
+RELEASE 0.97.0d20070809 - Fri, 10 Aug 2007 10:51:27 -0500
+
+ From Lars Albertsson:
+
+ - Don't error if a #include line happens to match a directory
+ somewhere on a path (like $CPPPATH, $FORTRANPATH, etc.).
+
+ From Mark Bertoglio:
+
+ - Fix listing multiple projects in Visual Studio 7.[01] solution files,
+ including generating individual project GUIDs instead of re-using
+ the solution GUID.
+
+ From Jean Brouwers:
+
+ - Add /opt/SUNWspro/bin to the default execution PATH on Solaris.
+
+ From Allan Erskine:
+
+ - Only expect the Microsoft IDL compiler to emit *_p.c and *_data.c
+ files if the /proxy and /dlldata switches are used (respectively).
+
+ From Steven Knight:
+
+ - Have --debug=explain report if a target is being rebuilt because
+ AlwaysBuild() is specified (instead of "unknown reasons").
+
+ - Support {Get,Set}Option('help') to make it easier for SConscript
+ files to tell if a help option (-h, --help, etc.) has been specified.
+
+ - Support {Get,Set}Option('random') so random-dependency interaction
+ with CacheDir() is controllable from SConscript files.
+
+ - Add a new AddOption() function to support user-defined command-
+ line flags (like --prefix=, --force, etc.).
+
+ - Push and retrieve built symlinks to/from a CacheDir() as actual
+ symlinks, not by copying the file contents.
+
+ - Fix how the Action module handles stringifying the shared library
+ generator in the Tool/mingw.py module.
+
+ - When generating a config.h file, print "#define HAVE_{FEATURE} 1"
+ instad of just "#define HAVE_{FEATURE}", for more compatibility
+ with Autoconf-style projects.
+
+ - Fix expansion of $TARGET, $TARGETS, $SOURCE and $SOURCES keywords in
+ Visual C/C++ PDB file names.
+
+ - Fix locating Visual C/C++ PDB files in build directories.
+
+ - Support an env.AddMethod() method and an AddMethod() global function
+ for adding a new method, respectively, to a construction environment
+ or an arbitrary object (such as a class).
+
+ - Fix the --debug=time option when the -j option is specified and all
+ files are up to date.
+
+ - Add a $SWIGOUTDIR variable to allow setting the swig -outdir option,
+ and use it to identify files created by the swig -java option.
+
+ - Add a $SWIGPATH variable that specifies the path to be searched
+ for included SWIG files, Also add related $SWIGINCPREFIX and
+ $SWIGINCSUFFIX variables that specify the prefix and suffix to
+ be be added to each $SWIGPATH directory when expanded on the SWIG
+ command line.
+
+ - More efficient copying of construction environments (mostly borrowed
+ from copy.deepcopy() in the standard Python library).
+
+ - When printing --tree=prune output, don't print [brackets] around
+ source files, only do so for built targets with children.
+
+ - Fix interpretation of Builder source arguments when the Builder has
+ a src_suffix *and* a source_builder and the argument has no suffix.
+
+ - Fix use of expansions like ${TARGET.dir} or ${SOURCE.dir} in the
+ following construction variables: $FORTRANMODDIR, $JARCHDIR,
+ $JARFLAGS, $LEXFLAGS, $SWIGFLAGS, $SWIGOUTDIR and $YACCFLAGS.
+
+ - Fix dependencies on Java files generated by SWIG so they can be
+ detected and built in one pass.
+
+ - Fix SWIG when used with a BuildDir().
+
+ From Leanid Nazdrynau:
+
+ - When applying Tool modules after a construction environment has
+ already been created, don't overwrite existing $CFILESUFFIX and
+ $CXXFILESUFFIX value.
+
+ - Support passing the Java() builder a list of explicit .java files
+ (not only a list of directories to be scanned for .java files).
+
+ - Support passing .java files to the Jar() and JavaH() builders, which
+ then use the builder underlying the Java() builder to turn them into
+ .class files. (That is, the Jar()-Java() chain of builders become
+ multi-step, like the Program()-Object()-CFile() builders.)
+
+ - Support passing SWIG .i files to the Java builders (Java(),
+ Jar(), JavaH()), to cause intermediate .java files to be created
+ automatically.
+
+ - Add $JAVACLASSPATH and $JAVASOURCEPATH variables, that get added to
+ the javac "-classpath" and "-sourcepath" options. (Note that SCons
+ does *not* currently search these paths for implicit dependencies.)
+
+ - Commonize initialization of Java-related builders.
+
+ From Jan Nijtmans:
+
+ - Find Java anonymous classes when the next token after the name is
+ an open parenthesis.
+
+ From Gary Oberbrunner:
+
+ - Fix a code example in the man page.
+
+ From Tilo Prutz:
+
+ - Add support for the file names that Java 1.5 (and 1.6) generates for
+ nested anonymous inner classes, which are different from Java 1.4.
+
+ From Adam Simpkins:
+
+ - Allow worker threads to terminate gracefully when all jobs are
+ finished.
+
+ From Sohail Somani:
+
+ - Add LaTeX scanner support for finding dependencies specified with
+ the \usepackage{} directive.
+
+
+
+RELEASE 0.97 - Thu, 17 May 2007 08:59:41 -0500
+
+ From Steven Knight:
+
+ - Fix a bug that would make parallel builds stop in their tracks if
+ Nodes that depended on lists that contained some Nodes built together
+ caused the reference count to drop below 0 if the Nodes were visited
+ and commands finished in the wrong order.
+
+ - Make sure the DirEntryScanner doesn't choke if it's handed something
+ that's not a directory (Node.FS.Dir) Node.
+
+
+
+RELEASE 0.96.96 - Thu, 12 Apr 2007 12:36:25 -0500
+
+ NOTE: This is (Yet) a(nother) pre-release of 0.97 for testing purposes.
+
+ From Joe Bloggs:
+
+ - Man page fix: remove cut-and-paste sentence in NoCache() description.
+
+ From Dmitry Grigorenko and Gary Oberbrunner:
+
+ - Use the Intel C++ compiler, not $CC, to link C++ source.
+
+ From Helmut Grohne:
+
+ - Fix the man page example of propagating a user's external environment.
+
+ From Steven Knight:
+
+ - Back out (most of) the Windows registry installer patch, which
+ seems to not work on some versions of Windows.
+
+ - Don't treat Java ".class" attributes as defining an inner class.
+
+ - Fix detecting an erroneous Java anonymous class when the first
+ non-skipped token after a "new" keyword is a closing brace.
+
+ - Fix a regression when a CPPDEFINES list contains a tuple, the second
+ item of which (the option value) is a construction variable expansion
+ (e.g. $VALUE) and the value of the variable isn't a string.
+
+ - Improve the error message if an IOError (like trying to read a
+ directory as a file) occurs while deciding if a node is up-to-date.
+
+ - Fix "maximum recursion" / "unhashable type" errors in $CPPPATH
+ PathList expansion if a subsidiary expansion yields a stringable,
+ non-Node object.
+
+ - Generate API documentation from the docstrings (using epydoc).
+
+ - Fix use of --debug=presub with Actions for out-of-the-box Builders.
+
+ - Fix handling nested lists within $CPPPATH, $LIBPATH, etc.
+
+ - Fix a "builders_used" AttributeError that real-world Qt initialization
+ triggered in the refactored suffix handling for Builders.
+
+ - Make the reported --debug=time timings meaningful when used with -j.
+ Better documentation of what the times mean.
+
+ - User Guide updates: --random, AlwaysBuild(), --tree=,
+ --debug=findlibs, --debug=presub, --debug=stacktrace,
+ --taskmastertrace.
+
+ - Document (in both man page and User's Guide) that --implicit-cache
+ ignores changes in $CPPPATH, $LIBPATH, etc.
+
+ From Jean-Baptiste Lab:
+
+ - Remove hard-coded dependency on Python 2.2 from Debian packaging files.
+
+ From Jeff Mahovsky:
+
+ - Handle spaces in the build target name in Visual Studio project files.
+
+ From Rob Managan:
+
+ - Re-run LaTeX after BibTeX has been re-run in response to a changed
+ .bib file.
+
+ From Joel B. Mohler:
+
+ - Make additional TeX auxiliary files (.toc, .idx and .bbl files)
+ Precious so their removal doesn't affect whether the necessary
+ sections are included in output PDF or PostScript files.
+
+ From Gary Oberbrunner:
+
+ - Fix the ability to import modules in the site_scons directory from
+ a subdirectory.
+
+ From Adam Simpkins:
+
+ - Make sure parallel (-j) builds all targets even if they show up
+ multiple times in the child list (as a source and a dependency).
+
+ From Matthias Troffaes:
+
+ - Don't re-run TeX if the triggering strings (\makeindex, \bibliography
+ \tableofcontents) are commented out.
+
+ From Richard Viney:
+
+ - Fix use of custom include and lib paths with Visual Studio 8.
+
+ - Select the default .NET Framework SDK Dir based on the version of
+ Visual Studio being used.
+
+
+
+RELEASE 0.96.95 - Mon, 12 Feb 2007 20:25:16 -0600
+
+ From Anatoly:
+
+ - Add the scons.org URL and a package description to the setup.py
+ arguments.
+
+ - Have the Windows installer add a registry entry for scons.bat in the
+ "App Paths" key, so scons.bat can be executed without adding the
+ directory to the %PATH%. (Python itself works this way.)
+
+ From Anonymous:
+
+ - Fix looking for default paths in Visual Studio 8.0 (and later).
+
+ - Add -lm to the list of default D libraries for linking.
+
+ From Matt Doar:
+
+ - Provide a more complete write-your-own-Scanner example in the man page.
+
+ From Ralf W. Grosse-Kunstleve:
+
+ - Contributed upstream Python change to our copied subprocess.py module
+ for more efficient standard input processing.
+
+ From Steven Knight:
+
+ - Fix the Node.FS.Base.rel_path() method when the two nodes are on
+ different drive letters. (This caused an infinite loop when
+ trying to write .sconsign files.)
+
+ - Fully support Scanners that use a dictionary to map file suffixes
+ to other scanners.
+
+ - Support delayed evaluation of the $SPAWN variable to allow selection
+ of a function via ${} string expansions.
+
+ - Add --srcdir as a synonym for -Y/--repository.
+
+ - Document limitations of #include "file.h" with Repository().
+
+ - Fix use of a toolpath under the source directory of a BuildDir().
+
+ - Fix env.Install() with a file name portion that begins with '#'.
+
+ - Fix ParseConfig()'s handling of multiple options in a string that's
+ replaced a *FLAGS construction variable.
+
+ - Have the C++ tools initialize common C compilation variables ($CCFLAGS,
+ $SHCCFLAGS and $_CCCOMCOM) even if the 'cc' Tool isn't loaded.
+
+ From Leanid Nazdrynau:
+
+ - Fix detection of Java anonymous classes if a newline precedes the
+ opening brace.
+
+ From Gary Oberbrunner:
+
+ - Document use of ${} to execute arbitrary Python code.
+
+ - Add support for:
+ 1) automatically adding a site_scons subdirectory (in the top-level
+ SConstruct directory) to sys.path (PYTHONPATH);
+ 2) automatically importing site_scons/site_init.py;
+ 3) automatically adding site_scons/site_tools to the toolpath.
+
+ From John Pye:
+
+ - Change ParseConfig() to preserve white space in arguments passed in
+ as a list.
+
+ From a smith:
+
+ - Fix adding explicitly-named Java inner class files (and any
+ other file names that may contain a '$') to Jar files.
+
+ From David Vitek:
+
+ - Add a NoCache() function to mark targets as unsuitable for propagating
+ to (or retrieving from) a CacheDir().
+
+ From Ben Webb:
+
+ - If the swig -noproxy option is used, it won't generate a .py file,
+ so don't emit it as a target that we expect to be built.
+
+
+
+RELEASE 0.96.94 - Sun, 07 Jan 2007 18:36:20 -0600
+
+ NOTE: This is a pre-release of 0.97 for testing purposes.
+
+ From Anonymous:
+
+ - Allow arbitrary white space after a SWIG %module declaration.
+
+ From Paul:
+
+ - When compiling resources under MinGW, make sure there's a space
+ between the --include-dir option and its argument.
+
+ From Jay Kint:
+
+ - Alleviate long command line issues on Windows by executing command
+ lines directly via os.spawnv() if the command line doesn't need
+ shell interpretation (has no pipes, redirection, etc.).
+
+ From Walter Franzini:
+
+ - Exclude additional Debian packaging files from the copyright check.
+
+ From Fawad Halim:
+
+ - Handle the conflict between the impending Python 2.6 'as' keyword
+ and our Tool/as.py module name.
+
+ From Steven Knight:
+
+ - Speed up the Node.FS.Dir.rel_path() method used to generate path names
+ that get put into the .sconsign* file(s).
+
+ - Optimize Node.FS.Base.get_suffix() by computing the suffix once, up
+ front, when we set the Node's name. (Duh...)
+
+ - Reduce the Memoizer's responsibilities to simply counting hits and
+ misses when the --debug=memoizer option is used, not to actually
+ handling the key calculation and memoization itself. This speeds
+ up some configurations significantly, and should cause no functional
+ differences.
+
+ - Add a new scons-time script with subcommands for generating
+ consistent timing output from SCons configurations, extracting
+ various information from those timings, and displaying them in
+ different formats.
+
+ - Reduce some unnecessary stat() calls from on-disk entry type checks.
+
+ - Fix SideEffect() when used with -j, which was badly broken in 0.96.93.
+
+ - Propagate TypeError exceptions when evaluating construction variable
+ expansions up the stack, so users can see what's going on.
+
+ - When disambiguating a Node.FS.Entry into a Dir or File, don't look
+ in the on-disk source directory until we've confirmed there's no
+ on-disk entry locally and there *is* one in the srcdir. This avoids
+ creating a phantom Node that can interfere with dependencies on
+ directory contents.
+
+ - Add an AllowSubstExceptions() function that gives the SConscript
+ files control over what exceptions cause a string to expand to ''
+ vs. terminating processing with an error.
+
+ - Allow the f90.py and f95.py Tool modules to compile earlier source
+ source files of earlier Fortran version.
+
+ - Fix storing signatures of files retrieved from CacheDir() so they're
+ correctly identified as up-to-date next invocation.
+
+ - Make sure lists of computed source suffixes cached by Builder objects
+ don't persist across changes to the list of source Builders (so the
+ addition of suffixes like .ui by the qt.py Tool module take effect).
+
+ - Enhance the bootstrap.py script to allow it to be used to execute
+ SCons more easily from a checked-out source tree.
+
+ From Ben Leslie:
+
+ - Fix post-Memoizer value caching misspellings in Node.FS._doLookup().
+
+ From Rob Managan, Dmitry Mikhin and Joel B. Mohler:
+
+ - Handle TeX/LaTeX files in subdirectories by changing directory
+ before invoking TeX/LaTeX.
+
+ - Scan LaTeX files for \bibliography lines.
+
+ - Support multiple file names in a "\bibliography{file1,file2}" string.
+
+ - Handle TeX warnings about undefined citations.
+
+ - Support re-running LaTeX if necessary due to a Table of Contents.
+
+ From Dmitry Mikhin:
+
+ - Return LaTeX if "Rerun to get citations correct" shows up on the next
+ line after the "Warning:" string.
+
+ From Gary Oberbrunner:
+
+ - Add #include lines to fix portability issues in two tests.
+
+ - Eliminate some unnecessary os.path.normpath() calls.
+
+ - Add a $CFLAGS variable for C-specific options, leaving $CCFLAGS
+ for options common to C and C++.
+
+ From Tom Parker:
+
+ - Have the error message print the missing file that Qt can't find.
+
+ From John Pye:
+
+ - Fix env.MergeFlags() appending to construction variable value of None.
+
+ From Steve Robbins:
+
+ - Fix the "sconsign" script when the .sconsign.dblite file is explicitly
+ specified on the command line (and not intuited from the old way of
+ calling it with just ".sconsign").
+
+ From Jose Pablo Ezequiel "Pupeno" Fernandez Silva:
+
+ - Give the 'lex' tool knowledge of the additional target files produced
+ by the flex "--header-file=" and "--tables-file=" options.
+
+ - Give the 'yacc' tool knowledge of the additional target files produced
+ by the bison "-g", "--defines=" and "--graph=" options.
+
+ - Generate intermediate files with Objective C file suffixes (.m) when
+ the lex and yacc source files have appropriate suffixes (.lm and .ym).
+
+ From Sohail Somain:
+
+ - Have the mslink.py Tool only look for a 'link' executable on Windows
+ systems.
+
+ From Vaclav Smilauer:
+
+ - Add support for a "srcdir" keyword argument when calling a Builder,
+ which will add a srcdir prefix to all non-relative string sources.
+
+ From Jonathan Ultis:
+
+ - Allow Options converters to take the construction environment as
+ an optional argument.
+
+
+
+RELEASE 0.96.93 - Mon, 06 Nov 2006 00:44:11 -0600
+
+ NOTE: This is a pre-release of 0.97 for testing purposes.
+
+ From Anonymous:
+
+ - Allow Python Value Nodes to be Builder targets.
+
+ From Matthias:
+
+ - Only filter Visual Studio common filename prefixes on complete
+ directory names.
+
+ From Chad Austin:
+
+ - Fix the build of the SCons documentation on systems that don't
+ have "python" in the $PATH.
+
+ From Ken Boortz:
+
+ - Enhance ParseConfig() to recognize options that begin with '+'.
+
+ From John Calcote, Elliot Murphy:
+
+ - Document ways to override the CCPDBFLAGS variable to use the
+ Microsoft linker's /Zi option instead of the default /Z7.
+
+ From Christopher Drexler:
+
+ - Make SCons aware bibtex must be called if any \include files
+ cause creation of a bibliography.
+
+ - Make SCons aware that "\bilbiography" in TeX source files means
+ that related .bbl and .blg bibliography files will be created.
+ (NOTE: This still needs to search for the string in \include files.)
+
+ From David Gruener:
+
+ - Fix inconsistent handling of Action strfunction arguments.
+
+ - Preserve white space in display Action strfunction strings.
+
+ From James Y. Knight and Gerard Patel:
+
+ - Support creation of shared object files from assembly language.
+
+ From Steven Knight:
+
+ - Speed up the Taskmaster significantly by avoiding unnecessary
+ re-scans of Nodes to find out if there's work to be done, having it
+ track the currently-executed top-level target directly and not
+ through its presence on the target list, and eliminating some other
+ minor list(s), method(s) and manipulation.
+
+ - Fix the expansion of $TARGET and $SOURCE in the expansion of
+ $INSTALLSTR displayed for non-environment calls to InstallAs().
+
+ - Fix the ability to have an Alias() call refer to a directory
+ name that's not identified as a directory until later.
+
+ - Enhance runtest.py with an option to use QMTest as the harness.
+ This will become the default behavior as we add more functionality
+ to the QMTest side.
+
+ - Let linking on mingw use the default function that chooses $CC (gcc)
+ or $CXX (g++) depending on whether there are any C++ source files.
+
+ - Work around a bug in early versions of the Python 2.4 profile module
+ that caused the --profile= option to fail.
+
+ - Only call Options validators and converters once when initializing a
+ construction environment.
+
+ - Fix the ability of env.Append() and env.Prepend(), in all known Python
+ versions, to handle different input value types when the construction
+ variable being updated is a dictionary.
+
+ - Add a --cache-debug option for information about what files it's
+ looking for in a CacheDir().
+
+ - Document the difference in construction variable expansion between
+ {Action,Builder}() and env.{Action,Builder}().
+
+ - Change the name of env.Copy() to env.Clone(), keeping the old name
+ around for backwards compatibility (with the intention of eventually
+ phasing it out to avoid confusion with the Copy() Action factory).
+
+ From Arve Knudsen:
+
+ - Support cleaning and scanning SWIG-generated files.
+
+ From Carsten Koch:
+
+ - Allow selection of Visual Studio version by setting $MSVS_VERSION
+ after construction environment initialization.
+
+ From Jean-Baptiste Lab:
+
+ - Try using zipimport if we can't import Tool or Platform modules
+ using the normal "imp" module. This allows SCons to be packaged
+ using py2exe's all-in-one-zip-file approach.
+
+ From Ben Liblit:
+
+ - Do not re-scan files if the scanner returns no implicit dependencies.
+
+ From Sanjoy Mahajan:
+
+ - Change use of $SOURCES to $SOURCE in all TeX-related Tool modules.
+
+ From Joel B. Mohler:
+
+ - Make SCons aware that "\makeindex" in TeX source files means that
+ related .ilg, .ind and .idx index files will be created.
+ (NOTE: This still needs to search for the string in \include files.)
+
+ - Prevent scanning the TeX .aux file for additional files from
+ trying to remove it twice when the -c option is used.
+
+ From Leanid Nazdrynau:
+
+ - Give the MSVC RES (resource) Builder a src_builder list and a .rc
+ src_suffix so other builders can generate .rc files.
+
+ From Matthew A. Nicholson:
+
+ - Enhance Install() and InstallAs() to handle directory trees as sources.
+
+ From Jan Nijtmans:
+
+ - Don't use the -fPIC flag when using gcc on Windows (e.g. MinGW).
+
+ From Greg Noel:
+
+ - Add an env.ParseFlags() method that provides separate logic for
+ parsing GNU tool chain flags into a dictionary.
+
+ - Add an env.MergeFlags() method to apply an arbitrary dictionary
+ of flags to a construction environment's variables.
+
+ From Gary Oberbrunner:
+
+ - Fix parsing tripartite Intel C compiler version numbers on Linux.
+
+ - Extend the ParseConfig() function to recognize -arch and
+ -isysroot options.
+
+ - Have the error message list the known suffixes when a Builder call
+ can't build a source file with an unknown suffix.
+
+ From Karol Pietrzak:
+
+ - Avoid recursive calls to main() in the program snippet used by the
+ SConf subsystem to test linking against libraries. This changes the
+ default behavior of CheckLib() and CheckLibWithHeader() to print
+ "Checking for C library foo..." instead of "Checking for main()
+ in C library foo...".
+
+ From John Pye:
+
+ - Throw an exception if a command called by ParseConfig() or
+ ParseFlags() returns an error.
+
+ From Stefan Seefeld:
+
+ - Initial infrastructure for running SCons tests under QMTest.
+
+ From Sohail Somani:
+
+ - Fix tests that fail due to gcc warnings.
+
+ From Dobes Vandermeer:
+
+ - In stack traces, print the full paths of SConscript files.
+
+ From Atul Varma:
+
+ - Fix detection of Visual C++ Express Edition.
+
+ From Dobes Vandermeer:
+
+ - Let the src_dir option to the SConscript() function affect all the
+ the source file paths, instead of treating all source files paths
+ as relative to the SConscript directory itself.
+
+ From Nicolas Vigier:
+
+ - Fix finding Fortran modules in build directories.
+
+ - Fix use of BuildDir() when the source file in the source directory
+ is a symlink with a relative path.
+
+ From Edward Wang:
+
+ - Fix the Memoizer when the SCons Python modules are executed from
+ .pyo files at different locations from where they were compiled.
+
+ From Johan Zander:
+
+ - Fix missing os.path.join() when constructing the $FRAMEWORKSDKDIR/bin.
+
+
+
+RELEASE 0.96.92 - Mon, 10 Apr 2006 21:08:22 -0400
+
+ NOTE: This was a pre-release of 0.97 for testing purposes.
+
+ From Anonymous:
+
+ - Fix the intelc.py Tool module to not throw an exception if the
+ only installed version is something other than ia32.
+
+ - Set $CCVERSION when using gcc.
+
+ From Matthias:
+
+ - Support generating project and solution files for Microsoft
+ Visual Studio version 8.
+
+ - Support generating more than one project file for a Microsoft
+ Visual Studio solution file.
+
+ - Add support for a support "runfile" parameter to Microsoft
+ Visual Studio project file creation.
+
+ - Put the project GUID, not the solution GUID, in the right spot
+ in the solution file.
+
+ From Erling Andersen:
+
+ - Fix interpretation of Node.FS objects wrapped in Proxy instances,
+ allowing expansion of things like ${File(TARGET)} in command lines.
+
+ From Stanislav Baranov:
+
+ - Add a separate MSVSSolution() Builder, with support for the
+ following new construction variables: $MSVSBUILDCOM, $MSVSCLEANCOM,
+ $MSVSENCODING, $MSVSREBUILDCOM, $MSVSSCONS, $MSVSSCONSCOM,
+ $MSVSSCONSFLAGS, $MSVSSCONSCRIPT and $MSVSSOLUTIONCOM.
+
+ From Ralph W. Grosse-Kunstleve and Patrick Mezard:
+
+ - Remove unneceesary (and incorrect) SCons.Util strings on some function
+ calls in SCons.Util.
+
+ From Bob Halley:
+
+ - Fix C/C++ compiler selection on AIX to not always use the external $CC
+ environment variable.
+
+ From August Hörandl:
+
+ - Add a scanner for \include and \import files, with support for
+ searching a directory list in $TEXINPUTS (imported from the external
+ environment).
+
+ - Support $MAKEINDEX, $MAKEINDEXCOM, $MAKEINDEXCOMSTR and
+ $MAKEINDEXFLAGS for generating indices from .idx files.
+
+ From Steven Johnson:
+
+ - Add a NoClean() Environment method and function to override removal
+ of targets during a -c clean, including documentation and tests.
+
+ From Steven Knight:
+
+ - Check for whether files exist on disk by listing the directory
+ contents, not calling os.path.exists() file by file. This is
+ somewhat more efficient in general, and may be significantly
+ more efficient on Windows.
+
+ - Minor speedups in the internal is_Dict(), is_List() and is_String()
+ functions.
+
+ - Fix a signature refactoring bug that caused Qt header files to
+ get re-generated every time.
+
+ - Don't fail when writing signatures if the .sconsign.dblite file is
+ owned by a different user (e.g. root) from a previous run.
+
+ - When deleting variables from stacked OverrideEnvironments, don't
+ throw a KeyError if we were able to delte the variable from any
+ Environment in the stack.
+
+ - Get rid of the last indentation tabs in the SCons source files and
+ add -tt to the Python invocations in the packaging build and the
+ tests so they don't creep back in.
+
+ - In Visual Studio project files, put quotes around the -C directory
+ so everything works even if the path has spaces in it.
+
+ - The Intel Fortran compiler uses -object:$TARGET, not "-o $TARGET",
+ when building object files on Windows. Have the the ifort Tool
+ modify the default command lines appropriately.
+
+ - Document the --debug=explain option in the man page. (How did we
+ miss this?)
+
+ - Add a $LATEXRETRIES variable to allow configuration of the number of
+ times LaTex can be re-called to try to resolve undefined references.
+
+ - Change the order of the arguments to Configure.Checklib() to match
+ the documentation.
+
+ - Handle signature calculation properly when the Python function used
+ for a FunctionAction is an object method.
+
+ - On Windows, assume that absolute path names without a drive letter
+ refer to the drive on which the SConstruct file lives.
+
+ - Add /usr/ccs/bin to the end of the the default external execution
+ PATH on Solaris.
+
+ - Add $PKGCHK and $PKGINFO variables for use on Solaris when searching
+ for the SunPRO C++ compiler. Make the default value for $PKGCHK
+ be /usr/sbin/pgkchk (since /usr/sbin isn't usually on the external
+ execution $PATH).
+
+ - Fix a man page example of overriding variables when calling
+ SharedLibrary() to also set the $LIBSUFFIXES variable.
+
+ - Add a --taskmastertrace=FILE option to give some insight on how
+ the taskmaster decides what Node to build next.
+
+ - Changed the names of the old $WIN32DEFPREFIX, $WIN32DEFSUFFIX,
+ $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX construction variables to
+ new $WINDOWSDEFPREFIX, $WINDOWSDEFSUFFIX, $WINDOWSDLLPREFIX and
+ $WINDOWSIMPLIBPREFIX construction variables. The old names are now
+ deprecated, but preserved for backwards compatibility.
+
+ - Fix (?) a runtest.py hang on Windows when the --xml option is used.
+
+ - Change the message when an error occurs trying to interact with the
+ file system to report the target(s) in square brackets (as before) and
+ the actual file or directory that encountered the error afterwards.
+
+ From Chen Lee:
+
+ - Add x64 support for Microsoft Visual Studio 8.
+
+ From Baptiste Lepilleur:
+
+ - Support the --debug=memory option on Windows when the Python version
+ has the win32process and win32api modules.
+
+ - Add support for Visual Studio 2005 Pro.
+
+ - Fix portability issues in various tests: test/Case.py,
+ Test/Java/{JAR,JARCHDIR,JARFLAGS,JAVAC,JAVACFLAGS,JAVAH,RMIC}.py,
+ test/MSVS/vs-{6.0,7.0,7.1,8.0}-exec.py,
+ test/Repository/{Java,JavaH,RMIC}.py,
+ test/QT/{generated-ui,installed,up-to-date,warnings}.py,
+ test/ZIP/ZIP.py.
+
+ - Ignore pkgchk errors on Solaris when searching for the C++ compiler.
+
+ - Speed up the SCons/EnvironmentTests.py unit tests.
+
+ - Add a --verbose= option to runtest.py to print executed commands
+ and their output at various levels.
+
+ From Christian Maaser:
+
+ - Add support for Visual Studio Express Editions.
+
+ - Add support for Visual Studio 8 *.manifest files, includng
+ new $WINDOWS_INSERT_MANIFEST, $WINDOWSPROGMANIFESTSUFFIX,
+ $WINDOWSPROGMANIFESTPREFIX, $WINDOWSPROGMANIFESTSUFFIX,
+ $WINDOWSSHLIBMANIFESTPREFIX and $WINDOWSSHLIBMANIFESTSUFFIX
+ construction variables.
+
+ From Adam MacBeth:
+
+ - Fix detection of additional Java inner classes following use of a
+ "new" keyword inside an inner class.
+
+ From Sanjoy Mahajan:
+
+ - Correct TeX-related command lines to just $SOURCE, not $SOURCES
+
+ From Patrick Mezard:
+
+ - Execute build commands for a command-line target if any of the
+ files built along with the target is out of date or non-existent,
+ not just if the command-line target itself is out of date.
+
+ - Fix the -n option when used with -c to print all of the targets
+ that will be removed for a multi-target Builder call.
+
+ - If there's no file in the source directory, make sure there isn't
+ one in the build directory, too, to avoid dangling files left
+ over from previous runs when a source file is removed.
+
+ - Allow AppendUnique() and PrependUnique() to append strings (and
+ other atomic objects) to lists.
+
+ From Joel B. Mohler:
+
+ - Extend latex.py, pdflatex.py, pdftex.py and tex.py so that building
+ from both TeX and LaTeX files uses the same logic to call $BIBTEX
+ when it's necessary, to call $MAKEINDEX when it's necessary, and to
+ call $TEX or $LATEX multiple times to handle undefined references.
+
+ - Add an emitter to the various TeX builders so that the generated
+ .aux and .log files also get deleted by the -c option.
+
+ From Leanid Nazdrynau:
+
+ - Fix the Qt UIC scanner to work with generated .ui files (by using
+ the FindFile() function instead of checking by-hand for the file).
+
+ From Jan Nieuwenhuizen:
+
+ - Fix a problem with interpreting quoted argument lists on command lines.
+
+ From Greg Noel:
+
+ - Add /sw/bin to the default execution PATH on Mac OS X.
+
+ From Kian Win Ong:
+
+ - When building a .jar file and there is a $JARCHDIR, put the -C
+ in front of each .class file on the command line.
+
+ - Recognize the Java 1.5 enum keyword.
+
+ From Asfand Yar Qazi:
+
+ - Add /opt/bin to the default execution PATH on all POSIX platforms
+ (between /usr/local/bin and /bin).
+
+ From Jon Rafkind:
+
+ - Fix the use of Configure() contexts from nested subsidiary
+ SConscript files.
+
+ From Christoph Schulz:
+
+ - Add support for $CONFIGUREDIR and $CONFIGURELOG variables to control
+ the directory and logs for configuration tests.
+
+ - Add support for a $INSTALLSTR variable.
+
+ - Add support for $RANLIBCOM and $RANLIBCOMSTR variables (which fixes
+ a bug when setting $ARCOMSTR).
+
+ From Amir Szekely:
+
+ - Add use of $CPPDEFINES to $RCCOM (resource file compilation) on MinGW.
+
+ From Erick Tryzelaar:
+
+ - Fix the error message when trying to report that a given option is
+ not gettable/settable from an SConscript file.
+
+ From Dobes Vandermeer:
+
+ - Add support for SCC and other settings in Microsoft Visual
+ Studio project and solution files: $MSVS_PROJECT_BASE_PATH,
+ $MSVS_PROJECT_GUID, $MSVS_SCC_AUX_PATH, $MSVS_SCC_LOCAL_PATH,
+ $MSVS_SCC_PROJECT_NAME, $MSVS_SCC_PROVIDER,
+
+ - Add support for using a $SCONS_HOME variable (imported from the
+ external environment, or settable internally) to put a shortened
+ SCons execution line in the Visual Studio project file.
+
+ From David J. Van Maren:
+
+ - Only filter common prefixes from source files names in Visual Studio
+ project files if the prefix is a complete (sub)directory name.
+
+ From Thad Ward:
+
+ - If $MSVSVERSIONS is already set, don't overwrite it with
+ information from the registry.
+
+
+
+RELEASE 0.96.91 - Thu, 08 Sep 2005 07:18:23 -0400
+
+ NOTE: This was a pre-release of 0.97 for testing purposes.
+
+ From Chad Austin:
+
+ - Have the environment store the toolpath and re-use it to find Tools
+ modules during later Copy() or Tool() calls (unless overridden).
+
+ - Normalize the directory path names in SConsignFile() database
+ files so the same signature file can interoperate on Windows and
+ non-Windows systems.
+
+ - Make --debug=stacktrace print a stacktrace when a UserError is thrown.
+
+ - Remove an old, erroneous cut-and-paste comment in Scanner/Dir.py.
+
+ From Stanislav Baranov:
+
+ - Make it possible to support with custom Alias (sub-)classes.
+
+ - Allow Builders to take empty source lists when called.
+
+ - Allow access to both TARGET and SOURCE in $*PATH expansions.
+
+ - Allow SConscript files to modify BUILD_TARGETS.
+
+ From Timothee Besset:
+
+ - Add support for Objective C/C++ .m and .mm file suffixes (for
+ Mac OS X).
+
+ From Charles Crain
+
+ - Fix the PharLap linkloc.py module to use target+source arguments
+ when calling env.subst().
+
+ From Bjorn Eriksson:
+
+ - Fix an incorrect Command() keyword argument in the man page.
+
+ - Add a $TEMPFILEPREFIX variable to control the prefix or flag used
+ to pass a long-command-line-execution tempfile to a command.
+
+ From Steven Knight:
+
+ - Enhanced the SCons setup.py script to install man pages on
+ UNIX/Linux systems.
+
+ - Add support for an Options.FormatOptionHelpText() method that can
+ be overridden to customize the format of Options help text.
+
+ - Add a global name for the Entry class (which had already been
+ documented).
+
+ - Fix re-scanning of generated source files for implicit dependencies
+ when the -j option is used.
+
+ - Fix a dependency problem that caused $LIBS scans to not be added
+ to all of the targets in a multiple-target builder call, which
+ could cause out-of-order builds when the -j option is used.
+
+ - Store the paths of source files and dependencies in the .sconsign*
+ file(s) relative to the target's directory, not relative to the
+ top-level SConstruct directory. This starts to make it possible to
+ subdivide the dependency tree arbitrarily by putting an SConstruct
+ file in every directory and using content signatures.
+
+ - Add support for $YACCHFILESUFFIX and $YACCHXXFILESUFFIX variables
+ that accomodate parser generators that write header files to a
+ different suffix than the hard-coded .hpp when the -d option is used.
+
+ - The default behavior is now to store signature information in a
+ single .sconsign.dblite file in the top-level SConstruct directory.
+ The old behavior of a separate .sconsign file in each directory can
+ be specified by calling SConsignFile(None).
+
+ - Remove line number byte codes within the signature calculation
+ of Python function actions, so that changing the location of an
+ otherwise unmodified Python function doesn't cause rebuilds.
+
+ - Fix AddPreAction() and AddPostAction() when an action has more than
+ one target file: attach the actions to the Executor, not the Node.
+
+ - Allow the source directory of a BuildDir / build_dir to be outside
+ of the top-level SConstruct directory tree.
+
+ - Add a --debug=nomemoizer option that disables the Memoizer for clearer
+ looks at the counts and profiles of the underlying function calls,
+ not the Memoizer wrappers.
+
+ - Print various --debug= stats even if we exit early (e.g. using -h).
+
+ - Really only use the cached content signature value if the file
+ is older than --max-drift, not just if --max-drift is set.
+
+ - Remove support for conversion from old (pre 0.96) .sconsign formats.
+
+ - Add support for a --diskcheck option to enable or disable various
+ on-disk checks: that File and Dir nodes match on-disk entries;
+ whether an RCS file exists for a missing source file; whether an
+ SCCS file exists for a missing source file.
+
+ - Add a --raw argument to the sconsign script, so it can print a
+ raw representation of each entry's NodeInfo dictionary.
+
+ - Add the 'f90' and 'f95' tools to the list of Fortran compilers
+ searched for by default.
+
+ - Add the +Z option by default when compiling shared objects on
+ HP-UX.
+
+ From Chen Lee:
+
+ - Handle Visual Studio project and solution files in Unicode.
+
+ From Sanjoy Mahajan:
+
+ - Fix a bad use of Copy() in an example in the man page, and a
+ bad regular expression example in the man page and User's Guide.
+
+ From Shannon Mann:
+
+ - Have the Visual Studio project file(s) echo "Starting SCons" before
+ executing SCons, mainly to work around a quote-stripping bug in
+ (some versions of?) the Windows cmd command executor.
+
+ From Georg Mischler:
+
+ - Remove the space after the -o option when invoking the Borland
+ BCC compiler; some versions apparently require that the file name
+ argument be concatenated with the option.
+
+ From Leanid Nazdrynau:
+
+ - Fix the Java parser's handling of backslashes in strings.
+
+ From Greg Noel:
+
+ - Add construction variables to support frameworks on Mac OS X:
+ $FRAMEWORKS, $FRAMEWORKPREFIX, $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX.
+
+ - Re-order link lines so the -o option always comes right after the
+ command name.
+
+ From Gary Oberbrunner:
+
+ - Add support for Intel C++ beta 9.0 (both 32 and 64 bit versions).
+
+ - Document the new $FRAMEWORK* variables for Mac OS X.
+
+ From Karol Pietrzak:
+
+ - Add $RPATH (-R) support to the Sun linker Tool (sunlink).
+
+ - Add a description of env.subst() to the man page.
+
+ From Chris Prince:
+
+ - Look in the right directory, not always the local directory, for a
+ same-named file or directory conflict on disk.
+
+ - On Windows, preserve the external environment's %SYSTEMDRIVE%
+ variable, too.
+
+ From Craig Scott:
+
+ - Have the Fortran module emitter look for Fortan modules to be created
+ relative to $FORTRANMODDIR, not the top-level directory.
+
+ - When saving Options to a file, run default values through the
+ converter before comparing them with the set values. This correctly
+ suppresses Boolean Option values from getting written to the saved
+ file when they're one of the many synonyms for a default True or
+ False value.
+
+ - Fix the Fortran Scanner's ability to handle a module being used
+ in the same file in which it is defined.
+
+ From Steve-o:
+
+ - Add the -KPIC option by default when compiling shared objects on
+ Solaris.
+
+ - Change the default suffix for Solaris objects to .o, to conform to
+ Sun WorkShop's expectations. Change the profix to so_ so they can
+ still be differentiated from static objects in the same directory.
+
+ From Amir Szekely:
+
+ - When calling the resource compiler on MinGW, add --include-dir and
+ the source directory so it finds the source file.
+
+ - Update EnsureSConsVersion() to support revision numbers.
+
+ From Greg Ward:
+
+ - Fix a misplaced line in the man page.
+
+
+
+RELEASE 0.96.90 - Tue, 15 Feb 2005 21:21:12 +0000
+
+ NOTE: This was a pre-release of 0.97 for testing purposes.
+
+ From Anonymous:
+
+ - Fix Java parsing to avoid erroneously identifying a new array
+ of class instances as an anonymous inner class.
+
+ - Fix a typo in the man page description of PathIsDirCreate.
+
+ From Chad Austin:
+
+ - Allow Help() to be called multiple times, appending to the help
+ text each call.
+
+ - Allow Tools found on a toolpath to import Python modules from
+ their local directory.
+
+ From Steve Christensen:
+
+ - Handle exceptions from Python functions as build actions.
+
+ - Add a set of canned PathOption validators: PathExists (the default),
+ PathIsFile, PathIsDir and PathIsDirCreate.
+
+ From Matthew Doar:
+
+ - Add support for .lex and .yacc file suffixes for Lex and Yacc files.
+
+ From Eric Frias:
+
+ - Huge performance improvement: wrap the tuples representing an
+ include path in an object, so that the time it takes to hash the
+ path doesn't grow porportionally to the length of the path.
+
+ From Gottfried Ganssauge:
+
+ - Fix SCons on SuSE/AMD-64 Linux by having the wrapper script also
+ check for the build engine in the parent directory of the Python
+ library directory (/usr/lib64 instead of /usr/lib).
+
+ From Stephen Kennedy:
+
+ - Speed up writing the .sconsign file at the end of a run by only
+ calling sync() once at the end, not after every entry.
+
+ From Steven Knight:
+
+ - When compiling with Microsoft Visual Studio, don't include the ATL and
+ MFC directories in the default INCLUDE and LIB environment variables.
+
+ - Remove the following deprecated features: the ParseConfig()
+ global function (deprecated in 0.93); the misspelled "validater"
+ keyword to the Options.Add() method (deprecated in 0.91); the
+ SetBuildSignatureType(), SetContentSignatureType(), SetJobs() and
+ GetJobs() global functions (deprecated in 0.14).
+
+ - Fix problems with corrupting the .sconsign.dblite file when
+ interrupting builds by writing to a temporary file and renaming,
+ not writing the file directly.
+
+ - Fix a 0.96 regression where when running with -k, targets built from
+ walking dependencies later on the command line would not realize
+ that a dependency had failed an earlier build attempt, and would
+ try to rebuild the dependent targets.
+
+ - Change the final messages when using -k and errors occur from
+ "{building,cleaning} terminated because of errors" to "done
+ {building,cleaning} targets (errors occurred during {build,clean})."
+
+ - Allow Configure.CheckFunc() to take an optional header argument
+ (already supported by Conftest.py) to specify text at the top of
+ the compiled test file.
+
+ - Fix the --debug=explain output when a Python function action changed
+ so it prints a meaningful string, not the binary representation of
+ the function contents.
+
+ - Allow a ListOption's default value(s) to be a Python list of specified
+ values, not just a string containing a comma-separated list of names.
+
+ - Add a ParseDepends() function that will parse up a list of explicit
+ dependencies from a "make depend" style file.
+
+ - Support the ability to change directory when executing an Action
+ through "chdir" keyword arguments to Action and Builder creation
+ and calls.
+
+ - Fix handling of Action ojects (and other callables that don't match
+ our calling arguments) in construction variable expansions.
+
+ - On Win32, install scons.bat in the Python directory when installing
+ from setup.py. (The bdist_wininst installer was already doing this.)
+
+ - Fix env.SConscript() when called with a list of SConscipt files.
+ (The SConscript() global function already worked properly.)
+
+ - Add a missing newline to the end of the --debug=explain "unknown
+ reasons" message.
+
+ - Enhance ParseConfig() to work properly for spaces in between the -I,
+ -L and -l options and their arguments.
+
+ - Packaging build fix: Rebuild the files that are use to report the
+ --version of SCons whenever the development version number changes.
+
+ - Fix the ability to specify a target_factory of Dir() to a Builder,
+ which the default create-a-directory Builder was interfering with.
+
+ - Mark a directory as built if it's created as part of the preparation
+ for another target, to avoid trying to build it again when it comes
+ up in the target list.
+
+ - Allow a function with the right calling signature to be put directly
+ in an Environment's BUILDERS dictionary, making for easier creation
+ and use of wrappers (pseudo-Builders) that call other Builders.
+
+ - On Python 2.x, wrap lists of Nodes returned by Builders in a UserList
+ object that adds a method that makes str() object return a string
+ with all of the Nodes expanded to their path names. (Builders under
+ Python 1.5.2 still return lists to avoid TypeErrors when trying
+ to extend() list, so Python 1.5.2 doesn't get pretty-printing of Node
+ lists, but everything should still function.)
+
+ - Allow Aliases to have actions that will be executed whenever
+ any of the expanded Alias targets are out of date.
+
+ - Fix expansion of env.Command() overrides within target and
+ source file names.
+
+ - Support easier customization of what's displayed by various default
+ actions by adding lots of new construction variables: $ARCOMSTR,
+ $ASCOMSTR, $ASPPCOMSTR, $BIBTEXCOMSTR, $BITKEEPERCOMSTR, $CCCOMSTR,
+ $CVSCOMSTR, $CXXCOMSTR, $DCOMSTR, $DVIPDFCOMSTR, $F77COMSTR,
+ $F90COMSTR, $F95COMSTR, $FORTRANCOMSTR, $GSCOMSTR, $JARCOMSTR,
+ $JAVACCOMSTR, $JAVAHCOMSTR, $LATEXCOMSTR, $LEXCOMSTR, $LINKCOMSTR,
+ $M4COMSTR, $MIDLCOMSTR, $P4COMSTR, $PCHCOMSTR, $PDFLATEXCOMSTR,
+ $PDFTEXCOMSTR, $PSCOMSTR, $QT_MOCFROMCXXCOMSTR, $QT_MOCFROMHCOMSTR,
+ $QT_UICCOMSTR, $RCCOMSTR, $REGSVRCOMSTR, $RCS_COCOMSTR, $RMICCOMSTR,
+ $SCCSCOMSTR, $SHCCCOMSTR, $SHCXXCOMSTR, $SHF77COMSTR, $SHF90COMSTR,
+ $SHF95COMSTR, $SHFORTRANCOMSTR, $SHLINKCOMSTR, $SWIGCOMSTR,
+ $TARCOMSTR, $TEXCOMSTR, $YACCCOMSTR and $ZIPCOMSTR.
+
+ - Add an optional "map" keyword argument to ListOption() that takes a
+ dictionary to map user-specified values to legal values from the list
+ (like EnumOption() already doee).
+
+ - Add specific exceptions to try:-except: blocks without any listed,
+ so that they won't catch and mask keyboard interrupts.
+
+ - Make --debug={tree,dtree,stree} print something even when there's
+ a build failure.
+
+ - Fix how Scanners sort the found dependencies so that it doesn't
+ matter whether the dependency file is in a Repository or not.
+ This may cause recompilations upon upgrade to this version.
+
+ - Make AlwaysBuild() work with Alias and Python value Nodes (making
+ it much simpler to support aliases like "clean" that just invoke
+ an arbitrary action).
+
+ - Have env.ParseConfig() use AppendUnique() by default to suppress
+ duplicate entries from multiple calls. Add a "unique" keyword
+ argument to allow the old behavior to be specified.
+
+ - Allow the library modules imported by an SConscript file to get at
+ all of the normally-available global functions and variables by saying
+ "from SCons.Script import *".
+
+ - Add a --debug=memoizer option to print Memoizer hit/mass statistics.
+
+ - Allow more than one --debug= option to be set at a time.
+
+ - Change --debug=count to report object counts before and after
+ reading SConscript files and before and after building targets.
+
+ - Change --debug=memory output to line up the numbers and to better
+ match (more or less) the headers on the --debug=count columns.
+
+ - Speed things up when there are lists of targets and/or sources by
+ getting rid of some N^2 walks of the lists involved.
+
+ - Cache evaluation of LazyActions so we don't create a new object
+ for each invocation.
+
+ - When scanning, don't create Nodes for include files that don't
+ actually exist on disk.
+
+ - Make supported global variables CScanner, DScanner, ProgramScanner and
+ SourceFileScanner. Make SourceFileScanner.add_scanner() a supported
+ part of the public interface. Keep the old SCons.Defaults.*Scan names
+ around for a while longer since some people were already using them.
+
+ - By default, don't scan directories for on-disk files. Add a
+ DirScanner global scanner that can be used in Builders or Command()
+ calls that want source directory trees scanned for on-disk changes.
+ Have the Tar() and Zip() Builders use the new DirScanner to preserve
+ the behavior of rebuilding a .tar or .zip file if any file or
+ directory under a source tree changes. Add Command() support for
+ a source_scanner keyword argument to Command() that can be set to
+ DirScanner to get this behavior.
+
+ - Documentation changes: Explain that $CXXFLAGS contains $CCFLAGS
+ by default. Fix a bad target_factory example in the man page.
+ Add appendices to the User's Guide to cover the available Tools,
+ Builders and construction variables. Comment out the build of
+ the old Python 10 paper, which doesn't build on all systems and
+ is old enough at this point that it probably isn't worth the
+ effort to make it do so.
+
+ From Wayne Lee:
+
+ - Avoid "maximum recursion limit" errors when removing $(-$) pairs
+ from long command lines.
+
+ From Clive Levinson:
+
+ - Make ParseConfig() recognize and add -mno-cygwin to $LINKFLAGS and
+ $CCFLAGS, and -mwindows to $LINKFLAGS.
+
+ From Michael McCracken:
+
+ - Add a new "applelink" tool to handle the things like Frameworks and
+ bundles that Apple has added to gcc for linking.
+
+ - Use more appropriate default search lists of linkers, compilers and
+ and other tools for the 'darwin' platform.
+
+ - Add a LoadableModule Builder that builds a bundle on Mac OS X (Darwin)
+ and a shared library on other systems.
+
+ - Improve SWIG tests for use on Mac OS X (Darwin).
+
+ From Elliot Murphy:
+
+ - Enhance the tests to guarantee persistence of ListOption
+ values in saved options files.
+
+ - Supply the help text when -h is used with the -u, -U or -D options.
+
+ From Christian Neeb:
+
+ - Fix the Java parser's handling of string definitions to avoid ignoring
+ subsequent code.
+
+ From Han-Wen Nienhuys:
+
+ - Optimize variable expansion by: using the re.sub() method (when
+ possible); not using "eval" for variables for which we can fetch the
+ value directory; avoiding slowing substitution logic when there's no
+ '$' in the string.
+
+ From Gary Oberbrunner:
+
+ - Add an Environment.Dump() method to print the contents of a
+ construction environment.
+
+ - Allow $LIBS (and similar variables) to contain explicit File Nodes.
+
+ - Change ParseConfig to add the found library names directly to the
+ $LIBS variable, instead of returning them.
+
+ - Add ParseConfig() support for the -framework GNU linker option.
+
+ - Add a PRINT_CMD_LINE_FUNC construction variable to allow people
+ to filter (or log) command-line output.
+
+ - Print an internal Python stack trace in response to an otherwise
+ unexplained error when --debug=stacktrace is specified.
+
+ - Add a --debug=findlibs option to print what's happening when
+ the scanner is searching for libraries.
+
+ - Allow Tool specifications to be passed a dictionary of keyword
+ arguments.
+
+ - Support an Options default value of None, in which case the variable
+ will not be added to the construction environment unless it's set
+ explicitly by the user or from an Options file.
+
+ - Avoid copying __builtin__ values into a construction environment's
+ dictionary when evaluating construction variables.
+
+ - Add a new cross-platform intelc.py Tool that can detect and
+ configure the Intel C++ v8 compiler on both Windows, where it's
+ named icl, and Linux, where it's named icc. It also checks that
+ the directory specified in the Windows registry exists, and sets a
+ new $INTEL_C_COMPILER_VERSION construction variable to identify the
+ version being used. (Niall Douglas contributed an early prototype
+ of parts of this module.)
+
+ - Fix the private Conftest._Have() function so it doesn't change
+ non-alphanumeric characters to underscores.
+
+ - Supply a better error message when a construction variable expansion
+ has an unknown attribute.
+
+ - Documentation changes: Update the man page to describe use of
+ filenames or Nodes in $LIBS.
+
+ From Chris Pawling:
+
+ - Have the linkloc tool use $MSVS_VERSION to select the Microsoft
+ Visual Studio version to use.
+
+ From Kevin Quick:
+
+ - Fix the Builder name returned from ListBuilders and other instances
+ of subclasses of the BuilderBase class.
+
+ - Add Builders and construction variables to support rpcgen:
+ RPCGenClient(), RPCGenHeader(), RPCGenService(), RPCGenXDR(),
+ $RPCGEN, $RPCGENFLAGS, $RPCGENCLIENTFLAGS, $RPCGENHEADERFLAGS,
+ $RPCGENSERVICEFLAGS, $RPCGENXDRFLAGS.
+
+ - Update the man page to document that prefix and suffix Builder
+ keyword arguments can be strings, callables or dictionaries.
+
+ - Provide more info in the error message when a user tries to build
+ a target multiple ways.
+
+ - Fix Delete() when a file doesn't exist and must_exist=1. (We were
+ unintentionally dependent on a bug in versions of the Python shutil.py
+ module prior to Python 2.3, which would generate an exception for
+ a nonexistent file even when ignore_errors was set.)
+
+ - Only replace a Node's builder with a non-null source builder.
+
+ - Fix a stack trace when a suffix selection dictionary is passed
+ an empty source file list.
+
+ - Allow optional names to be attached to Builders, for default
+ Builders that don't get attached to construction environments.
+
+ - Fix problems with Parallel Task Exception handling.
+
+ - Build targets in an associated BuildDir even if there are targets
+ or subdirectories locally in the source directory.
+
+ - If a FunctionAction has a callable class as its underlying Python
+ function, use its strfunction() method (if any) to display the
+ action.
+
+ - Fix handling when BuildDir() exists but is unwriteable. Add
+ "Stop." to those error messages for consistency.
+
+ - Catch incidents of bad builder creation (without an action) and
+ supply meaningful error messages.
+
+ - Fix handling of src_suffix values that aren't extensions (don't
+ begin with a '.').
+
+ - Don't retrieve files from a CacheDir, but report what would happen,
+ when the -n option is used.
+
+ - Use the source_scanner from the target Node, not the source node
+ itself.
+
+ - Internal Scanners fixes: Make sure Scanners are only passed Nodes.
+ Fix how a Scanner.Selector called its base class initialization.
+ Make comparisons of Scanner objects more robust. Add a name to
+ an internal default ObjSourceScanner.
+
+ - Add a deprecated warning for use of the old "scanner" keyword argument
+ to Builder creation.
+
+ - Improve the --debug=explain message when the build action changes.
+
+ - Test enhancements in SourceCode.py, option-n.py, midl.py. Better
+ Command() and Scanner test coverage. Improved test infrastructure
+ for -c output.
+
+ - Refactor the interface between Action and Executor objects to treat
+ Actions atomically.
+
+ - The --debug=presub option will now report the pre-substitution
+ each action seprately, instead of reporting the entire list before
+ executing the actions one by one.
+
+ - The --debug=explain option explaining a changed action will now
+ (more correctly) show pre-substitution action strings, instead of
+ the commands with substituted file names.
+
+ - A Node (file) will now be rebuilt if its PreAction or PostAction
+ actions change.
+
+ - Python Function actions now have their calling signature (target,
+ source, env) reported correctly when displayed.
+
+ - Fix BuildDir()/build_dir handling when the build_dir is underneath
+ the source directory and trying to use entries from the build_dir
+ as sources for other targets in the build-dir.
+
+ - Fix hard-coding of JDK path names in various Java tests.
+
+ - Handle Python stack traces consistently (stop at the SConscript stack
+ frame, by default) even if the Python source code isn't available.
+
+ - Improve the performance of the --debug={tree,dtree} options.
+
+ - Add --debug=objects logging of creation of OverrideWarner,
+ EnvironmentCopy and EnvironmentOverride objects.
+
+ - Fix command-line expansion of Python Value Nodes.
+
+ - Internal cleanups: Remove an unnecessary scan argument. Associate
+ Scanners only with Builders, not nodes. Apply overrides once when
+ a Builder is called, not in multiple places. Cache results from the
+ Node.FS.get_suffix() and Node.get_build_env() methods. Use the Python
+ md5 modules' hexdigest() method, if there is one. Have Taskmaster
+ call get_stat() once for each Node and re-use the value instead of
+ calling it each time it needs the value. Have Node.depends_on()
+ re-use the list from the children() method instead of calling it
+ multiple times.
+
+ - Use the correct scanner if the same source file is used for targets in
+ two different environments with the same path but different scanners.
+
+ - Collect logic for caching values in memory in a Memoizer class,
+ which cleans up a lot of special-case code in various methods and
+ caches additional values to speed up most configurations.
+
+ - Add a PathAccept validator to the list of new canned PathOption
+ validators.
+
+ From Jeff Squyres:
+
+ - Documentation changes: Use $CPPDEFINES instead of $CCFLAGS in man
+ page examples.
+
+ From Levi Stephen:
+
+ - Allow $JARCHDIR to be expanded to other construction variables.
+
+ From Christoph Wiedemann:
+
+ - Add an Environment.SetDefault() method that only sets values if
+ they aren't already set.
+
+ - Have the qt.py Tool not override variables already set by the user.
+
+ - Add separate $QT_BINPATH, $QT_CPPPATH and $QT_LIBPATH variables
+ so these can be set individually, instead of being hard-wired
+ relative to $QTDIR.
+
+ - The %TEMP% and %TMP% external environment variables are now propagated
+ automatically to the command execution environment on Windows systems.
+
+ - A new --config= command-line option allows explicit control of
+ of when the Configure() tests are run: --config=force forces all
+ checks to be run, --config=cache uses all previously cached values,
+ --config=auto (the default) runs tests only when dependency analysis
+ determines it's necessary.
+
+ - The Configure() subsystem can now write a config.h file with values
+ like HAVE_STDIO_H, HAVE_LIBM, etc.
+
+ - The Configure() subsystem now executes its checks silently when the
+ -Q option is specified.
+
+ - The Configure() subsystem now reports if a test result is being
+ taken from cache, and prints the standard output and error output
+ of tests even when cached.
+
+ - Configure() test results are now reported as "yes" or "no" instead of
+ "ok" or "failed."
+
+ - Fixed traceback printing when calling the env.Configure() method
+ instead of the Configure() global function.
+
+ - The Configure() subsystem now caches build failures in a .sconsign
+ file in the subdirectory, not a .cache file. This may cause
+ tests to be re-executed the first time after you install 0.97.
+
+ - Additional significant internal cleanups in the Configure() subsystem
+ and its tests.
+
+ - Have the Qt Builder make uic-generated files dependent on the .ui.h
+ file, if one exists.
+
+ - Add a test to make sure that SCons source code does not contain
+ try:-except: blocks that catch all errors, which potentially catch
+ and mask keyboard interrupts.
+
+ - Fix us of TargetSignatures('content') with the SConf subsystem.
+
+ From Russell Yanofsky:
+
+ - Add support for the Metrowerks Codewarrior compiler and linker
+ (mwcc and mwld).
+
+
+
+RELEASE 0.96.1 - Mon, 23 Aug 2004 12:55:50 +0000
+
+ From Craig Bachelor:
+
+ - Handle white space in the executable Python path name within in MSVS
+ project files by quoting the path.
+
+ - Correct the format of a GUID string in a solution (.dsw) file so
+ MSVS can correctly "build enable" a project.
+
+ From Steven Knight:
+
+ - Add a must_exist flag to Delete() to let the user control whether
+ it's an error if the specified entry doesn't exist. The default
+ behavior is now to silently do nothing if it doesn't exist.
+
+ - Package up the new Platform/darwin.py, mistakenly left out of 0.96.
+
+ - Make the scons.bat REM statements into @REM so they aren't printed.
+
+ - Make the SCons packaging SConscript files platform independent.
+
+ From Anthony Roach:
+
+ - Fix scanning of pre-compiled header (.pch) files for #includes,
+ broken in 0.96.
+
+
+
+RELEASE 0.96 - Wed, 18 Aug 2004 13:36:40 +0000
+
+ From Chad Austin:
+
+ - Make the CacheDir() directory if it doesn't already exist.
+
+ - Allow construction variable substitutions in $LIBS specifications.
+
+ - Allow the emitter argument to a Builder() to be or expand to a list
+ of emitter functions, which will be called in sequence.
+
+ - Suppress null values in construction variables like $LIBS that use
+ the internal _concat() function.
+
+ - Remove .dll files from the construction variables searched for
+ libraries that can be fed to Win32 compilers.
+
+ From Chad Austin and Christoph Wiedemann:
+
+ - Add support for a $RPATH variable to supply a list of directories
+ to search for shared libraries when linking a program. Used by
+ the GNU and IRIX linkers (gnulink and sgilink).
+
+ From Charles Crain:
+
+ - Restore the ability to do construction variable substitutions in all
+ kinds of *PATH variables, even when the substitution returns a Node
+ or other object.
+
+ From Tom Epperly:
+
+ - Allow the Java() Builder to take more than one source directory.
+
+ From Ralf W. Grosse-Kunstleve:
+
+ - Have SConsignFile() use, by default, a custom "dblite.py" that we can
+ control and guarantee to work on all Python versions (or nearly so).
+
+ From Jonathan Gurley:
+
+ - Add support for the newer "ifort" versions of the Intel Fortran
+ Compiler for Linux.
+
+ From Bob Halley:
+
+ - Make the new *FLAGS variable type work with copied Environments.
+
+ From Chris Hoeppler:
+
+ - Initialize the name of a Scanner.Classic scanner correctly.
+
+ From James Juhasz:
+
+ - Add support for the .dylib shared library suffix and the -dynamiclib
+ linker option on Mac OS X.
+
+ From Steven Knight:
+
+ - Add an Execute() method for executing actions directly.
+
+ - Support passing environment override keyword arguments to Command().
+
+ - Fix use of $MSVS_IGNORE_IDE_PATHS, which was broken when we added
+ support for $MSVS_USE_MFC_DIRS last release.
+
+ - Make env.Append() and env.Prepend() act like the underlying Python
+ behavior when the variable being appended to is a UserList object.
+
+ - Fix a regression that prevented the Command() global function in
+ 0.95 from working with command-line strings as actions.
+
+ - Fix checking out a file from a source code management system when
+ the env.SourceCode() method was called with an individual file name
+ or node, not a directory name or node.
+
+ - Enhance the Task.make_ready() method to create a list of the
+ out-of-date Nodes for the task for use by the wrapping interface.
+
+ - Allow Scanners to pull the list of suffixes from the construction
+ environment when the "skeys" keyword argument is a string containing
+ a construction variable to be expanded.
+
+ - Support new $CPPSUFFIXES, $DSUFFIXES $FORTRANSUFFIXES, and
+ $IDLSUFFIXES. construction variables that contain the default list
+ of suffixes to be scanned by a given type of scanner, allowing these
+ suffix lists to be easily added to or overridden.
+
+ - Speed up Node creation when calling a Builder by comparing whether two
+ Environments are the same object, not if their underlying dictionaries
+ are equivalent.
+
+ - Add a --debug=explain option that reports the reason(s) why SCons
+ thinks it must rebuild something.
+
+ - Add support for functions that return platform-independent Actions
+ to Chmod(), Copy(), Delete(), Mkdir(), Move() and Touch() files
+ and/or directories. Like any other Actions, the returned Action
+ object may be executed directly using the Execute() global function
+ or env.Execute() environment method, or may be used as a Builder
+ action or in an env.Command() action list.
+
+ - Add support for the strfunction argument to all types of Actions:
+ CommandAction, ListAction, and CommandGeneratorAction.
+
+ - Speed up turning file system Nodes into strings by caching the
+ values after we're finished reading the SConscript files.
+
+ - Have ParseConfig() recognize and supporting adding the -Wa, -Wl,
+ and -Wp, flags to ASFLAGS, LINKFLAGS and CPPFLAGS, respectively.
+
+ - Change the .sconsign format and the checks for whether a Node is
+ up-to-date to make dependency checks more efficient and correct.
+
+ - Add wrapper Actions to SCons.Defaults for $ASCOM, $ASPPCOM, $LINKCOM,
+ $SHLINKCOM, $ARCOM, $LEXCOM and $YACCCOM. This makes it possible
+ to replace the default print behavior with a custom strfunction()
+ for each of these.
+
+ - When a Node has been built, don't walk the whole tree back to delete
+ the parents's implicit dependencies, let returning up the normal
+ Taskmaster descent take care of it for us.
+
+ - Add documented support for separate target_scanner and source_scanner
+ arguments to Builder creation, which allows different scanners to
+ be applied to source files
+
+ - Don't re-install or (re-generate) .h files when a subsidiary #included
+ .h file changes. This eliminates incorrect circular dependencies
+ with .h files generated from other source files.
+
+ - Slim down the internal Sig.Calculator class by eliminating methods
+ whose functionality is now covered by Node methods.
+
+ - Document use of the target_factory and source_factory keyword
+ arguments when creating Builder objects. Enhance Dir Nodes so that
+ they can be created with user-specified Builder objects.
+
+ - Don't blow up with stack trace when the external $PATH environment
+ variable isn't set.
+
+ - Make Builder calls return lists all the time, even if there's only
+ one target. This keeps things consistent and easier to program to
+ across platforms.
+
+ - Add a Flatten() function to make it easier to deal with the Builders
+ all returning lists of targets, not individual targets.
+
+ - Performance optimizations in Node.FS.__doLookup().
+
+ - Man page fixes: formatting typos, misspellings, bad example.
+
+ - User's Guide fixes: Fix the signatures of the various example
+ *Options() calls. Triple-quote properly a multi-line Split example.
+
+ - User's Guide additions: Chapter describing File and Directory
+ Nodes. Section describing declarative nature of SCons functions in
+ SConscript files. Better organization and clarification of points
+ raised by Robert P. J. Day. Chapter describing SConf (Autoconf-like)
+ functionality. Chapter describing how to install Python and
+ SCons. Chapter describing Java builds.
+
+ From Chris Murray:
+
+ - Add a .win32 attribute to force file names to expand with
+ Windows backslash path separators.
+
+ - Fix escaping file names on command lines when the expansion is
+ concatenated with another string.
+
+ - Add support for Fortran 90 and Fortran 95. This adds $FORTRAN*
+ variables that specify a default compiler, command-line, flags,
+ etc. for all Fortran versions, plus separate $F90* and $F95*
+ variables for when different compilers/flags/etc. must be specified
+ for different Fortran versions.
+
+ - Have individual tools that create libraries override the default
+ $LIBPREFIX and $LIBSUFFIX values set by the platform. This makes
+ it easier to use Microsoft Visual Studio tools on a CygWin platform.
+
+ From Gary Oberbrunner:
+
+ - Add a --debug=presub option to print actions prior to substitution.
+
+ - Add a warning upon use of the override keywords "targets" and
+ "sources" when calling Builders. These are usually mistakes which
+ are otherwise silently (and confusingly) turned into construction
+ variable overrides.
+
+ - Try to find the ICL license file path name in the external environment
+ and the registry before resorting to the hard-coded path name.
+
+ - Add support for fetching command-line keyword=value arguments in
+ order from an ARGLIST list.
+
+ - Avoid stack traces when trying to read dangling symlinks.
+
+ - Treat file "extensions" that only contain digits as part of the
+ file basename. This supports version numbers as part of shared
+ library names, for example.
+
+ - Avoid problems when there are null entries (None or '') in tool
+ lists or CPPPATH.
+
+ - Add an example and explanation of how to use "tools = ['default', ..."
+ when creating a construction environment.
+
+ - Add a section describing File and Directory Nodes and some of their
+ attributes and methods.
+
+ - Have ParseConfig() add a returned -pthread flag to both $CCFLAGS
+ and $LINKFLAGS.
+
+ - Fix some test portability issues on Mac OS X (darwin).
+
+ From Simon Perkins:
+
+ - Fix a bug introduced in building shared libraries under MinGW.
+
+ From Kevin Quick:
+
+ - Handling SCons exceptions according to Pythonic standards.
+
+ - Fix test/chained-build.py on systems that execute within one second.
+
+ - Fix tests on systems where 'ar' warns about archive creation.
+
+ From Anthony Roach:
+
+ - Fix use of the --implicit-cache option with timestamp signatures.
+
+ - If Visual Studio is installed, assume the C/C++ compiler, the linker
+ and the MIDL compiler that comes with it are available, too.
+
+ - Better error messages when evaluating a construction variable
+ expansion yields a Python syntax error.
+
+ - Change the generation of PDB files when using Visual Studio from
+ compile time to link time.
+
+ From sam th:
+
+ - Allow SConf.CheckLib() to search a list of libraries, like the
+ Autoconf AC_SEARCH_LIBS macro.
+
+ - Allow the env.WhereIs() method to take a "reject" argument to
+ let it weed out specific path names.
+
+ From Christoph Wiedemann:
+
+ - Add new Moc() and Uic() Builders for more explicit control over
+ Qt builds, plus new construction variables to control them:
+ $QT_AUTOSCAN, $QT_DEBUG, $QT_MOCCXXPREFIX, $QT_MOCCXXSUFFIX,
+ $QT_MOCHPREFIX, $QT_MOCHSUFFIX, $QT_UICDECLPREFIX, $QT_UICDECLSUFFIX,
+ $QT_UICIMPLPREFIX, $QT_UICIMPLSUFFIX and $QT_UISUFFIX.
+
+ - Add a new single_source keyword argument for Builders that enforces
+ a single source file on calls to the Builder.
+
+
+
+RELEASE 0.95 - Mon, 08 Mar 2004 06:43:20 -0600
+
+ From Chad Austin:
+
+ - Replace print statements with calls to sys.stdout.write() so output
+ lines stay together when -j is used.
+
+ - Add portability fixes for a number of tests.
+
+ - Accomodate the fact that Cygwin's os.path.normcase() lies about
+ the underlying system being case-sensitive.
+
+ - Fix an incorrect _concat() call in the $RCINCFLAGS definition for
+ the mingw Tool.
+
+ - Fix a problem with the msvc tool with Python versions prior to 2.3.
+
+ - Add support for a "toolpath" Tool() and Environment keyword that
+ allows Tool modules to be found in specified local directories.
+
+ - Work around Cygwin Python's silly fiction that it's using a
+ case-sensitive file system.
+
+ - More robust handling of data in VCComponents.dat.
+
+ - If the "env" command is available, spawn commands with the more
+ general "env -" instead of "env -i".
+
+ From Kerim Borchaev:
+
+ - Fix a typo in a msvc.py's registry lookup: "VCComponents.dat", not
+ "VSComponents.dat".
+
+ From Chris Burghart:
+
+ - Fix the ability to save/restore a PackageOption to a file.
+
+ From Steve Christensen:
+
+ - Update the MSVS .NET and MSVC 6.0/7.0 path detection.
+
+ From David M. Cooke:
+
+ - Make the Fortran scanner case-insensitive for the INCLUDE string.
+
+ From Charles Crain:
+
+ - If no version of MSVC is detected but the tool is specified,
+ use the MSVC 6.0 paths by default.
+
+ - Ignore any "6.1" version of MSVC found in the registry; this is a
+ phony version number (created by later service packs?) and would
+ throw off the logic if the user had any non-default paths configure.
+
+ - Correctly detect if the user has independently configured the MSVC
+ "include," "lib" or "path" in the registry and use the appropriate
+ values. Previously, SCons would only use the values if all three
+ were set in the registry.
+
+ - Make sure side-effect nodes are prepare()d before building their
+ corresponding target.
+
+ - Preserve the ability to call BuildDir() multiple times with the
+ same target and source directory arguments.
+
+ From Andy Friesen:
+
+ - Add support for the Digital Mars "D" programming language.
+
+ From Scott Lystig Fritchie:
+
+ - Fix the ability to use a custom _concat() function in the
+ construction environment when calling _stripixes().
+
+ - Make the message about ignoring a missing SConscript file into a
+ suppressable Warning, not a hard-coded sys.stderr.write().
+
+ - If a builder can be called multiple times for a target (because
+ the sources and overrides are identical, or it's a builder with the
+ "multi" flag set), allow the builder to be called through multiple
+ environments so long as the builders have the same signature for
+ the environments in questions (that is, they're the same action).
+
+ From Bob Halley:
+
+ - When multiple targets are built by a single action, retrieve all
+ of them from cache, not just the first target, and exec the build
+ command if any of the targets isn't present in the cache.
+
+ From Zephaniah Hull:
+
+ - Fix command-line ARGUMENTS with multiple = in them.
+
+ From Steven Knight:
+
+ - Fix EnsureSConsVersion() so it checks against the SCons version,
+ not the Python version, on Pythons with sys.version_info.
+
+ - Don't swallow the AttributeError when someone uses an expansion like
+ $TARGET.bak, so we can supply a more informative error message.
+
+ - Fix an odd double-quote escape sequence in the man page.
+
+ - Fix looking up a naked drive letter as a directory (Dir('C:')).
+
+ - Support using File nodes in the LIBS construction variable.
+
+ - Allow the LIBS construction variable to be a single string or File
+ node, not a list, when only one library is needed.
+
+ - Fix typos in the man page: JAVACHDIR => JARCHDIR; add "for_signature"
+ to the __call__() example in the "Variable Substitution" section.
+
+ - Correct error message spellings of "non-existant" to "non-existent."
+
+ - When scanning for libraries to link with, don't append $LIBPREFIXES
+ or $LIBSUFFIXES values to the $LIBS values if they're already present.
+
+ - Add a ZIPCOMPRESSION construction variable to control whether the
+ internal Python action for the Zip Builder compresses the file or
+ not. The default value is zipfile.ZIP_DEFLATED, which generates
+ a compressed file.
+
+ - Refactor construction variable expansion to support recursive
+ expansion of variables (e.g. CCFLAGS = "$CCFLAGS -g") without going
+ into an infinite loop. Support this in all construction variable
+ overrides, as well as when copying Environments.
+
+ - Fix calling Configure() from more than one subsidiary SConscript file.
+
+ - Fix the env.Action() method so it returns the correct type of
+ Action for its argument(s).
+
+ - Fix specifying .class files as input to JavaH with the .class suffix
+ when they weren't generated using the Java Builder.
+
+ - Make the check for whether all of the objects going into a
+ SharedLibrary() are shared work even if the object was built in a
+ previous run.
+
+ - Supply meaningful error messages, not stack traces, if we try to add
+ a non-Node as a source, dependency, or ignored dependency of a Node.
+
+ - Generate MSVS Project files that re-invoke SCons properly regardless
+ of whether the file was built via scons.bat or scons.py.
+ (Thanks to Niall Douglas for contributing code and testing.)
+
+ - Fix TestCmd.py, runtest.py and specific tests to accomodate being
+ run from directories whose paths include white space.
+
+ - Provide a more useful error message if a construction variable
+ expansion contains a syntax error during evaluation.
+
+ - Fix transparent checkout of implicit dependency files from SCCS
+ and RCS.
+
+ - Added new --debug=count, --debug=memory and --debug=objects options.
+ --debug=count and --debug=objects only print anything when run
+ under Python 2.1 or later.
+
+ - Deprecate the "overrides" keyword argument to Builder() creation
+ in favor of using keyword argument values directly (like we do
+ for builder execution and the like).
+
+ - Always use the Builder overrides in substitutions, not just if
+ there isn't a target-specific environment.
+
+ - Add new "rsrcpath" and "rsrcdir" and attributes to $TARGET/$SOURCE,
+ so Builder command lines can find things in Repository source
+ directories when using BuildDir.
+
+ - Fix the M4 Builder so that it chdirs to the Repository directory
+ when the input file is in the source directory of a BuildDir.
+
+ - Save memory at build time by allowing Nodes to delete their build
+ environments after they've been built.
+
+ - Add AppendUnique() and PrependUnique() Environment methods, which
+ add values to construction variables like Append() and Prepend()
+ do, but suppress any duplicate elements in the list.
+
+ - Allow the 'qt' tool to still be used successfully from a copied
+ Environment. The include and library directories previously ended up
+ having the same string re-appended to the end, yielding an incorrect
+ path name.
+
+ - Supply a more descriptive error message when the source for a target
+ can't be found.
+
+ - Initialize all *FLAGS variables with objects do the right thing with
+ appending flags as strings or lists.
+
+ - Make things like ${TARGET.dir} work in *PATH construction variables.
+
+ - Allow a $MSVS_USE_MFC_DIRS construction variable to control whether
+ ATL and MFC directories are included in the default INCLUDE and
+ LIB paths.
+
+ - Document the dbm_module argument to the SConsignFile() function.
+
+ From Vincent Risi:
+
+ - Add support for the bcc32, ilink32 and tlib Borland tools.
+
+ From Anthony Roach:
+
+ - Supply an error message if the user tries to configure a BuildDir
+ for a directory that already has one.
+
+ - Remove documentation of the still-unimplemented -e option.
+
+ - Add -H help text listing the legal --debug values.
+
+ - Don't choke if a construction variable is a non-string value.
+
+ - Build Type Libraries in the target directory, not the source
+ directory.
+
+ - Add an appendix to the User's Guide showing how to accomplish
+ various common tasks in Python.
+
+ From Greg Spencer:
+
+ - Add support for Microsoft Visual Studio 2003 (version 7.1).
+
+ - Evaluate $MSVSPROJECTSUFFIX and $MSVSSOLUTIONSUFFIX when the Builder
+ is invoked, not when the tool is initialized.
+
+ From Christoph Wiedemann:
+
+ - When compiling Qt, make sure the moc_*.cc files are compiled using
+ the flags from the environment used to specify the target, not
+ the environment that first has the Qt Builders attached.
+
+
+
+RELEASE 0.94 - Fri, 07 Nov 2003 05:29:48 -0600
+
+ From Hartmut Goebel:
+
+ - Add several new types of canned functions to help create options:
+ BoolOption(), EnumOption(), ListOption(), PackageOption(),
+ PathOption().
+
+ From Steven Knight:
+
+ - Fix use of CPPDEFINES with C++ source files.
+
+ - Fix env.Append() when the operand is an object with a __cmp__()
+ method (like a Scanner instance).
+
+ - Fix subclassing the Environment and Scanner classes.
+
+ - Add BUILD_TARGETS, COMMAND_LINE_TARGETS and DEFAULT_TARGETS variables.
+
+ From Steve Leblanc:
+
+ - SGI fixes: Fix C++ compilation, add a separate Tool/sgic++.py module.
+
+ From Gary Oberbrunner:
+
+ - Fix how the man page un-indents after examples in some browsers.
+
+ From Vincent Risi:
+
+ - Fix the C and C++ tool specifications for AIX.
+
+
+
+RELEASE 0.93 - Thu, 23 Oct 2003 07:26:55 -0500
+
+ From J.T. Conklin:
+
+ - On POSIX, execute commands with the more modern os.spawnvpe()
+ function, if it's available.
+
+ - Scan .S, .spp and .SPP files for C preprocessor dependencies.
+
+ - Refactor the Job.Parallel() class to use a thread pool without a
+ condition variable. This improves parallel build performance and
+ handles keyboard interrupts properly when -j is used.
+
+ From Charles Crain:
+
+ - Add support for a JARCHDIR variable to control changing to a
+ directory using the jar -C option.
+
+ - Add support for detecting Java manifest files when using jar,
+ and specifying them using the jar m flag.
+
+ - Fix some Python 2.2 specific things in various tool modules.
+
+ - Support directories as build sources, so that a rebuild of a target
+ can be triggered if anything underneath the directory changes.
+
+ - Have the scons.bat and scons.py files look for the SCons modules
+ in site-packages as well.
+
+ From Christian Engel:
+
+ - Support more flexible inclusion of separate C and C++ compilers.
+
+ - Use package management tools on AIX and Solaris to find where
+ the comilers are installed, and what version they are.
+
+ - Add support for CCVERSION and CXXVERSION variables for a number
+ of C and C++ compilers.
+
+ From Sergey Fogel:
+
+ - Add test cases for the new capabilities to run bibtex and to rerun
+ latex as needed.
+
+ From Ralf W. Grosse-Kunstleve:
+
+ - Accomodate anydbm modules that don't have a sync() method.
+
+ - Allow SConsignFile() to take an argument specifying the DBM
+ module to be used.
+
+ From Stephen Kennedy:
+
+ - Add support for a configurable global .sconsign.dbm file which
+ can be used to avoid cluttering each directory with an individual
+ .sconsign file.
+
+ From John Johnson:
+
+ - Fix (re-)scanning of dependencies in generated or installed
+ header files.
+
+ From Steven Knight:
+
+ - The -Q option suppressed too many messages; fix it so that it only
+ suppresses the Reading/Building messages.
+
+ - Support #include when there's no space before the opening quote
+ or angle bracket.
+
+ - Accomodate alphanumeric version strings in EnsurePythonVersion().
+
+ - Support arbitrary expansion of construction variables within
+ file and directory arguments to Builder calls and Environment methods.
+
+ - Add Environment-method versions of the following global functions:
+ Action(), AddPostAction(), AddPreAction(), Alias(), Builder(),
+ BuildDir(), CacheDir(), Clean(), Configure(), Default(),
+ EnsurePythonVersion(), EnsureSConsVersion(), Environment(),
+ Exit(), Export(), FindFile(), GetBuildPath(), GetOption(), Help(),
+ Import(), Literal(), Local(), Platform(), Repository(), Scanner(),
+ SConscriptChdir(), SConsignFile(), SetOption(), SourceSignatures(),
+ Split(), TargetSignatures(), Tool(), Value().
+
+ - Add the following global functions that correspond to the same-named
+ Environment methods: AlwaysBuild(), Command(), Depends(), Ignore(),
+ Install(), InstallAs(), Precious(), SideEffect() and SourceCode().
+
+ - Add the following global functions that correspond to the default
+ Builder methods supported by SCons: CFile(), CXXFile(), DVI(), Jar(),
+ Java(), JavaH(), Library(), M4(), MSVSProject(), Object(), PCH(),
+ PDF(), PostScript(), Program(), RES(), RMIC(), SharedLibrary(),
+ SharedObject(), StaticLibrary(), StaticObject(), Tar(), TypeLibrary()
+ and Zip().
+
+ - Rearrange the man page to show construction environment methods and
+ global functions in the same list, and to explain the difference.
+
+ - Alphabetize the explanations of the builder methods in the man page.
+
+ - Rename the Environment.Environment class to Enviroment.Base.
+ Allow the wrapping interface to extend an Environment by using its own
+ subclass of Environment.Base and setting a new Environment.Environment
+ variable as the calling entry point.
+
+ - Deprecate the ParseConfig() global function in favor of a same-named
+ construction environment method.
+
+ - Allow the Environment.WhereIs() method to take explicit path and
+ pathext arguments (like the underlying SCons.Util.WhereIs() function).
+
+ - Remove the long-obsolete {Get,Set}CommandHandler() functions.
+
+ - Enhance env.Append() to suppress null values when appropriate.
+
+ - Fix ParseConfig() so it works regardless of initial construction
+ variable values.
+
+ Extend CheckHeader(), CheckCHeader(), CheckCXXHeader() and
+ CheckLibWithHeader() to accept a list of header files that will be
+ #included in the test. The last one in the list is assumed to be
+ the one being checked for. (Prototype code contributed by Gerard
+ Patel and Niall Douglas).
+
+ - Supply a warning when -j is used and threading isn't built in to
+ the current version of Python.
+
+ - First release of the User's Guide (finally, and despite a lot
+ of things still missing from it...).
+
+ From Clark McGrew:
+
+ - Generalize the action for .tex files so that it will decide whether
+ a file is TeX or LaTeX, check the .aux output to decide if it should
+ run bibtex, and check the .log output to re-run LaTeX if needed.
+
+ From Bram Moolenaar:
+
+ - Split the non-SCons-specific functionality from SConf.py to a new,
+ re-usable Conftest.py module.
+
+ From Gary Oberbrunner:
+
+ - Allow a directory to be the target or source or dependency of a
+ Depends(), Ignore(), Precious() or SideEffect() call.
+
+ From Gerard Patel:
+
+ - Use the %{_mandir} macro when building our RPM package.
+
+ From Marko Rauhamaa:
+
+ - Have the closing message say "...terminated because of errors" if
+ there were any.
+
+ From Anthony Roach:
+
+ - On Win32 systems, only use "rm" to delete files if Cygwin is being
+ used. ("rm" doesn't understand Win32-format path names.)
+
+ From Christoph Wiedemann:
+
+ - Fix test/SWIG.py to find the Python include directory in all cases.
+
+ - Fix a bug in detection of Qt installed on the local system.
+
+ - Support returning Python 2.3 BooleanType values from Configure checks.
+
+ - Provide an error message if someone mistakenly tries to call a
+ Configure check from within a Builder function.
+
+ - Support calling a Builder when a Configure context is still open.
+
+ - Handle interrupts better by eliminating all try:-except: blocks
+ which caught any and all exceptions, including KeyboardInterrupt.
+
+ - Add a --duplicate= option to control how files are duplicated.
+
+
+
+RELEASE 0.92 - Wed, 20 Aug 2003 03:45:28 -0500
+
+ From Charles Crain and Gary Oberbrunner:
+
+ - Fix Tool import problems with the Intel and PharLap linkers.
+
+ From Steven Knight
+
+ - Refactor the DictCmdGenerator class to be a Selector subclass.
+
+ - Allow the DefaultEnvironment() function to take arguments and pass
+ them to instantiation of the default construction environment.
+
+ - Update the Debian package so it uses Python 2.2 and more closely
+ resembles the currently official Debian packaging info.
+
+ From Gerard Patel
+
+ - When the yacc -d flag is used, take the .h file base name from the
+ target .c file, not the source (matching what yacc does).
+
+
+
+RELEASE 0.91 - Thu, 14 Aug 2003 13:00:44 -0500
+
+ From Chad Austin:
+
+ - Support specifying a list of tools when calling Environment.Copy().
+
+ - Give a Value Nodes a timestamp of the system time when they're
+ created, so they'll work when using timestamp-based signatures.
+
+ - Add a DefaultEnvironment() function that only creates a default
+ environment on-demand (for fetching source files, e.g.).
+
+ - Portability fix for test/M4.py.
+
+ From Steven Knight:
+
+ - Tighten up the scons -H help output.
+
+ - When the input yacc file ends in .yy and the -d flag is specified,
+ recognize that a .hpp file (not a .h file) will be created.
+
+ - Make builder prefixes work correctly when deducing a target
+ from a source file name in another directory.
+
+ - Documentation fixes: typo in the man page; explain up-front about
+ not propagating the external environment.
+
+ - Use "cvs co -d" instead of "cvs co -p >" when checking out something
+ from CVS with a specified module name. This avoids zero-length
+ files when there is a checkout error.
+
+ - Add an "sconsign" script to print the contents of .sconsign files.
+
+ - Speed up maintaining the various lists of Node children by using
+ dictionaries to avoid "x in list" searches.
+
+ - Cache the computed list of Node children minus those being Ignored
+ so it's only calculated once.
+
+ - Fix use of the --cache-show option when building a Program()
+ (or using any other arbitrary action) by making sure all Action
+ instances have strfunction() methods.
+
+ - Allow the source of Command() to be a directory.
+
+ - Better error handling of things like raw TypeErrors in SConscripts.
+
+ - When installing using "setup.py install --prefix=", suppress the
+ distutils warning message about adding the (incorrect) library
+ directory to your search path.
+
+ - Correct the spelling of the "validater" option to "validator."
+ Add a DeprecatedWarning when the old spelling is used.
+
+ - Allow a Builder's emitter to be a dictionary that maps source file
+ suffixes to emitter functions, using the suffix of the first file
+ in the source list to pick the right one.
+
+ - Refactor the creation of the Program, *Object and *Library Builders
+ so that they're moved out of SCons.Defaults and created on demand.
+
+ - Don't split SConscript file names on white space.
+
+ - Document the SConscript function's "dirs" and "name" keywords.
+
+ - Remove the internal (and superfluous) SCons.Util.argmunge() function.
+
+ - Add /TP to the default CXXFLAGS for msvc, so it can compile all
+ of the suffixes we use as C++ files.
+
+ - Allow the "prefix" and "suffix" attributes of a Builder to be
+ callable objects that return generated strings, or dictionaries
+ that map a source file suffix to the right prefix/suffix.
+
+ - Support a MAXLINELINELENGTH construction variable on Win32 systems
+ to control when a temporary file is used for long command lines.
+
+ - Make how we build .rpm packages not depend on the installation
+ locations from the distutils being used.
+
+ - When deducing a target Node, create it directly from the first
+ source Node, not by trying to create the right string to pass to
+ arg2nodes().
+
+ - Add support for SWIG.
+
+ From Bram Moolenaar:
+
+ - Test portability fixes for FreeBSD.
+
+ From Gary Oberbrunner:
+
+ - Report the target being built in error messages when building
+ multiple sources from different extensions, or when the target file
+ extension can't be deduced, or when we don't have an action for a
+ file suffix.
+
+ - Provide helpful error messages when the arguments to env.Install()
+ are incorrect.
+
+ - Fix the value returned by the Node.prevsiginfo() method to conform
+ to a previous change when checking whether a node is current.
+
+ - Supply a stack trace if the Taskmaster catches an exception.
+
+ - When using a temporary file for a long link line on Win32 systems,
+ (also) print the command line that is being executed through the
+ temporary file.
+
+ - Initialize the LIB environment variable when using the Intel
+ compiler (icl).
+
+ - Documentation fixes: better explain the AlwaysBuild() function.
+
+ From Laurent Pelecq:
+
+ - When the -debug=pdb option is specified, use pdb.Pdb().runcall() to
+ call pdb directly, don't call Python recursively.
+
+ From Ben Scott:
+
+ - Add support for a platform-independent CPPDEFINES variable.
+
+ From Christoph Wiedemann:
+
+ - Have the g++ Tool actually use g++ in preference to c++.
+
+ - Have the gcc Tool actually use gcc in preference to cc.
+
+ - Add a gnutools.py test of the GNU tool chain.
+
+ - Be smarter about linking: use $CC by default and $CXX only if we're
+ linking with any C++ objects.
+
+ - Avoid SCons hanging when a piped command has a lot of output to read.
+
+ - Add QT support for preprocessing .ui files into .c files.
+
+
+
+RELEASE 0.90 - Wed, 25 Jun 2003 14:24:52 -0500
+
+ From Chad Austin:
+
+ - Fix the _concat() documentation, and add a test for it.
+
+ - Portability fixes for non-GNU versions of lex and yacc.
+
+ From Matt Balvin:
+
+ - Fix handling of library prefixes when the subdirectory matches
+ the prefix.
+
+ From Timothee Bessett:
+
+ - Add an M4 Builder.
+
+ From Charles Crain:
+
+ - Use '.lnk' as the suffix on the temporary file for linking long
+ command lines (necessary for the Phar Lap linkloc linker).
+
+ - Save non-string Options values as their actual type.
+
+ - Save Options string values that contain a single quote correctly.
+
+ - Save any Options values that are changed from the default
+ Environment values, not just ones changed on the command line or in
+ an Options file.
+
+ - Make closing the Options file descriptor exception-safe.
+
+ From Steven Knight:
+
+ - SCons now enforces (with an error) that construction variables
+ must have the same form as valid Python identifiers.
+
+ - Fix man page bugs: remove duplicate AddPostAction() description;
+ document no_import_lib; mention that CPPFLAGS does not contain
+ $_CPPINCFLAGS; mention that F77FLAGS does not contain $_F77INCFLAGS;
+ mention that LINKFLAGS and SHLINKFLAGS contains neither $_LIBFLAGS
+ nor $_LIBDIRFLAGS.
+
+ - Eliminate a dependency on the distutils.fancy_getopt module by
+ copying and pasting its wrap_text() function directly.
+
+ - Make the Script.Options() subclass match the underlying base class
+ implementation.
+
+ - When reporting a target is up to date, quote the target like make
+ (backquote-quote) instead of with double quotes.
+
+ - Fix handling of ../* targets when using -U, -D or -u.
+
+ From Steve Leblanc:
+
+ - Don't update the .sconsign files when run with -n.
+
+ From Gary Oberbrunner:
+
+ - Add support for the Intel C Compiler (icl.exe).
+
+ From Anthony Roach
+
+ - Fix Import('*').
+
+ From David Snopek
+
+ - Fix use of SConf in paths with white space in them.
+
+ - Add CheckFunc and CheckType functionality to SConf.
+
+ - Fix use of SConf with Builders that return a list of nodes.
+
+ From David Snopek and Christoph Wiedemann
+
+ - Fix use of the SConf subsystem with SConscriptChdir().
+
+ From Greg Spencer
+
+ - Check for the existence of MS Visual Studio on disk before using it,
+ to avoid getting fooled by leftover junk in the registry.
+
+ - Add support for MSVC++ .NET.
+
+ - Add support for MS Visual Studio project files (DSP, DSW,
+ SLN and VCPROJ files).
+
+ From Christoph Wiedemann
+
+ - SConf now works correctly when the -n and -q options are used.
+
+
+
+RELEASE 0.14 - Wed, 21 May 2003 05:16:32 -0500
+
+ From Chad Austin:
+
+ - Use .dll (not .so) for shared libraries on Cygwin; use -fPIC
+ when compiling them.
+
+ - Use 'rm' to remove files under Cygwin.
+
+ - Add a PLATFORM variable to construction environments.
+
+ - Remove the "platform" argument from tool specifications.
+
+ - Propogate PYTHONPATH when running the regression tests so distutils
+ can be found in non-standard locations.
+
+ - Using MSVC long command-line linking when running Cygwin.
+
+ - Portability fixes for a lot of tests.
+
+ - Add a Value Node class for dependencies on in-core Python values.
+
+ From Allen Bierbaum:
+
+ - Pass an Environment to the Options validator method, and
+ add an Options.Save() method.
+
+ From Steve Christensen:
+
+ - Add an optional sort function argument to the GenerateHelpText()
+ Options function.
+
+ - Evaluate the "varlist" variables when computing the signature of a
+ function action.
+
+ From Charles Crain:
+
+ - Parse the source .java files for class names (including inner class
+ names) to figure out the target .class files that will be created.
+
+ - Make Java support work with Repositories and SConscriptChdir(0).
+
+ - Pass Nodes, not strings, to Builder emitter functions.
+
+ - Refactor command-line interpolation and signature calculation
+ so we can use real Node attributes.
+
+ From Steven Knight:
+
+ - Add Java support (javac, javah, jar and rmic).
+
+ - Propagate the external SYSTEMROOT environment variable into ENV on
+ Win32 systems, so external commands that use sockets will work.
+
+ - Add a .posix attribute to PathList expansions.
+
+ - Check out CVS source files using POSIX path names (forward slashes
+ as separators) even on Win32.
+
+ - Add Node.clear() and Node.FS.Entry.clear() methods to wipe out a
+ Node's state, allowing it to be re-evaluated by continuous
+ integration build interfaces.
+
+ - Change the name of the Set{Build,Content}SignatureType() functions
+ to {Target,Source}Signatures(). Deprecate the old names but support
+ them for backwards compatibility.
+
+ - Add internal SCons.Node.FS.{Dir,File}.Entry() methods.
+
+ - Interpolate the null string if an out-of-range subscript is used
+ for a construction variable.
+
+ - Fix the internal Link function so that it properly links or copies
+ files in subsidiary BuildDir directories.
+
+ - Refactor the internal representation of a single execution instance
+ of an action to eliminate redundant signature calculations.
+
+ - Eliminate redundant signature calculations for Nodes.
+
+ - Optimize out calling hasattr() before accessing attributes.
+
+ - Say "Cleaning targets" (not "Building...") when the -c option is
+ used.
+
+ From Damyan Pepper:
+
+ - Quote the "Entering directory" message like Make.
+
+ From Stefan Reichor:
+
+ - Add support for using Ghostscript to convert Postscript to PDF files.
+
+ From Anthony Roach:
+
+ - Add a standalone "Alias" function (separate from an Environment).
+
+ - Make Export() work for local variables.
+
+ - Support passing a dictionary to Export().
+
+ - Support Import('*') to import everything that's been Export()ed.
+
+ - Fix an undefined exitvalmap on Win32 systems.
+
+ - Support new SetOption() and GetOption() functions for setting
+ various command-line options from with an SConscript file.
+
+ - Deprecate the old SetJobs() and GetJobs() functions in favor of
+ using the new generic {Set,Get}Option() functions.
+
+ - Fix a number of tests that searched for a Fortran compiler using the
+ external PATH instead of what SCons would use.
+
+ - Fix the interaction of SideEffect() and BuildDir() so that (for
+ example) PDB files get put correctly in a BuildDir().
+
+ From David Snopek:
+
+ - Contribute the "Autoscons" code for Autoconf-like checking for
+ the existence of libraries, header files and the like.
+
+ - Have the Tool() function add the tool name to the $TOOLS
+ construction variable.
+
+ From Greg Spencer:
+
+ - Support the C preprocessor #import statement.
+
+ - Allow the SharedLibrary() Builder on Win32 systems to be able to
+ register a newly-built dll using regsvr32.
+
+ - Add a Builder for Windows type library (.tlb) files from IDL files.
+
+ - Add an IDL scanner.
+
+ - Refactor the Fortran, C and IDL scanners to share common logic.
+
+ - Add .srcpath and .srcdir attributes to $TARGET and $SOURCE.
+
+ From Christoph Wiedemann:
+
+ - Integrate David Snopek's "Autoscons" code as the new SConf
+ configuration subsystem, including caching of values between
+ runs (using normal SCons dependency mechanisms), tests, and
+ documentation.
+
+
+
+RELEASE 0.13 - Mon, 31 Mar 2003 20:22:00 -0600
+
+ From Charles Crain:
+
+ - Fix a bug when BuildDir(duplicate=0) is used and SConscript
+ files are called from within other SConscript files.
+
+ - Support (older) versions of Perforce which don't set the Windows
+ registry.
+
+
+
+RELEASE 0.12 - Thu, 27 Mar 2003 23:52:09 -0600
+
+ From Charles Crain:
+
+ - Added support for the Perforce source code management system.
+
+ - Fix str(Node.FS) so that it returns a path relative to the calling
+ SConscript file's directory, not the top-level directory.
+
+ - Added support for a separate src_dir argument to SConscript()
+ that allows explicit specification of where the source files
+ for an SConscript file can be found.
+
+ - Support more easily re-usable flavors of command generators by
+ calling callable variables when strings are expanded.
+
+ From Steven Knight:
+
+ - Added an INSTALL construction variable that can be set to a function
+ to control how the Install() and InstallAs() Builders install files.
+ The default INSTALL function now copies, not links, files.
+
+ - Remove deprecated features: the "name" argument to Builder objects,
+ and the Environment.Update() method.
+
+ - Add an Environment.SourceCode() method to support fetching files
+ from source code systems. Add factory methods that create Builders
+ to support BitKeeper, CVS, RCS, and SCCS. Add support for fetching
+ files from RCS or SCCS transparently (like GNU Make).
+
+ - Make the internal to_String() function more efficient.
+
+ - Make the error message the same as other build errors when there's a
+ problem unlinking a target file in preparation for it being built.
+
+ - Make TARGET, TARGETS, SOURCE and SOURCES reserved variable names and
+ warn if the user tries to set them in a construction environment.
+
+ - Add support for Tar and Zip files.
+
+ - Better documentation of the different ways to export variables to a
+ subsidiary SConscript file. Fix documentation bugs in a tools
+ example, places that still assumed SCons split strings on white
+ space, and typos.
+
+ - Support fetching arbitrary files from the TARGETS or SOURCES lists
+ (e.g. ${SOURCES[2]}) when calculating the build signature of a
+ command.
+
+ - Don't silently swallow exceptions thrown by Scanners (or other
+ exceptions while finding a node's dependent children).
+
+ - Push files to CacheDir() before calling the superclass built()
+ method (which may clear the build signature as part of clearing
+ cached implicit dependencies, if the file has a source scanner).
+ (Bug reported by Jeff Petkau.)
+
+ - Raise an internal error if we attempt to push a file to CacheDir()
+ with a build signature of None.
+
+ - Add an explicit Exit() function for terminating early.
+
+ - Change the documentation to correctly describe that the -f option
+ doesn't change to the directory in which the specified file lives.
+
+ - Support changing directories locally with SConscript directory
+ path names relative to any SConstruct file specified with -f.
+ This allows you to build in another directory by simply changing
+ there and pointing at the SConstruct file in another directory.
+
+ - Change the default SConscriptChdir() behavior to change to the
+ SConscript directory while it's being read.
+
+ - Fix an exception thrown when the -U option was used with no
+ Default() target specified.
+
+ - Fix -u so that it builds things in corresponding build directories
+ when used in a source directory.
+
+ From Lachlan O'Dea:
+
+ - Add SharedObject() support to the masm tool.
+
+ - Fix WhereIs() to return normalized paths.
+
+ From Jeff Petkau:
+
+ - Don't copy a built file to a CacheDir() if it's already there.
+
+ - Avoid partial copies of built files in a CacheDir() by copying
+ to a temporary file and renaming.
+
+ From Anthony Roach:
+
+ - Fix incorrect dependency-cycle errors when an Aliased source doesn't
+ exist.
+
+
+
+RELEASE 0.11 - Tue, 11 Feb 2003 05:24:33 -0600
+
+ From Chad Austin:
+
+ - Add support for IRIX and the SGI MIPSPro tool chain.
+
+ - Support using the MSVC tool chain when running Cygwin Python.
+
+ From Michael Cook:
+
+ - Avoid losing signal bits in the exit status from a command,
+ helping terminate builds on interrupt (CTRL+C).
+
+ From Charles Crain:
+
+ - Added new AddPreAction() and AddPostAction() functions that support
+ taking additional actions before or after building specific targets.
+
+ - Add support for the PharLap ETS tool chain.
+
+ From Steven Knight:
+
+ - Allow Python function Actions to specify a list of construction
+ variables that should be included in the Action's signature.
+
+ - Allow libraries in the LIBS variable to explicitly include the prefix
+ and suffix, even when using the GNU linker.
+ (Bug reported by Neal Becker.)
+
+ - Use DOS-standard CR-LF line endings in the scons.bat file.
+ (Bug reported by Gary Ruben.)
+
+ - Doc changes: Eliminate description of deprecated "name" keyword
+ argument from Builder definition (reported by Gary Ruben).
+
+ - Support using env.Append() on BUILDERS (and other dictionaries).
+ (Bug reported by Bj=F6rn Bylander.)
+
+ - Setting the BUILDERS construction variable now properly clears
+ the previous Builder attributes from the construction Environment.
+ (Bug reported by Bj=F6rn Bylander.)
+
+ - Fix adding a prefix to a file when the target isn't specified.
+ (Bug reported by Esa Ilari Vuokko.)
+
+ - Clean up error messages from problems duplicating into read-only
+ BuildDir directories or into read-only files.
+
+ - Add a CommandAction.strfunction() method, and add an "env" argument
+ to the FunctionAction.strfunction() method, so that all Action
+ objects have strfunction() methods, and the functions for building
+ and returning a string both take the same arguments.
+
+ - Add support for new CacheDir() functionality to share derived files
+ between builds, with related options --cache-disable, --cache-force,
+ and --cache-show.
+
+ - Change the default behavior when no targets are specified to build
+ everything in the current directory and below (like Make). This
+ can be disabled by specifying Default(None) in an SConscript.
+
+ - Revamp SCons installation to fix a case-sensitive installation
+ on Win32 systems, and to add SCons-specific --standard-lib,
+ --standalone-lib, and --version-lib options for easier user
+ control of where the libraries get installed.
+
+ - Fix the ability to directly import and use Platform and Tool modules
+ that have been implicitly imported into an Environment().
+
+ - Add support for allowing an embedding interface to annotate a node
+ when it's created.
+
+ - Extend the SConscript() function to accept build_dir and duplicate
+ keyword arguments that function like a BuildDir() call.
+
+ From Steve Leblanc:
+
+ - Fix the output of -c -n when directories are involved, so it
+ matches -c.
+
+ From Anthony Roach:
+
+ - Use a different shared object suffix (.os) when using gcc so shared
+ and static objects can exist side-by-side in the same directory.
+
+ - Allow the same object files on Win32 to be linked into either
+ shared or static libraries.
+
+ - Cache implicit cache values when using --implicit-cache.
+
+
+
+RELEASE 0.10 - Thu, 16 Jan 2003 04:11:46 -0600
+
+ From Derrick 'dman' Hudson:
+
+ - Support Repositories on other file systems by symlinking or
+ copying files when hard linking won't work.
+
+ From Steven Knight:
+
+ - Remove Python bytecode (*.pyc) files from the scons-local packages.
+
+ - Have FunctionActions print a description of what they're doing
+ (a representation of the Python call).
+
+ - Fix the Install() method so that, like other actions, it prints
+ what would have happened when the -n option is used.
+
+ - Don't create duplicate source files in a BuildDir when the -n
+ option is used.
+
+ - Refactor the Scanner interface to eliminate unnecessary Scanner
+ calls and make it easier to write efficient scanners.
+
+ - Added a "recursive" flag to Scanner creation that specifies the
+ Scanner should be invoked recursively on dependency files returned
+ by the scanner.
+
+ - Significant performance improvement from using a more efficient
+ check, throughout the code, for whether a Node has a Builder.
+
+ - Fix specifying only the source file to MultiStepBuilders such as
+ the Program Builder. (Bug reported by Dean Bair.)
+
+ - Fix an exception when building from a file with the same basename as
+ the subdirectory in which it lives. (Bug reported by Gerard Patel.)
+
+ - Fix automatic deduction of a target file name when there are
+ multiple source files specified; the target is now deduced from just
+ the first source file in the list.
+
+ - Documentation fixes: better initial explanation of SConscript files;
+ fix a misformatted "table" in the StaticObject explanation.
+
+ From Steven Knight and Steve Leblanc:
+
+ - Fix the -c option so it will remove symlinks.
+
+ From Steve Leblanc:
+
+ - Add a Clean() method to support removing user-specified targets
+ when using the -c option.
+
+ - Add a development script for running SCons through PyChecker.
+
+ - Clean up things found by PyChecker (mostly unnecessary imports).
+
+ - Add a script to use HappyDoc to create HTML class documentation.
+
+ From Lachlan O'Dea:
+
+ - Make the Environment.get() method return None by default.
+
+ From Anthony Roach:
+
+ - Add SetJobs() and GetJobs() methods to allow configuration of the
+ number of default jobs (still overridden by -j).
+
+ - Convert the .sconsign file format from ASCII to a pickled Python
+ data structure.
+
+ - Error message cleanups: Made consistent the format of error
+ messages (now all start with "scons: ***") and warning messages (now
+ all start with "scons: warning:"). Caught more cases with the "Do
+ not know how to build" error message.
+
+ - Added support for the MinGW tool chain.
+
+ - Added a --debug=includes option.
+
+
+
+RELEASE 0.09 - Thu, 5 Dec 2002 04:48:25 -0600
+
+ From Chad Austin:
+
+ - Add a Prepend() method to Environments, to append values to
+ the beginning of construction variables.
+
+ From Matt Balvin:
+
+ - Add long command-line support to the "lib" Tool (Microsoft library
+ archiver), too.
+
+ From Charles Crain:
+
+ - Allow $$ in a string to be passed through as $.
+
+ - Support file names with odd characters in them.
+
+ - Add support for construction variable substition on scanner
+ directories (in CPPPATH, F77PATH, LIBPATH, etc.).
+
+ From Charles Crain and Steven Knight:
+
+ - Add Repository() functionality, including the -Y option.
+
+ From Steven Knight:
+
+ - Fix auto-deduction of target names so that deduced targets end
+ up in the same subdirectory as the source.
+
+ - Don't remove source files specified on the command line!
+
+ - Suport the Intel Fortran Compiler (ifl.exe).
+
+ - Supply an error message if there are no command-line or
+ Default() targets specified.
+
+ - Fix the ASPPCOM values for the GNU assembler.
+ (Bug reported by Brett Polivka.)
+
+ - Fix an exception thrown when a Default() directory was specified
+ when using the -U option.
+
+ - Issue a warning when -c can't remove a target.
+
+ - Eliminate unnecessary Scanner calls by checking for the
+ existence of a file before scanning it. (This adds a generic
+ hook to check an arbitrary condition before scanning.)
+
+ - Add explicit messages to tell when we're "Reading SConscript files
+ ...," "done reading SConscript files," "Building targets," and
+ "done building targets." Add a -Q option to supress these.
+
+ - Add separate $SHOBJPREFIX and $SHOBJSUFFIX construction variables
+ (by default, the same as $OBJPREFIX and $OBJSUFFIX).
+
+ - Add Make-like error messages when asked to build a source file,
+ and before trying to build a file that doesn't have all its source
+ files (including when an invalid drive letter is used on WIN32).
+
+ - Add an scons-local-{version} package (in both .tar.gz and .zip
+ flavors) to help people who want to ship SCons as a stand-alone
+ build tool in their software packages.
+
+ - Prevent SCons from unlinking files in certain situations when
+ the -n option is used.
+
+ - Change the name of Tool/lib.py to Tool/mslib.py.
+
+ From Steven Knight and Anthony Roach:
+
+ - Man page: document the fact that Builder calls return Node objects.
+
+ From Steve LeBlanc:
+
+ - Refactor option processing to use our own version of Greg Ward's
+ Optik module, modified to run under Python 1.5.2.
+
+ - Add a ParseConfig() command to modify an environment based on
+ parsing output from a *-config command.
+
+ From Jeff Petkau:
+
+ - Fix interpretation of '#/../foo' on Win32 systems.
+
+ From Anthony Roach:
+
+ - Fixed use of command lines with spaces in their arguments,
+ and use of Nodes with spaces in their string representation.
+
+ - Make access and modification times of files in a BuildDir match
+ the source file, even when hard linking isn't available.
+
+ - Make -U be case insensitive on Win32 systems.
+
+ - Issue a warning and continue when finding a corrupt .sconsign file.
+
+ - Fix using an alias as a dependency of a target so that if one of the
+ alias' dependencies gets rebuilt, the resulting target will, too.
+
+ - Fix differently ordered targets causing unnecessary rebuilds
+ on case insensitive systems.
+
+ - Use os.system() to execute external commands whenever the "env"
+ utility is available, which is much faster than fork()/exec(),
+ and fixes the -j option on several platforms.
+
+ - Fix use of -j with multiple targets.
+
+ - Add an Options() object for friendlier accomodation of command-
+ line arguments.
+
+ - Add support for Microsoft VC++ precompiled header (.pch) files,
+ debugger (.pdb) files, and resource (.rc) files.
+
+ - Don't compute the $_CPPINCFLAGS, $_F77INCFLAGS, $_LIBFLAGS and
+ $_LIBDIRFLAGS variables each time a command is executed, define
+ them so they're computed only as needed. Add a new _concat
+ function to the Environment that allows people to define their
+ own similar variables.
+
+ - Fix dependency scans when $LIBS is overridden.
+
+ - Add EnsurePythonVersion() and EnsureSConsVersion() functions.
+
+ - Fix the overly-verbose stack trace on ListBuilder build errors.
+
+ - Add a SetContentSignatureType() function, allowing use of file
+ timestamps instead of MD5 signatures.
+
+ - Make -U and Default('source') fail gracefully.
+
+ - Allow the File() and Dir() methods to take a path-name string as
+ the starting directory, in addition to a Dir object.
+
+ - Allow the command handler to be selected via the SPAWN, SHELL
+ and ESCAPE construction variables.
+
+ - Allow construction variables to be overridden when a Builder
+ is called.
+
+ From sam th:
+
+ - Dynamically check for the existence of utilities with which to
+ initialize Environments by default.
+
+
+
+RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
+
+ From Charles Crain:
+
+ - Fixed a bug with relative CPPPATH dirs when using BuildDir().
+ (Bug reported by Bob Summerwill.)
+
+ - Added a warnings framework and a --warn option to enable or
+ disable warnings.
+
+ - Make the C scanner warn users if files referenced by #include
+ directives cannot be found and --warn=dependency is specified.
+
+ - The BUILDERS construction variable should now be a dictionary
+ that maps builder names to actions. Existing uses of lists,
+ and the Builder name= keyword argument, generate warnings
+ about use of deprecated features.
+
+ - Removed the "shared" keyword argument from the Object and
+ Library builders.
+
+ - Added separated StaticObject, SharedObject, StaticLibrary and
+ SharedLibrary builders. Made Object and Library synonyms for
+ StaticObject and StaticLibrary, respectively.
+
+ - Add LIBS and LIBPATH dependencies for shared libraries.
+
+ - Removed support for the prefix, suffix and src_suffix arguments
+ to Builder() to be callable functions.
+
+ - Fix handling file names with multiple dots.
+
+ - Allow a build directory to be outside of the SConstruct tree.
+
+ - Add a FindFile() function that searches for a file node with a
+ specified name.
+
+ - Add $CPPFLAGS to the shared-object command lines for g++ and gcc.
+
+ From Charles Crain and Steven Knight:
+
+ - Add a "tools=" keyword argument to Environment instantiation,
+ and a separate Tools() method, for more flexible specification
+ of tool-specific environment changes.
+
+ From Steven Knight:
+
+ - Add a "platform=" keyword argument to Environment instantiation,
+ and a separate Platform() method, for more flexible specification
+ of platform-specific environment changes.
+
+ - Updated README instructions and setup.py code to catch an
+ installation failure from not having distutils installed.
+
+ - Add descriptions to the -H help text for -D, -u and -U so
+ people can tell them apart.
+
+ - Remove the old feature of automatically splitting strings
+ of file names on white space.
+
+ - Add a dependency Scanner for native Fortran "include" statements,
+ using a new "F77PATH" construction variable.
+
+ - Fix C #include scanning to detect file names with characters like
+ '-' in them.
+
+ - Add more specific version / build output to the -v option.
+
+ - Add support for the GNU as, Microsoft masm, and nasm assemblers.
+
+ - Allow the "target" argument to a Builder call to be omitted, in
+ which case the target(s) are deduced from the source file(s) and the
+ Builder's specified suffix.
+
+ - Add a tar archive builder.
+
+ - Add preliminary support for the OS/2 Platform, including the icc
+ and ilink Tools.
+
+ From Jeff Petkau:
+
+ - Fix --implicit-cache if the scanner returns an empty list.
+
+ From Anthony Roach:
+
+ - Add a "multi" keyword argument to Builder creation that specifies
+ it's okay to call the builder multiple times for a target.
+
+ - Set a "multi" on Aliases so multiple calls will append to an Alias.
+
+ - Fix emitter functions' use of path names when using BuildDir or
+ in subdirectories.
+
+ - Fix --implicit-cache causing redundant rebuilds when the header
+ file list changed.
+
+ - Fix --implicit-cache when a file has no implicit dependencies and
+ its source is generated.
+
+ - Make the drive letters on Windows always be the same case, so that
+ changes in the case of drive letters don't cause a rebuild.
+
+ - Fall back to importing the SCons.TimeStamp module if the SCons.MD5
+ module can't be imported.
+
+ - Fix interrupt handling to guarantee that a single interrupt will
+ halt SCons both when using -j and not.
+
+ - Fix .sconsign signature storage so that output files of one build
+ can be safely used as input files to another build.
+
+ - Added a --debug=time option to print SCons execution times.
+
+ - Print an error message if a file can't be unlinked before being
+ built, rather than just silently terminating the build.
+
+ - Add a SideEffect() method that can be used to tell the build
+ engine that a given file is created as a side effect of building
+ a target. A file can be specified as a side effect of more than
+ one build comand, in which case the commands will not be executed
+ simultaneously.
+
+ - Significant performance gains from not using our own version of
+ the inefficient stock os.path.splitext() method, caching source
+ suffix computation, code cleanup in MultiStepBuilder.__call__(),
+ and replicating some logic in scons_subst().
+
+ - Add --implicit-deps-changed and --implicit-deps-unchanged options.
+
+ - Add a GetLaunchDir() function.
+
+ - Add a SetBuildSignatureType() function.
+
+ From Zed Shaw:
+
+ - Add an Append() method to Environments, to append values to
+ construction variables.
+
+ - Change the name of Update() to Replace(). Keep Update() as a
+ deprecated synonym, at least for now.
+
+ From Terrel Shumway:
+
+ - Use a $PYTHON construction variable, initialized to sys.executable,
+ when using Python to build parts of the SCons packages.
+
+ - Use sys.prefix, not sys.exec_prefix, to find pdb.py.
+
+
+
+RELEASE 0.07 - Thu, 2 May 2002 13:37:16 -0500
+
+ From Chad Austin:
+
+ - Changes to build SCons packages on IRIX (and other *NIces).
+
+ - Don't create a directory Node when a file already exists there,
+ and vice versa.
+
+ - Add 'dirs' and 'names' keyword arguments to SConscript for
+ easier specification of subsidiary SConscript files.
+
+ From Charles Crain:
+
+ - Internal cleanup of environment passing to function Actions.
+
+ - Builders can now take arbitrary keyword arguments to create
+ attributes to be passed to: command generator functions,
+ FunctionAction functions, Builder emitter functions (below),
+ and prefix/suffix generator functions (below).
+
+ - Command generator functions can now return ANYTHING that can be
+ converted into an Action (a function, a string, a CommandGenerator
+ instance, even an ActionBase instance).
+
+ - Actions now call get_contents() with the actual target and source
+ nodes used for the build.
+
+ - A new DictCmdGenerator class replaces CompositeBuilder to support
+ more flexible Builder behavior internally.
+
+ - Builders can now take an emitter= keyword argument. An emitter
+ is a function that takes target, source, and env argument, then
+ return a 2-tuple of (new sources, new targets). The emitter is
+ called when the Builder is __call__'ed, allowing a user to modify
+ source and target lists.
+
+ - The prefix, suffix and src_suffix Builder arguments now take a
+ callable as well a string. The callable is passed the Environment
+ and any extra Builder keyword arguments and is expected to return
+ the appropriate prefix or suffix.
+
+ - CommandActions can now be a string, a list of command + argument
+ strings, or a list of commands (strings or lists).
+
+ - Added shared library support. The Object and Library Builders now
+ take a "shared=1" keyword argument to specify that a shared object
+ or shared library should be built. It is an error to try to build
+ static objects into a shared library or vice versa.
+
+ - Win32 support for .def files has been added. Added the Win32-specific
+ construction variables $WIN32DEFPREFIX, $WIN32DEFSUFFIX,
+ $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX. When building a .dll,
+ the new construction variable $WIN32_INSERT_DEF, controls whether
+ the appropriately-named .def file is inserted into the target
+ list (if not already present). A .lib file is always added to
+ a Library build if not present in the list of targets.
+
+ - ListBuilder now passes all targets to the action, not just the first.
+
+ - Fix so that -c now deletes generated yacc .h files.
+
+ - Builder actions and emitter functions can now be initialized, through
+ construction variables, to things other than strings.
+
+ - Make top-relative '#/dir' lookups work like '#dir'.
+
+ - Fix for relative CPPPATH directories in subsidiary SConscript files
+ (broken in 0.06).
+
+ - Add a for_signature argument to command generators, so that
+ generators that need to can return distinct values for the
+ command signature and for executing the command.
+
+ From Alex Jacques:
+
+ - Create a better scons.bat file from a py2bat.py script on the Python
+ mailing list two years ago (modeled after pl2bat.pl).
+
+ From Steven Knight:
+
+ - Fix so that -c -n does *not* remove the targets!
+
+ - Man page: Add a hierarchical libraries + Program example.
+
+ - Support long MSVC linker command lines through a builder action
+ that writes to a temporary file and uses the magic MSVC "link @file"
+ argument syntax if the line is longer than 2K characters.
+
+ - Fix F77 command-line options on Win32 (use /Fo instead of -o).
+
+ - Use the same action to build from .c (lower case) and .C (upper
+ case) files on case-insensitive systems like Win32.
+
+ - Support building a PDF file directly from a TeX or LaTeX file
+ using pdftex or pdflatex.
+
+ - Add a -x option to runtest.py to specify the script being tested.
+ A -X option indicates it's an executable, not a script to feed
+ to the Python interpreter.
+
+ - Add a Split() function (identical to SCons.Util.argmunge()) for use
+ in the next release, when Builders will no longer automatically split
+ strings on white space.
+
+ From Steve Leblanc:
+
+ - Add the SConscriptChdir() method.
+
+ From Anthony Roach:
+
+ - Fix --debug=tree when used with directory targets.
+
+ - Significant internal restructuring of Scanners and Taskmaster.
+
+ - Added new --debug=dtree option.
+
+ - Fixes for --profile option.
+
+ - Performance improvement in construction variable substitution.
+
+ - Implemented caching of content signatures, plus added --max-drift
+ option to control caching.
+
+ - Implemented caching of dependency signatures, enabled by new
+ --implicit-cache option.
+
+ - Added abspath construction variable modifier.
+
+ - Added $SOURCE variable as a synonym for $SOURCES[0].
+
+ - Write out .sconsign files on error or interrupt so intermediate
+ build results are saved.
+
+ - Change the -U option to -D. Make a new -U that builds just the
+ targets from the local SConscript file.
+
+ - Fixed use of sys.path so Python modules can be imported from
+ the SConscript directory.
+
+ - Fix for using Aliases with the -u, -U and -D options.
+
+ - Fix so that Nodes can be passed to SConscript files.
+
+ From Moshe Zadka:
+
+ - Changes for official Debian packaging.
+
+
+
+RELEASE 0.06 - Thu, 28 Mar 2002 01:24:29 -0600
+
+ From Charles Crain:
+
+ - Fix command generators to expand construction variables.
+
+ - Make FunctionAction arguments be Nodes, not strings.
+
+ From Stephen Kennedy:
+
+ - Performance: Use a dictionary, not a list, for a Node's parents.
+
+ From Steven Knight:
+
+ - Add .zip files to the packages we build.
+
+ - Man page: document LIBS, fix a typo, document ARGUMENTS.
+
+ - Added RANLIB and RANLIBFLAGS construction variables. Only use them
+ in ARCOM if there's a "ranlib" program on the system.
+
+ - Add a configurable CFILESUFFIX for the Builder of .l and .y files
+ into C files.
+
+ - Add a CXXFile Builder that turns .ll and .yy files into .cc files
+ (configurable via a CXXFILESUFFIX construction variable).
+
+ - Use the POSIX-standard lex -t flag, not the GNU-specific -o flag.
+ (Bug reported by Russell Christensen.)
+
+ - Fixed an exception when CPPPATH or LIBPATH is a null string.
+ (Bug reported by Richard Kiss.)
+
+ - Add a --profile=FILE option to make profiling SCons easier.
+
+ - Modify the new DVI builder to create .dvi files from LaTeX (.ltx
+ and .latex) files.
+
+ - Add support for Aliases (phony targets).
+
+ - Add a WhereIs() method for searching for path names to executables.
+
+ - Add PDF and PostScript document builders.
+
+ - Add support for compiling Fortran programs from a variety of
+ suffixes (a la GNU Make): .f, .F, .for, .FOR, .fpp and .FPP
+
+ - Support a CPPFLAGS variable on all default commands that use the
+ C preprocessor.
+
+ From Steve Leblanc:
+
+ - Add support for the -U option.
+
+ - Allow CPPPATH, LIBPATH and LIBS to be specified as white-space
+ separated strings.
+
+ - Add a document builder to create .dvi files from TeX (.tex) files.
+
+ From Anthony Roach:
+
+ - Fix: Construction variables with values of 0 were incorrectly
+ interpolated as ''.
+
+ - Support env['VAR'] to fetch construction variable values.
+
+ - Man page: document Precious().
+
+
+
+RELEASE 0.05 - Thu, 21 Feb 2002 16:50:03 -0600
+
+ From Chad Austin:
+
+ - Set PROGSUFFIX to .exe under Cygwin.
+
+ From Charles Crain:
+
+ - Allow a library to specified as a command-line source file, not just
+ in the LIBS construction variable.
+
+ - Compensate for a bug in os.path.normpath() that returns '' for './'
+ on WIN32.
+
+ - More performance optimizations: cache #include lines from files,
+ eliminate unnecessary calls.
+
+ - If a prefix or suffix contains white space, treat the resulting
+ concatenation as separate arguments.
+
+ - Fix irregularities in the way we fetch DevStudio information from
+ the Windows registry, and in our registry error handling.
+
+ From Steven Knight:
+
+ - Flush stdout after print so it intermixes correctly with stderr
+ when redirected.
+
+ - Allow Scanners to return a list of strings, and document how to
+ write your own Scanners.
+
+ - Look up implicit (scanned) dependencies relative to the directory
+ of file being scanned.
+
+ - Make writing .sconsign files more robust by first trying to write
+ to a temp file that gets renamed.
+
+ - Create all of the directories for a list of targets before trying
+ to build any of the targets.
+
+ - WIN32 portability fixes in tests.
+
+ - Allow the list of variables exported to an SConscript file to be
+ a UserList, too.
+
+ - Document the overlooked LIBPATH construction variable.
+ (Bug reported by Eicke Godehardt.)
+
+ - Fix so that Ignore() ignores indirect, implicit dependencies
+ (included files), not just direct dependencies.
+
+ - Put the man page in the Debian distribution.
+
+ - Run HTML docs through tidy to clean up the HTML (for Konqueror).
+
+ - Add preliminary support for Unicode strings.
+
+ - Efficiency: don't scan dependencies more than once during the
+ walk of a tree.
+
+ - Fix the -c option so it doesn't stop removing targets if one doesn't
+ already exist.
+ (Bug reported by Paul Connell.)
+
+ - Fix the --debug=pdb option when run on Windows NT.
+ (Bug reported by Paul Connell.)
+
+ - Add support for the -q option.
+
+ From Steve Leblanc:
+
+ - Add support for the -u option.
+
+ - Add .cc and .hh file suffixes to the C Scanner.
+
+ From Anthony Roach:
+
+ - Make the scons script return an error code on failures.
+
+ - Add support for using code to generate a command to build a target.
+
+
+
+RELEASE 0.04 - Wed, 30 Jan 2002 11:09:42 -0600
+
+ From Charles Crain:
+
+ - Significant performance improvements in the Node.FS and
+ Scanner subsystems.
+
+ - Fix signatures of binary files on Win32 systems.
+
+ - Allow LIBS and LIBPATH to be strings, not just arrays.
+
+ - Print a traceback if a Python-function builder throws an exception.
+
+ From Steven Knight:
+
+ - Fix using a directory as a Default(), and allow Default() to
+ support white space in file names for strings in arrays.
+
+ - Man page updates: corrected some mistakes, documented various
+ missing Environment methods, alphabetized the construction
+ variables and other functions, defined begin and end macros for
+ the example sections, regularized white space separation, fixed
+ the use of Export() in the Multiple Variants example.
+
+ - Function action fixes: None is now a successful return value.
+ Exceptions are now reported. Document function actions.
+
+ - Add 'Action' and 'Scanner' to the global keywords so SConscript
+ files can use them too.
+
+ - Removed the Wrapper class between Nodes and Walkers.
+
+ - Add examples using Library, LIBS, and LIBPATH.
+
+ - The C Scanner now always returns a sorted list of dependencies
+ so order changes don't cause unnecessary rebuilds.
+
+ - Strip $(-$) bracketed text from command lines. Use this to
+ surround $_INCDIRS and $_LIBDIRS so we don't rebuild in response
+ to changes to -I or -L options.
+
+ - Add the Ignore() method to ignore dependencies.
+
+ - Provide an error message when a nonexistent target is specified
+ on the command line.
+
+ - Remove targets before building them, and add an Environment
+ Precious() method to override that.
+
+ - Eliminate redundant calls to the same builder when the target is a
+ list of targets: Add a ListBuilder class that wraps Builders to
+ handle lists atomically. Extend the Task class to support building
+ and updating multiple targets in a single Task. Simplify the
+ interface between Task and Taskmaster.
+
+ - Add a --debug=pdb option to re-run SCons under the Python debugger.
+
+ - Only compute a build signature once for each node.
+
+ - Changes to our sys.path[] manipulation to support installation into
+ an arbitrary --prefix value.
+
+ From Steve Leblanc:
+
+ - Add var=value command-line arguments.
+
+
+
+RELEASE 0.03 - Fri, 11 Jan 2002 01:09:30 -0600
+
+ From Charles Crain:
+
+ - Performance improvements in the Node.FS and Sig.Calculator classes.
+
+ - Add the InstallAs() method.
+
+ - Execute commands through an external interpreter (sh, cmd.exe, or
+ command.com) to handle redirection metacharacters.
+
+ - Allow the user to supply a command handler.
+
+ From Steven Knight:
+
+ - Search both /usr/lib and /usr/local/lib for scons directories by
+ adding them both to sys.path, with whichever is in sys.prefix first.
+
+ - Fix interpreting strings of multiple white-space separated file names
+ as separate file names, allowing prefixes and suffixes to be appended
+ to each individually.
+
+ - Refactor to move CompositeBuilder initialization logic from the
+ factory wrapper to the __init__() method, and allow a Builder to
+ have both an action and a src_builder (or array of them).
+
+ - Refactor BuilderBase.__call__() to separate Node creation/lookup
+ from initialization of the Node's builder information.
+
+ - Add a CFile Builder object that supports turning lex (.l) and
+ yacc (.y) files into .c files.
+
+ - Document: variable interpretation attributes; how to propogate
+ the user's environment variables to executed commands; how to
+ build variants in multiple BuildDirs.
+
+ - Collect String, Dict, and List type-checking in common utility
+ routines so we can accept User{String,Dict,List}s all over.
+
+ - Put the Action factory and classes into their own module.
+
+ - Use one CPlusPlusAction in the Object Builder's action dictionary,
+ instead of letting it create multiple identical instances.
+
+ - Document the Install() and InstallAs() methods.
+
+ From Steve Leblanc:
+
+ - Require that a Builder be given a name argument, supplying a
+ useful error message when it isn't.
+
+ From Anthony Roach:
+
+ - Add a "duplicate" keyword argument to BuildDir() that can be set
+ to prevent linking/copying source files into build directories.
+
+ - Add a "--debug=tree" option to print an ASCII dependency tree.
+
+ - Fetch the location of the Microsoft Visual C++ compiler(s) from
+ the Registry, instead of hard-coding the location.
+
+ - Made Scanner objects take Nodes, not path names.
+
+ - Have the C Scanner cache the #include file names instead of
+ (re-)scanning the file each time it's called.
+
+ - Created a separate class for parent "nodes" of file system roots,
+ eliminating the need for separate is-parent-null checks everywhere.
+
+ - Removed defined __hash__() and __cmp() methods from FS.Entry, in
+ favor of Python's more efficient built-in identity comparisons.
+
+
+
+RELEASE 0.02 - Sun, 23 Dec 2001 19:05:09 -0600
+
+ From Charles Crain:
+
+ - Added the Install(), BuildDir(), and Export() methods.
+
+ - Fix the -C option by delaying setting the top of the FS tree.
+
+ - Avoid putting the directory path on the libraries in the LIBS
+ construction variable.
+
+ - Added a GetBuildPath() method to return the full path to the
+ Node for a specified string.
+
+ - Fixed variable substitution in CPPPATH and LIBPATH.
+
+ From Steven Knight:
+
+ - Fixed the version comment in the scons.bat (the UNIX geek used
+ # instead of @rem).
+
+ - Fix to setup.py so it doesn't require a sys.argv[1] argument.
+
+ - Provide make-like warning message for "command not found" and
+ similar errors.
+
+ - Added an EXAMPLES section to the man page.
+
+ - Make Default() targets properly relative to their SConscript
+ file's subdirectory.
+
+ From Anthony Roach:
+
+ - Documented CXXFLAGS, CXXCOM, and CPPPATH.
+
+ - Fixed SCONS_LIB_DIR to work as documented.
+
+ - Made Default() accept Nodes as arguments.
+
+ - Changed Export() to make it easier to use.
+
+ - Added the Import() and Return() methods.
+
+
+
+RELEASE 0.01 - Thu Dec 13 19:25:23 CST 2001
+
+A brief overview of important functionality available in release 0.01:
+
+ - C and C++ compilation on POSIX and Windows NT.
+
+ - Automatic scanning of C/C++ source files for #include dependencies.
+
+ - Support for building libraries; setting construction variables
+ allows creation of shared libraries.
+
+ - Library and C preprocessor search paths.
+
+ - File changes detected using MD5 signatures.
+
+ - User-definable Builder objects for building files.
+
+ - User-definable Scanner objects for scanning for dependencies.
+
+ - Parallel build (-j) support.
+
+ - Dependency cycles detected.
+
+ - Linux packages available in RPM and Debian format.
+
+ - Windows installer available.
diff --git a/src/LICENSE.txt b/src/LICENSE.txt
new file mode 100644
index 0000000..67f1906
--- /dev/null
+++ b/src/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/MANIFEST.in b/src/MANIFEST.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/MANIFEST.in
diff --git a/src/README.txt b/src/README.txt
new file mode 100644
index 0000000..c347136
--- /dev/null
+++ b/src/README.txt
@@ -0,0 +1,273 @@
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+# src/README.txt 4577 2009/12/27 19:44:43 scons
+
+
+ SCons - a software construction tool
+
+ Version 1.2.0.d20091224
+
+
+This is SCons, a tool for building software (and other files). SCons is
+implemented in Python, and its "configuration files" are actually Python
+scripts, allowing you to use the full power of a real scripting language
+to solve build problems. You do not, however, need to know Python to
+use SCons effectively.
+
+See the RELEASE.txt file for notes about this specific release,
+including known problems. See the CHANGES.txt file for a list of
+changes since the previous release.
+
+
+LATEST VERSION
+==============
+
+Before going further, you can check that this package you have is
+the latest version by checking the SCons download page at:
+
+ http://www.scons.org/download.html
+
+
+EXECUTION REQUIREMENTS
+======================
+
+Running SCons requires Python version 1.5.2 or later. There should be
+no other dependencies or requirements to run SCons. (There is, however,
+an additional requirement to *install* SCons from this particular
+package; see the next section.)
+
+By default, SCons knows how to search for available programming tools
+on various systems--see the SCons man page for details. You may,
+of course, override the default SCons choices made by appropriate
+configuration of Environment construction variables.
+
+
+INSTALLATION REQUIREMENTS
+=========================
+
+Installing SCons from this package requires the Python distutils
+package. The distutils package was not shipped as a standard part of
+Python until Python version 1.6, so if your system is running Python
+1.5.2, you may not have distutils installed. If you are running
+Python version 1.6 or later, you should be fine.
+
+NOTE TO RED HAT USERS: Red Hat shipped Python 1.5.2 as the default all
+the way up to Red Hat Linux 7.3, so you probably do *not* have distutils
+installed, unless you have already done so manually or are running Red
+Hat 8.0 or later.
+
+In this case, your options are:
+
+ -- (Recommended.) Install from a pre-packaged SCons package that
+ does not require distutils:
+
+ Red Hat Linux scons-1.2.0.d20091224-1.noarch.rpm
+
+ Debian GNU/Linux scons_1.2.0.d20091224-1_all.deb
+ (or use apt-get)
+
+ Windows scons-1.2.0.d20091224.win32.exe
+
+ -- (Optional.) Download the latest distutils package from the
+ following URL:
+
+ http://www.python.org/sigs/distutils-sig/download.html
+
+ Install the distutils according to the instructions on the page.
+ You can then proceed to the next section to install SCons from
+ this package.
+
+
+INSTALLATION
+============
+
+Assuming your system satisfies the installation requirements in the
+previous section, install SCons from this package simply by running the
+provided Python-standard setup script as follows:
+
+ # python setup.py install
+
+By default, the above command will do the following:
+
+ -- Install the version-numbered "scons-1.2.0.d20091224" and "sconsign-1.2.0.d20091224"
+ scripts in the default system script directory (/usr/bin or
+ C:\Python*\Scripts, for example). This can be disabled by
+ specifying the "--no-version-script" option on the command
+ line.
+
+ -- Install scripts named "scons" and "sconsign" scripts in the
+ default system script directory (/usr/bin or C:\Python*\Scripts,
+ for example). This can be disabled by specifying the
+ "--no-scons-script" option on the command line, which is useful
+ if you want to install and experiment with a new version before
+ making it the default on your system.
+
+ On UNIX or Linux systems, you can have the "scons" and "sconsign"
+ scripts be hard links or symbolic links to the "scons-1.2.0.d20091224" and
+ "sconsign-1.2.0.d20091224" scripts by specifying the "--hardlink-scons"
+ or "--symlink-scons" options on the command line.
+
+ -- Install "scons-1.2.0.d20091224.bat" and "scons.bat" wrapper scripts in the
+ Python prefix directory on Windows (C:\Python*, for example).
+ This can be disabled by specifying the "--no-install-bat" option
+ on the command line.
+
+ On UNIX or Linux systems, the "--install-bat" option may be
+ specified to have "scons-1.2.0.d20091224.bat" and "scons.bat" files
+ installed in the default system script directory, which is useful
+ if you want to install SCons in a shared file system directory
+ that can be used to execute SCons from both UNIX/Linux and
+ Windows systems.
+
+ -- Install the SCons build engine (a Python module) in an
+ appropriate version-numbered SCons library directory
+ (/usr/lib/scons-1.2.0.d20091224 or C:\Python*\scons-1.2.0.d20091224, for example).
+ See below for more options related to installing the build
+ engine library.
+
+ -- Install the troff-format man pages in an appropriate directory
+ on UNIX or Linux systems (/usr/share/man/man1 or /usr/man/man1,
+ for example). This can be disabled by specifying the
+ "--no-install-man" option on the command line. The man pages
+ can be installed on Windows systems by specifying the
+ "--install-man" option on the command line.
+
+Note that, by default, SCons does not install its build engine library
+in the standard Python library directories. If you want to be able to
+use the SCons library modules (the build engine) in other Python
+scripts, specify the "--standard-lib" option on the command line, as
+follows:
+
+ # python setup.py install --standard-lib
+
+This will install the build engine in the standard Python library
+directory (/usr/lib/python*/site-packages or
+C:\Python*\Lib\site-packages).
+
+Alternatively, you can have SCons install its build engine library in a
+hard-coded standalone library directory, instead of the default
+version-numbered directory, by specifying the "--standalone-lib" option
+on the command line, as follows:
+
+ # python setup.py install --standalone-lib
+
+This is usually not recommended, however.
+
+Note that, to install SCons in any of the above system directories,
+you should have system installation privileges (that is, "root" or
+"Administrator") when running the setup.py script. If you don't have
+system installation privileges, you can use the --prefix option to
+specify an alternate installation location, such as your home directory:
+
+ $ python setup.py install --prefix=$HOME
+
+This will install SCons in the appropriate locations relative to
+$HOME--that is, the scons script itself $HOME/bin and the associated
+library in $HOME/lib/scons, for example.
+
+
+DOCUMENTATION
+=============
+
+See the RELEASE.txt file for notes about this specific release,
+including known problems. See the CHANGES.txt file for a list of
+changes since the previous release.
+
+The scons.1 man page is included in this package, and contains a section
+of small examples for getting started using SCons.
+
+Additional documentation for SCons is available at:
+
+ http://www.scons.org/doc.html
+
+
+LICENSING
+=========
+
+SCons is distributed under the MIT license, a full copy of which is
+available in the LICENSE.txt file. The MIT license is an approved Open
+Source license, which means:
+
+ This software is OSI Certified Open Source Software. OSI
+ Certified is a certification mark of the Open Source Initiative.
+
+More information about OSI certifications and Open Source software is
+available at:
+
+ http://www.opensource.org/
+
+
+REPORTING BUGS
+==============
+
+Please report bugs by following the detailed instructions on our Bug
+Submission page:
+
+ http://scons.tigris.org/bug-submission.html
+
+You can also send mail to the SCons developers' mailing list:
+
+ dev@scons.tigris.org
+
+But even if you send email to the mailing list please make sure that you
+ALSO submit a bug report to the project page bug tracker, because bug
+reports in email often get overlooked in the general flood of messages.
+
+
+MAILING LISTS
+=============
+
+An active mailing list for users of SCons is available. You may send
+questions or comments to the list at:
+
+ users@scons.tigris.org
+
+You may subscribe to the mailing list by sending email to:
+
+ users-subscribe@scons.tigris.org
+
+There is also a low-volume mailing list available for announcements
+about SCons. Subscribe by sending email to:
+
+ announce-subscribe@scons.tigris.org
+
+There are other mailing lists available for SCons developers, for
+notification of SCons code changes, and for notification of updated
+bug reports and project documents. Please see our mailing lists page
+for details.
+
+
+DONATIONS
+=========
+
+If you find SCons helpful, please consider making a donation (of cash,
+software, or hardware) to support continued work on the project.
+Information is available at:
+
+ http://www.scons.org/donate.html
+
+
+FOR MORE INFORMATION
+====================
+
+Check the SCons web site at:
+
+ http://www.scons.org/
+
+
+AUTHOR INFO
+===========
+
+Steven Knight
+knight at baldmt dot com
+http://www.baldmt.com/~knight/
+
+With plenty of help from the SCons Development team:
+ Chad Austin
+ Charles Crain
+ Steve Leblanc
+ Greg Noel
+ Gary Oberbrunner
+ Anthony Roach
+ Greg Spencer
+ Christoph Wiedemann
+
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
new file mode 100644
index 0000000..94c7f26
--- /dev/null
+++ b/src/RELEASE.txt
@@ -0,0 +1,1016 @@
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+# src/RELEASE.txt 4577 2009/12/27 19:44:43 scons
+
+
+ SCons - a software construction tool
+
+ Release Notes
+
+
+This is SCons, a tool for building software (and other files). SCons is
+implemented in Python, and its "configuration files" are actually Python
+scripts, allowing you to use the full power of a real scripting language
+to solve build problems. You do not, however, need to know Python to
+use SCons effectively.
+
+So that everyone using SCons can help each other learn how to use it
+more effectively, please sign up for the scons-users mailing list at:
+
+ http://lists.sourceforge.net/lists/listinfo/scons-users
+
+
+
+RELEASE 1.2.0.d20091224 - Thu, 24 Dec 2009 17:20:55 -0800
+
+ Please consult the CHANGES.txt file for a list of specific changes
+ since last release.
+
+ Please note the following important changes scheduled for the next
+ minor release (1.3.0):
+
+ -- DEPRECATED FEATURES WILL GENERATE MANDATORY WARNINGS IN 1.3.0
+
+ In keeping with our deprecation cycle, the following deprecated
+ features will still be supported in 1.3.0 but will generate
+ mandatory, non-disableable warnings:
+
+ -- Support for Python versions 1.5, 1.6, 2.0, 2.1, 2.2, and 2.3.
+ -- The overrides= keyword argument to the Builder() call.
+ -- The scanner= keyword argument to the Builder() call.
+ -- The BuildDir() function and env.BuildDir() method.
+ -- The env.Copy() method.
+ -- The SourceSignatures() function and
+ env.SourceSignatures() method.
+ -- The TargetSignatures() function and
+ env.TargetSignatures() method.
+ -- The Sig module (now an unnused stub).
+ -- The --debug=dtree, --debug=stree and --debug=tree options.
+ -- The --debug=nomemoizer option.
+ -- The Options object and the related BoolOption(),
+ EnumOption(), ListOption(), PackageOption() and
+ PathOption() functions.
+
+ The mandatory warnings will be issued in order to make sure
+ users of 1.3.0 notice *prior* to the release of SCons 2.0.0, that
+ these features will be removed. In SCons 2.0.0 these features
+ will no longer work at all, and will instead generate specific
+ fatal errors when anyone tries to use them.
+
+ Please note the following important changes since release 1.2.0:
+ -- Visual Studio/Visual C++ support has been change significantly
+
+ Support for HOST_OS,HOST_ARCH,TARGET_OS, TARGET_ARCH has been
+ added to allow specifying different target arch than the host
+ system. This is only supported for Visual Studio/Visual C++
+ at this time.
+
+ -- Support for Latex glosseries and acronyms has been added
+
+ -- VISUAL C/C++ PRECOMPILED HEADERS WILL BE REBUILT
+
+ Precompiled header files built with Visual C/C++ will be
+ rebuilt after upgrading from 1.2.0 to a later release.
+
+ This rebuild is normal and will occur because the command line
+ defined by the $PCHCOM construction variable has had the $CCFLAGS
+ variable added, and has been rearranged to put the "/Fo" output
+ flag towards the beginning of the line, consistent with the
+ related command lines for $CCCOM, $CXXCOM, etc.
+
+ -- CHANGES TO SOME LINKER COMMAND LINES WILL CAUSE RELINKING
+
+ Changes to the command line definitions for the Microsoft link.exe
+ linker, the OS/2 ilink linker and the Phar Lap linkloc linker
+ will cause targets built with those tools be to be rebuilt after
+ upgrading from 1.2.0 to a later release.
+
+ This relink is normal and will occur because the command lines for
+ these tools have been redefined to remove unnecessary nested $(
+ and $) character strings.
+
+ Please note the following important changes since release 1.1.0:
+
+ -- THE $CHANGED_SOURCES, $CHANGED_TARGETS, $UNCHANGED_SOURCES
+ AND $UNCHANGED_TARGETS VARIABLES WILL BECOME RESERVED
+
+ A future release (probably 1.3.0) will make the construction
+ variable names $CHANGED_SOURCES, $CHANGED_TARGETS,
+ $UNCHANGED_SOURCES and $UNCHANGED_TARGETS into reserved
+ construction variable names controlled by SCons itself (like
+ the current $SOURCE, $TARGETS, etc.).
+
+ Setting these variable names in the current release will generate
+ a warning but still set the variables. When they become reserved
+ variable names, they will generate a different warning message
+ and attempts to set these variables will be ignored.
+
+ SCons configurations that happen to use these variable names
+ should be changed to use different variable names, in order
+ to ensure that the configuration continues to work with future
+ versions of SCons.
+
+ -- THE Options OBJECT AND RELATED FUNCTIONS NOW GENERATE WARNINGS
+
+ Use of the Options object, and related functions BoolOption(),
+ EnumOption(), ListOption(), PackageOption() and PathOption()
+ were announced as deprecated in release 0.98.1. Since then,
+ however, no warning messages were ever implemented for the
+ use of these deprecated functions.
+
+ By default, release 1.2.0 prints warning messages when these
+ deprecated features are used. Warnings about all deprecated
+ features may be suppressed by using the --warn=no-deprecated
+ command-line option:
+
+ $ scons --warn=no-deprecated
+
+ Or by using the appropriate SetOption() call in any SConscript
+ file:
+
+ SetOption('warn', 'no-deprecated')
+
+ You may optionally disable just warnings about the deprecation
+ of the Options object and its related functions as follows:
+
+ SetOption('warn', 'no-deprecated-options')
+
+ The current plan is for these warnings to become mandatory
+ (non-suppressible) in release 1.3.0, and for the use of Options
+ and its related functions to generate errors in release 2.0.
+
+ Please note the following important changes since release 0.98.4:
+
+ -- scons.bat NOW RETURNS THE REAL SCONS EXIT STATUS
+
+ The scons.bat script shipped with SCons used to exit with
+ a status of 1 when it detected any failed (non-zero) exit
+ status from the underlying Python execution of SCons itself.
+ The scons.bat script now exits with the actual status
+ returned by SCons.
+
+ -- SCONS NOW WARNS WHEN TRYING TO LINK C++ AND FORTRAN OBJECT FILES
+
+ Some C++ toolchains do not understand Fortran runtimes and create
+ unpredictable executables when linking C++ and Fortran object
+ files together. SCons now issues a warning if you try to link
+ C++ and Fortran object files into the same executable:
+
+ scons: warning: Using $CXX to link Fortran and C++ code together.
+ This may generate a buggy executable if the '/usr/bin/gcc'
+ compiler does not know how to deal with Fortran runtimes.
+
+ The warning may be suppressed with either the --warning=no-link
+ or --warning=no-fortran-cxx-mix command line options, or by
+ adding either of the following lines to a SConscript file:
+
+ SetOption('warn', 'no-link')
+ SetOption('warn', 'no-fortran-cxx-mix')
+
+ Please note the following important changes since release 0.98:
+
+ -- SCONS NO LONGER SETS THE GNU TOOLCHAIN -fPIC FLAG IN $SHCXXFLAGS
+
+ The GNU toolchain support in previous versions of SCons would
+ add the -fPIC flag to the $SHCXXFLAGS construction variable.
+ The -fPIC flag has been now been removed from the default
+ $SHCXXFLAGS setting. Instead, the $SHCXXCOM construction variable
+ (the default SCons command line for compiling shared objects
+ from C++ source files) has been changed to add the $SHCCFLAGS
+ variable, which contains the -fPIC flag.
+
+ This change was made in order to make the behavior of the default
+ C++ compilation line including $SHCCFLAGS consistent with the
+ default C compilation line including $CCFLAGS.
+
+ This change should have no impact on configurations that use
+ the default $SHCXXCOM command line. It may have an impact on
+ configurations that were using the default $SHCXXFLAGS value
+ *without* the $SHCCFLAGS variable to get the -fPIC flag into a
+ custom command line. You can fix these by adding the $SHCCFLAGS
+ to the custom command line.
+
+ Adding $SHCCFLAGS is backwards compatible with older SCons
+ releases, although it might cause the -fPIC flag to be repeated
+ on the command line if you execute it on an older version of
+ SCons that sets -fPIC in both the $SHCCLAFGS and $SHCXXFLAGS
+ variables. Duplicating the -fPIC flag on the g++ command line
+ will not cause any compilation problems, but the change to the
+ command line may cause SCons to rebuild object files.
+
+ -- FORTRAN NOW COMPILES .f FILES WITH gfortran BY DEFAULT
+
+ The Fortran Tool modules have had a major overhaul with the intent
+ of making them work as-is for most configurations. In general,
+ most configurations that use default settings should not see
+ any noticeable difference.
+
+ One configuration that has changed is if you have both a gfortran
+ and g77 compiler installed. In this case, previous versions of
+ SCons would, by default, use g77 by default to compile files with
+ a .f suffix, while SCons 0.98.1 will use the gfortran compiler
+ by default. The old behavior may be preserved by explicitly
+ initializing construction environments with the 'g77' Tool module:
+
+ env = Environment(tools = ['g77', 'default'])
+
+ The above code is backwards compatible to older versions of SCons.
+
+ If you notice any other changes in the behavior of default
+ Fortran support, please let us know so we can document them in
+ these release notes for other users.
+
+ Please note the following important changes since release 0.97.0d20071212:
+
+ -- SUPPORT FOR PYTHON VERSIONS BEFORE 2.2 IS NOW DEPRECATED
+
+ SCons now prints the following warning when it is run by any
+ Python 1.5, 2.0 or 2.1 release or sub-release:
+
+ scons: warning: Support for pre-2.2 Python (VERSION) is deprecated.
+ If this will cause hardship, contact dev@scons.tigris.org.
+
+ You may disable all warnings about deprecated features by adding
+ the option "--warn=no-deprecated" to the command line or to the
+ $SCONSFLAGS environment variable:
+
+ $ scons --warn=no-deprecated
+
+ Using '--warn=no-deprecated' is compatible with earlier versions
+ of SCons.
+
+ You may also, as of this version of SCons, disable all warnings
+ about deprecated features by adding the following to any
+ SConscript file:
+
+ SetOption('warn', 'no-deprecated')
+
+ You may disable only the specific warning about running under
+ a deprecated Python version by adding the following to any
+ SConscript file:
+
+ SetOption('warn', 'no-python-version')
+
+ The warning may also be suppressed on the command line:
+
+ $ scons --warn=no-python-version
+
+ Or by specifying the --warn=no-python-version option in the
+ $SCONSFLAGS environment variable.
+
+ Using SetOption('warn', ...), and the 'no-python-version'
+ command-line option for suppressing this specific warning,
+ are *not* backwards-compatible to earlier versions of SCons.
+
+ -- THE env.Copy() METHOD IS NOW OFFICIALLY DEPRECATED
+
+ The env.Copy() method is now officially deprecated and will
+ be removed in a future release. Using the env.Copy() method
+ now generates the following message:
+
+ scons: warning: The env.Copy() method is deprecated; use the env.Clone() method instead.
+
+ You may disable all warnings about deprecated features by adding
+ the option "--warn=no-deprecated" to the command line or to the
+ $SCONSFLAGS environment variable:
+
+ $ scons --warn=no-deprecated
+
+ Using '--warn=no-deprecated' is compatible with earlier versions
+ of SCons.
+
+ You may also, as of this version of SCons, disable all warnings
+ about deprecated features by adding the following to any
+ SConscript file:
+
+ SetOption('warn', 'no-deprecated')
+
+ You may disable only the specific warning about the deprecated
+ env.Copy() method by adding the following to any SConscript
+ file:
+
+ SetOption('warn', 'no-deprecated-copy')
+
+ The warning may also be suppressed on the command line:
+
+ $ scons --warn=no-deprecated-copy
+
+ Or by specifying the --warn=no-deprecated-copy option in the
+ $SCONSFLAGS environment variable.
+
+ Using SetOption('warn', ...), and the 'no-deprecated-copy'
+ command-line option for suppressing this specific warning,
+ are *not* backwards-compatible to earlier versions of SCons.
+
+ -- THE --debug=dtree, --debug=stree AND --debug=tree OPTIONS ARE DEPRECATED
+
+ The --debug=dtree, --debug=stree and --debug=tree methods
+ are now officially deprecated and will be removed in a
+ future release. Using these options now generate a warning
+ message recommending use of the --tree=derived, --tree=all,status
+ and --tree=all options, respectively.
+
+ You may disable these warnings, and all warnings about
+ deprecated features, by adding the option "--warn=no-deprecated"
+ to the command line or to the $SCONSFLAGS environment
+ variable:
+
+ $ scons --warn=no-deprecated
+
+ Using '--warn=no-deprecated' is compatible with earlier versions
+ of SCons.
+
+ -- THE TargetSignatures() AND SourceSignatures() FUNCTIONS ARE DEPRECATED
+
+ The TargetSignatures() and SourceSignatures() functions,
+ and their corresponding env.TargetSignatures() and
+ env.SourceSignatures() methods, are now officially deprecated
+ and will be be removed in a future release. Using ahy of
+ these functions or methods now generates a message
+ similar to the following:
+
+ scons: warning: The env.TargetSignatures() method is deprecated;
+ convert your build to use the env.Decider() method instead.
+
+ You may disable all warnings about deprecated features by adding
+ the option "--warn=no-deprecated" to the command line or to the
+ $SCONSFLAGS environment variable:
+
+ $ scons --warn=no-deprecated
+
+ Using '--warn=no-deprecated' is compatible with earlier versions
+ of SCons.
+
+ You may also, as of this version of SCons, disable all warnings
+ about deprecated features by adding the following to any
+ SConscript file:
+
+ SetOption('warn', 'no-deprecated')
+
+ You may disable only the specific warning about the use of
+ TargetSignatures() or SourceSignatures() by adding the
+ following to any SConscript file:
+
+ SetOption('warn', 'no-deprecated-target-signatures')
+ SetOption('warn', 'no-deprecated-source-signatures')
+
+ The warnings may also be suppressed on the command line:
+
+ $ scons --warn=no-deprecated-target-signatures --warn=no-deprecated-source-signatures
+
+ Or by specifying these options in the $SCONSFLAGS environment
+ variable.
+
+ Using SetOption('warn', ...), or the command-line options
+ for suppressing these warnings, is *not* backwards-compatible
+ to earlier versions of SCons.
+
+ -- File(), Dir() and Entry() NOW RETURN A LIST WHEN THE INPUT IS A SEQUENCE
+
+ Previously, if these methods were passed a list, the list was
+ substituted and stringified, then passed as a single string to
+ create a File/Dir/Entry Node. This rarely if ever worked with
+ more than one element in the list. They now return a list of
+ Nodes when passed a list.
+
+ One case that works differently now is a passing in a
+ single-element sequence; that formerly was stringified
+ (returning its only element) and then a single Node would be
+ returned. Now a single-element list containing the Node will
+ be returned, for consistency.
+
+ -- THE env.subst() METHOD NOW RETURNS A LIST WHEN THE INPUT IS A SEQUENCE
+
+ The env.subst() method now returns a list with the elements
+ expanded when given a list as input. Previously, the env.subst()
+ method would always turn its result into a string.
+
+ This behavior was changed because it interfered with being able
+ to include things like lists within the expansion of variables
+ like $CPPPATH and then have SCons understand that the elements
+ of the "internal" lists still needed to be treated separately.
+ This would cause a $CPPPATH list like ['subdir1', 'subdir']
+ to show up in a command line as "-Isubdir1 subdir".
+
+ -- THE Jar() BUILDER NOW USES THE Java() BUILDER CLASSDIR BY DEFAULT
+
+ By default, the Jar() Builder will now use the class directory
+ specified when the Java() builder is called. So the following
+ input:
+
+ classes = env.Java('classes', 'src')
+ env.Jar('out.jar', classes)
+
+ Will cause "-C classes" to be passed the "jar" command invocation,
+ and the Java classes in the "out.jar" file will not be prefixed
+ "classes/".
+
+ Explicitly setting the $JARCHDIR variable overrides this default
+ behavior. The old behavior of not passing any -C option to the
+ "jar" command can be preserved by explicitly setting $JARCHDIR
+ to None:
+
+ env = Environment(JARCHDIR = None)
+
+ The above setting is compatible with older versions of SCons.
+
+ Please note the following important changes since release 0.97.0d20070918:
+
+ -- SCons REDEFINES PYTHON open() AND file() ON Windows TO NOT PASS
+ ON OPEN FILE HANDLES TO CREATED PROCESSES
+
+ On Windows systems, SCons now redefines the Python open()
+ and file() functions so that, if the Python Win32 extensions
+ are available, the file handles for any opened files will *not*
+ be inherited by subprocesses, such as the spawned compilers and
+ other tools invoked to build the software.
+
+ This prevents certain race conditions where a file handle for
+ a file opened by Python (either in a Python function action,
+ or directly in a SConscript file) could be inherited and help
+ open by a subprocess, interfering with the ability of other
+ processes to create or modify the file.
+
+ In general, this should not cause problems for the vast majority
+ of configurations. The only time this would be a problem would be
+ in the unlikely event that a process spawned by SCons specifically
+ *expected* to use an inherited file handle opened by SCons.
+
+ If the Python Win32 extensions are not installed or are an
+ earlier version that does not have the ability to disable file
+ handle inheritance, SCons will print a warning message when the
+ -j option is used. The warning message may be suppressed by
+ specifying --warn=no-parallel-support.
+
+ Please note the following important changes since release 0.97.0d20070809:
+
+ -- "content" SIGNATURES ARE NOW THE DEFAULT BEHAVIOR
+
+ The default behavior of SCons is now to use the MD5 checksum of
+ all file contents to decide if any files have changed and should
+ cause rebuilds of their source files. This means that SCons may
+ decide not to rebuild "downstream" targets if a a given input
+ file is rebuilt to the exact same contents as the last time.
+ The old behavior may preserved by explicity specifying:
+
+ TargetSignatures("build")
+
+ In any of your SConscript files.
+
+ -- TARGETS NOW IMPLICITLY DEPEND ON THE COMMAND THAT BUILDS THEM
+
+ For all targets built by calling external commands (such as a
+ compiler or other utility), SCons now adds an implicit dependency
+ on the command(s) used to build the target.
+
+ This will cause rebuilds of all targets built by external commands
+ when running SCons in a tree built by previous version of SCons,
+ in order to update the recorded signatures.
+
+ The old behavior of not having targets depend on the external
+ commands that build them can be preserved by setting a new
+ $IMPLICIT_COMMAND_DEPENDENCIES construction variable to a
+ non-True value:
+
+ env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0)
+
+ or by adding Ignore() calls for any targets where the behavior
+ is desired:
+
+ Ignore('/usr/bin/gcc', 'foo.o')
+
+ Both of these settings are compatible with older versions
+ of SCons.
+
+ -- CHANGING SourceSignature() MAY CAUSE "UNECESSARY" REBUILDS
+
+ If you change the SourceSignature() value from 'timestamp' to
+ 'MD5', SCons will now rebuild targets that were already up-to-date
+ with respect to their source files.
+
+ This will happen because SCons did not record the content
+ signatures of the input source files when the target was last
+ built--it only recorded the timestamps--and it must record them
+ to make sure the signature information is correct. However,
+ the content of source files may have changed since the last
+ timestamp build was performed, and SCons would not have any way to
+ verify that. (It would have had to open up the file and record
+ a content signature, which is one of the things you're trying to
+ avoid by specifying use of timestamps....) So in order to make
+ sure the built targets reflect the contents of the source files,
+ the targets must be rebuilt.
+
+ Change the SourceSignature() value from 'MD5' to 'timestamp'
+ should correctly not rebuild target files, because the timestamp
+ of the files is always recorded.
+
+ In previous versions of SCons, changing the SourceSignature()
+ value would lead to unpredictable behavior, usually including
+ rebuilding targets.
+
+ -- THE Return() FUNCTION NOW ACTUALLY RETURNS IMMEDIATELY
+
+ The Return() function now immediately stops processing the
+ SConscript file in which it appears and returns the values of the
+ variables named in its arguments. It used to continue processing
+ the rest of the SConscript file, and then return the values of the
+ specified variables at the point the Return() function was called.
+
+ The old behavior may be requested by adding a "stop=False"
+ keyword argument to the Return() call:
+
+ Return('value', stop=False)
+
+ The "stop=" keyword argument is *not* compatible with SCons
+ versions 0.97.0d20070809 or earlier.
+
+ Please note the following important changes since release 0.97:
+
+ -- env.CacheDir() NOW ONLY AFFECTS CONSTRUCTION ENVIRONMENT TARGETS
+
+ The env.CacheDir() method now only causes derived files to be
+ retrieved from the specified cache directory for targets built
+ with the specified specified construction environment ("env").
+
+ Previously, any call to env.CacheDir() or CacheDir() would modify
+ a global setting and cause all built targets to be retrieved
+ from the specified cache directory. This behavior was changed so
+ that env.CacheDir() would be consistent with other construction
+ environment methods, which only affect targets built with the
+ specified construction environment.
+
+ The old behavior of changing the global behavior may be preserved
+ by changing any env.CacheDir() calls to:
+
+ CacheDir('/path/to/cache/directory')
+
+ The above change is backwards-compatible and works in all earlier
+ versions of SCons that support CacheDir().
+
+ -- INTERPRETATION OF SUFFIX-LESS SOURCE ARGUMENTS HAS CHANGED
+
+ The interpretation of source arguments (files) without suffixes
+ has changed in one specific configuration.
+
+ Previously, if a Builder had a src_suffix specified (indicating
+ that source files without suffixes should have that suffix
+ appended), the suffix would only be applied to suffix-less source
+ arguments if the Builder did *not* have one or more attached
+ source Builders (that is, the Builder was not a "multi-stage"
+ Builder). So in the following configuration:
+
+ build_foo = Builder(src_suffix = '.foo')
+ build_bar = Builder(src_suffix = '.bar',
+ src_builder = build_bar)
+
+ env = Environment(BUILDERS = {
+ 'Foo' : build_foo,
+ 'Boo' : build_bar,
+ })
+
+ env.Foo('tgt1', 'src1')
+ env.Bar('tgt2', 'src2')
+
+ SCons would have expected to find a source file 'src1.foo' for the
+ env.Foo() call, but a source file 'src2' for the env.Bar() call.
+
+ This behavior has now been made consistent, so that the two
+ above calls would expect source files named 'src1.foo' and
+ 'src2.bar', respectively.
+
+ Note that, if genuinely desired, the old behavior of building
+ from a source file without a suffix at all (when the Builder has
+ a src_suffix *and* a src_builder) can be specified explicity by
+ turning the string into a File Node directly:
+
+ env.Bar('tgt2', File('src2'))
+
+ The above use of File() is backwards-compatible and will work
+ on earlier versions of SCons.
+
+ -- THE DEFAULT EXECUTION PATH FOR Solaris HAS CHANGED
+
+ On Solaris systems, SCons now adds the "/opt/SUNWspro/bin"
+ directory to the default execution $PATH variable before the
+ "/usr/ccs/bin" directory. This was done to reflect the fact
+ that /opt/SUNWspro/ is the default for SUN tools, but it may
+ cause a different compiler to be used if you have compilers
+ installed in both directories.
+
+ -- GENERATED config.h FILES NOW SAY "#define HAVE_{FEATURE} 1"
+
+ When generating a "config.h" file, SCons now defines values that
+ record the existence of a feature with a "1" value:
+
+ #define HAVE_FEATURE 1
+
+ Instead of printing the line without a "1", as it used to:
+
+ #define HAVE_FEATURE
+
+ This should not cause any problems in the normal use of "#ifdef
+ HAVE_{FEATURE}" statements interpreted by a C preprocessor, but
+ might cause a compatibility issue if a script or other utility
+ looks for an exact match of the previous text.
+
+ Please note the following planned, future changes:
+
+ -- THE Options OBJECT AND RELATED FUNCTIONS WILL BE DEPRECATED
+
+ The Options object is being replaced by a new Variables
+ object, which uses a new Variables.AddVariable() method
+ where the previous interface used Options.AddOptions().
+
+ Similarly, the following utility functions are being replaced
+ by the following similarly-named functions:
+
+ BoolOption() BoolVariable()
+ EnumOption() EnumVariable()
+ ListOption() ListVariable()
+ PackageOption() PackageVariable()
+ PathOption() PathVariable()
+
+ And also related, the options= keyword argument when creating
+ construction environments with the Environment() functions is
+ being replaced with a variables= keyword argument.
+
+ In some future release a deprecation warning will be added to
+ existing uses of the Options object, its methods, the above
+ utility functions, and the options= keyword argument of the
+ Environment() function. At some point after the deprecation
+ warning is added, the Options object, related functions and
+ options= keyword argument will be removed entirely.
+
+ You can prepare for this by changing all your uses of the Options
+ object and related functions to the Variables object and the new
+ function names, and changing any uses of the options= keyword
+ argument to variables=.
+
+ NOTE: CONVERTING TO USING THE NEW Variables OBJECT OR THE
+ RELATED *Variable() FUNCTIONS, OR USING THE NEW variable=
+ KEYWORD ARGUMENT, IS NOT BACKWARDS COMPATIBLE TO VERSIONS OF
+ SCons BEFORE 0.98. YOUR SConscript FILES WILL NOT WORK ON
+ EARLIER VERSIONS OF SCons AFTER MAKING THIS CHANGE.
+
+ If you change SConscript files in software that you make available
+ for download or otherwise distribute, other users may try to
+ build your software with an earlier version of SCons that does
+ not have the Variables object or related *Variable() functions.
+ We recommend preparing for this in one of two ways:
+
+ -- Make your SConscript files backwards-compatible by
+ modifying your calls with Python try:-except: blocks
+ as follows:
+
+ try:
+ vars = Variables('custom.py', ARGUMENTS)
+ vars.AddVariables(
+ BoolVariable('WARNINGS', 'cmopile with -Wall', 1),
+ EnumVariable('DEBUG', 'debug version', 'no'
+ allowed_values=('yes', 'no', 'full'),
+ map={}, ignorecase=0),
+ ListVariable('SHAREDLIBS',
+ 'libraries to build shared',
+ 'all',
+ names = list_of_libs),
+ PackageVariable('X11',
+ 'use X11 from here',
+ '/usr/bin/X11'),
+ PathVariable('QTDIR', 'root of Qt', qtdir),
+ )
+ except NameError:
+ vars = Options('custom.py', ARGUMENTS)
+ vars.AddOptions(
+ BoolOption('WARNINGS', 'cmopile with -Wall', 1),
+ EnumOption('DEBUG', 'debug version', 'no'
+ allowed_values=('yes', 'no', 'full'),
+ map={}, ignorecase=0),
+ ListOption('SHAREDLIBS',
+ 'libraries to build shared',
+ 'all',
+ names = list_of_libs),
+ PackageOption('X11',
+ 'use X11 from here',
+ '/usr/bin/X11'),
+ PathOption('QTDIR', 'root of Qt', qtdir),
+ )
+
+ Additionally, you can check for availability of the new
+ variables= keyword argument as follows:
+
+ try:
+ env = Environment(variables=vars)
+ except TypeError:
+ env = Environment(options=vars)
+
+ (Note that we plan to maintain the existing Options object
+ name for some time, to ensure backwards compatibility,
+ so in practice it may be easier to just continue to use
+ the old name until you're reasonably sure you won't have
+ people trying to build your software with versions of
+ SCons earlier than 0.98.1.)
+
+ -- Use the EnsureSConsVersion() function to provide a
+ descriptive error message if your SConscript files
+ are executed by an earlier version of SCons:
+
+ EnsureSConsVersion(0, 98, 1)
+
+ -- THE BuildDir() METHOD AND FUNCTION WILL BE DEPRECATED
+
+ The env.BuildDir() method and BuildDir() function are being
+ replaced by the new env.VariantDir() method and VariantDir()
+ function.
+
+ In some future release a deprecation warning will be added
+ to existing uses of the env.BuildDir() method and BuildDir()
+ function. At some point after the deprecation warning, the
+ env.Builder() method and BuildDir() function will either
+ be removed entirely or have their behavior changed.
+
+ You can prepare for this by changing all your uses of the
+ env.BuildDir() method to env.VariantDir() and uses of the
+ global BuildDir() function to VariantDir(). If you use a
+ named keyword argument of "build_dir" when calling
+ env.BuildDir() or BuildDir():
+
+ env.BuildDir(build_dir='opt', src_dir='src')
+
+ The keyword must be changed to "variant_dir":
+
+ env.VariantDir(variant_dir='opt', src_dir='src')
+
+ NOTE: CHANGING USES OF env.BuildDir() AND BuildDir() to
+ env.VariantDir() AND VariantDir() IS NOT BACKWARDS COMPATIBLE
+ TO VERSIONS OF SCons BEFORE 0.98. YOUR SConscript FILES
+ WILL NOT WORK ON EARLIER VERSIONS OF SCons AFTER MAKING
+ THIS CHANGE.
+
+ If you change SConscript files in software that you make
+ available for download or otherwise distribute, other users
+ may try to build your software with an earlier version of
+ SCons that does not have the env.VariantDir() method or
+ VariantDir() fnction. We recommend preparing for this in
+ one of two ways:
+
+ -- Make your SConscript files backwards-compatible by
+ including the following code near the beginning of your
+ top-level SConstruct file:
+
+ import SCons.Environment
+ try:
+ SCons.Environment.Environment.VariantDir
+ except AttributeError:
+ SCons.Environment.Environment.VariantDir = \
+ SCons.Environment.Environment.BuildDir
+
+ -- Use the EnsureSConsVersion() function to provide a
+ descriptive error message if your SConscript files
+ are executed by an earlier version of SCons:
+
+ EnsureSConsVersion(0, 98)
+
+ -- THE SConscript() "build_dir" KEYWORD ARGUMENT WILL BE DEPRECATED
+
+ The "build_dir" keyword argument of the SConscript function
+ and env.SConscript() method are being replaced by a new
+ "variant_dir" keyword argument.
+
+ In some future release a deprecation warning will be added
+ to existing uses of the SConscript()/env.SConscript()
+ "build_dir" keyword argument. At some point after the
+ deprecation warning, support for this keyword argument will
+ be removed entirely.
+
+ You can prepare for this by changing all your uses of the
+ SConscript()/env.SConscript() 'build_dir" keyword argument:
+
+ SConscript('src/SConscript', build_dir='opt')
+
+ To use the new "variant_dir" keyword argument:
+
+ SConscript('src/SConscript', variant_dir='opt')
+
+ NOTE: USING THE NEW "variant_dir" KEYWORD IS NOT BACKWARDS
+ COMPATIBLE TO VERSIONS OF SCons BEFORE 0.98. YOUR SConscript
+ FILES WILL NOT WORK ON EARLIER VERSIONS OF SCons AFTER
+ MAKING THIS CHANGE.
+
+ If you change SConscript files in software that you make
+ available for download or otherwise distribute, other users
+ may try to build your software with an earlier version of
+ SCons that does not support the "variant_dir" keyword.
+
+ If you can insist that users use a recent version of SCons
+ that supports "variant_dir", we recommend using the
+ EnsureSConsVersion() function to provide a descriptive error
+ message if your SConscript files are executed by an earlier
+ version of SCons:
+
+ EnsureSConsVersion(0, 98)
+
+ If you want to make sure that your SConscript files will
+ still work with earlier versions of SCons, then your best
+ bet is to continue to use the "build_dir" keyword until the
+ support is removed (which, in all likelihood, won't happen
+ for quite some time).
+
+ -- SCANNER NAMES HAVE BEEN DEPRECATED AND WILL BE REMOVED
+
+ Several internal variable names in SCons.Defaults for various
+ pre-made default Scanner objects have been deprecated and will
+ be removed in a future revision. In their place are several new
+ global variable names that are now part of the publicly-supported
+ interface:
+
+ NEW NAME DEPRECATED NAME
+ -------- ----------------------------
+ CScanner SCons.Defaults.CScan
+ DSCanner SCons.Defaults.DScan
+ SourceFileScanner SCons.Defaults.ObjSourceScan
+ ProgramScanner SCons.Defaults.ProgScan
+
+ Of these, only ObjSourceScan was probably used at all, to add
+ new mappings of file suffixes to other scanners for use by the
+ Object() Builder. This should now be done as follows:
+
+ SourceFileScanner.add_scanner('.x', XScanner)
+
+ -- THE env.Copy() METHOD WILL CHANGE OR GO AWAY ENTIRELY
+
+ The env.Copy() method (to make a copy of a construction
+ environment) is being replaced by the env.Clone() method.
+
+ As of SCons 0.98, a deprecation warning has been added to
+ current uses of the env.Copy() method. At some point in
+ the future, the env.Copy() method will either be removed
+ entirely or have its behavior changed.
+
+ You can prepare for this by changing all your uses of env.Copy()
+ to env.Clone(), which has the exact same calling arguments.
+
+ NOTE: CHANGING USES OF env.Copy() TO env.Clone() WILL MAKE
+ YOUR SConscript FILES NOT WORK ON VERSIONS OF SCons BEFORE
+ 0.96.93.
+
+ If you change SConscript files in software that you make
+ available for download or otherwise distribute, other users
+ may try to build your software with an earlier version of
+ SCons that does not have the env.Clone() method. We recommend
+ preparing for this in one of two ways:
+
+ -- Make your SConscript files backwards-compatible by
+ including the following code near the beginning of your
+ top-level SConstruct file:
+
+ import SCons.Environment
+ try:
+ SCons.Environment.Environment.Clone
+ except AttributeError:
+ SCons.Environment.Environment.Clone = \
+ SCons.Environment.Environment.Copy
+
+ -- Use the EnsureSConsVersion() function to provide a
+ descriptive error message if your SConscript files
+ are executed by an earlier version of SCons:
+
+ EnsureSConsVersion(0, 96, 93)
+
+ -- THE CheckLib Configure TEST WILL CHANGE BEHAVIOR
+
+ The CheckLib() Configure test appends the lib(s) to the
+ Environment's LIBS list in 1.3 and earlier. In 1.3 there is a
+ new CheckLib argument, append, which defaults to True to
+ preserve the old behavior. In a future release, append will
+ be changed to default to False, to conform with autoconf and
+ user expectations, since it is usually used to build up
+ library lists in a right-to-left way.
+
+
+
+ SCons is developed with an extensive regression test suite, and a
+ rigorous development methodology for continually improving that suite.
+ Because of this, SCons is of sufficient quality that you can use it
+ for real work.
+
+ The interfaces in release 1.0 will *not* be knowingly changed in
+ any new, future 1.x release. If an interface change should ever
+ become necessary due to extraordinary circumstances, the change
+ and an appropriate transition strategy will be documented in these
+ RELEASE notes.
+
+ As you use SCons, please heed the following:
+
+ - Please report any bugs or other problems that you find to our bug
+ tracker at our SourceForge project page:
+
+ http://sourceforge.net/tracker/?func=add&group_id=30337&atid=398971
+
+ We have a reliable bug-fixing methodology already in place and
+ strive to respond to problems relatively quickly.
+
+ - Documentation is spottier than we'd like. You may need to dive
+ into the source code to figure out how to do something. Asking
+ questions on the scons-users mailing list is also welcome. We
+ will be addressing the documentation in upcoming releases, but
+ would be more than glad to have your assistance in correcting this
+ problem... :-)
+
+ - The "SCons Design" documentation on the SCons web site is very
+ out of date, as we made significant changes to portions of the
+ interface as we figured out what worked and what didn't during the
+ extensive beta implementation. The "SCons Design" document should
+ be used only for historical purposes, or for just an extremely
+ general understanding of SCons' architectural goals.
+
+ - There may be performance issues. Improving SCons performance
+ is an ongoing priority. If you still find the performance
+ unacceptable, we would very much like to hear from you and learn
+ more about your configuration so we can optimize the right things.
+
+ - Error messages don't always exist where they'd be helpful.
+ Please let us know about any errors you ran into that would
+ have benefitted from a (more) descriptive message.
+
+ KNOWN PROBLEMS IN THIS RELEASE:
+
+ For a complete list of known problems, consult the SCons Issue Tracker
+ at tigris.org:
+
+ http://scons.tigris.org/project_issues.html
+
+ - Support for parallel builds (-j) does not work on WIN32 systems
+ prior to *official* Python release 2.2 (not 2.2 pre-releases).
+
+ Prior to Python 2.2, there is a bug in Python's Win32
+ implementation such that when a thread spawns an external command,
+ it blocks all threads from running. This breaks the SCons
+ multithreading architecture used to support -j builds.
+
+ We have included a patch file, os_spawnv_fix.diff, that you can
+ use if you you want to fix your version of Python to support
+ parallel builds in SCons.
+
+ - Again, the "SCons Design" documentation on the SCons web site is
+ out of date. Take what you read there with a grain of salt.
+
+ - On Win32 systems, you must put a space between the redirection
+ characters < and >, and the specified files (or construction
+ variable expansions):
+
+ command < $SOURCE > $TARGET
+
+ If you don't supply a space (for example, "<$SOURCE"), SCons will
+ not recognize the redirection.
+
+ - MSVC .res files are not rebuilt when icons change.
+
+ - The -c option does not clean up .sconsign files or directories
+ created as part of the build, and also does not clean up
+ SideEffect files (for example, Visual Studio .pdb files).
+
+ - When using multiple Repositories, changing the name of an include
+ file can cause an old version of the file to be used.
+
+ - There is currently no way to force use of a relative path (../*)
+ for directories outside the top-level SConstruct file.
+
+ - The Jar() Builder will, on its second or subsequent invocation,
+ package up the .sconsign files that SCons uses to track signatures.
+ You can work around this by using the SConsignFile() function
+ to collect all of the .sconsign information into a single file
+ outside of the directory being packaged by Jar().
+
+ - SCons does not currently have a way to detect that an intermediate
+ file has been corrupted from outside and should be rebuilt.
+
+ - Unicode characters in path names do not work in all circumstances.
+
+ - SCons does not currently automatically check out SConstruct or
+ SConscript files from SCCS, RCS or BitKeeper.
+
+ - No support yet for the following planned command-line options:
+
+ -d -e -l --list-actions --list-derived --list-where
+ -o --override -p -r -R -w --write-filenames
+ -W --warn-undefined-variables
+
+
+
+Thank you for your interest, and please let us know how we can help
+improve SCons for your needs.
+
+Steven Knight
+knight at baldmt dot com
+http://www.baldmt.com/~knight/
+
+With plenty of help from the SCons Development team:
+ Chad Austin
+ Charles Crain
+ Bill Deegan
+ Steve Leblanc
+ Greg Noel
+ Gary Oberbrunner
+ Anthony Roach
+ Greg Spencer
+ Christoph Wiedemann
+
diff --git a/src/engine/MANIFEST-xml.in b/src/engine/MANIFEST-xml.in
new file mode 100644
index 0000000..f6ce08a
--- /dev/null
+++ b/src/engine/MANIFEST-xml.in
@@ -0,0 +1,99 @@
+SCons/Action.xml
+SCons/Defaults.xml
+SCons/Environment.xml
+SCons/Platform/__init__.xml
+SCons/Platform/posix.xml
+SCons/Platform/sunos.xml
+SCons/Platform/win32.xml
+SCons/Tool/386asm.xml
+SCons/Tool/BitKeeper.xml
+SCons/Tool/CVS.xml
+SCons/Tool/Perforce.xml
+SCons/Tool/RCS.xml
+SCons/Tool/SCCS.xml
+SCons/Tool/Subversion.xml
+SCons/Tool/__init__.xml
+SCons/Tool/aixc++.xml
+SCons/Tool/aixcc.xml
+SCons/Tool/aixf77.xml
+SCons/Tool/aixlink.xml
+SCons/Tool/applelink.xml
+SCons/Tool/ar.xml
+SCons/Tool/as.xml
+SCons/Tool/bcc32.xml
+SCons/Tool/c++.xml
+SCons/Tool/c++.xml
+SCons/Tool/cc.xml
+SCons/Tool/cvf.xml
+SCons/Tool/default.xml
+SCons/Tool/dmd.xml
+SCons/Tool/dvi.xml
+SCons/Tool/dvipdf.xml
+SCons/Tool/dvips.xml
+SCons/Tool/f77.xml
+SCons/Tool/f90.xml
+SCons/Tool/f95.xml
+SCons/Tool/fortran.xml
+SCons/Tool/g++.xml
+SCons/Tool/g77.xml
+SCons/Tool/gas.xml
+SCons/Tool/gcc.xml
+SCons/Tool/gfortran.xml
+SCons/Tool/gnulink.xml
+SCons/Tool/gs.xml
+SCons/Tool/hpc++.xml
+SCons/Tool/hpcc.xml
+SCons/Tool/hplink.xml
+SCons/Tool/icc.xml
+SCons/Tool/icl.xml
+SCons/Tool/ifl.xml
+SCons/Tool/ifort.xml
+SCons/Tool/ilink.xml
+SCons/Tool/ilink32.xml
+SCons/Tool/install.xml
+SCons/Tool/intelc.xml
+SCons/Tool/jar.xml
+SCons/Tool/javac.xml
+SCons/Tool/javah.xml
+SCons/Tool/latex.xml
+SCons/Tool/lex.xml
+SCons/Tool/link.xml
+SCons/Tool/linkloc.xml
+SCons/Tool/m4.xml
+SCons/Tool/masm.xml
+SCons/Tool/midl.xml
+SCons/Tool/mingw.xml
+SCons/Tool/mslib.xml
+SCons/Tool/mslink.xml
+SCons/Tool/mssdk.xml
+SCons/Tool/msvc.xml
+SCons/Tool/msvs.xml
+SCons/Tool/mwcc.xml
+SCons/Tool/mwld.xml
+SCons/Tool/nasm.xml
+SCons/Tool/packaging.xml
+SCons/Tool/packaging/__init__.xml
+SCons/Tool/pdf.xml
+SCons/Tool/pdflatex.xml
+SCons/Tool/pdftex.xml
+SCons/Tool/qt.xml
+SCons/Tool/rmic.xml
+SCons/Tool/rpcgen.xml
+SCons/Tool/sgiar.xml
+SCons/Tool/sgic++.xml
+SCons/Tool/sgicc.xml
+SCons/Tool/sgilink.xml
+SCons/Tool/sunar.xml
+SCons/Tool/sunc++.xml
+SCons/Tool/suncc.xml
+SCons/Tool/sunf77.xml
+SCons/Tool/sunf90.xml
+SCons/Tool/sunf95.xml
+SCons/Tool/sunlink.xml
+SCons/Tool/swig.xml
+SCons/Tool/tar.xml
+SCons/Tool/tex.xml
+SCons/Tool/textfile.xml
+SCons/Tool/tlib.xml
+SCons/Tool/yacc.xml
+SCons/Tool/zip.xml
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
new file mode 100644
index 0000000..2185d30
--- /dev/null
+++ b/src/engine/MANIFEST.in
@@ -0,0 +1,187 @@
+SCons/__init__.py
+SCons/Action.py
+SCons/Builder.py
+SCons/compat/__init__.py
+SCons/compat/_scons_hashlib.py
+SCons/compat/_scons_itertools.py
+SCons/compat/_scons_optparse.py
+SCons/compat/_scons_sets.py
+SCons/compat/_scons_sets15.py
+SCons/compat/_scons_shlex.py
+SCons/compat/_scons_subprocess.py
+SCons/compat/_scons_textwrap.py
+SCons/compat/_scons_UserString.py
+SCons/compat/builtins.py
+SCons/CacheDir.py
+SCons/Conftest.py
+SCons/cpp.py
+SCons/dblite.py
+SCons/Debug.py
+SCons/Defaults.py
+SCons/Environment.py
+SCons/Errors.py
+SCons/Executor.py
+SCons/Job.py
+SCons/exitfuncs.py
+SCons/Memoize.py
+SCons/Node/__init__.py
+SCons/Node/Alias.py
+SCons/Node/FS.py
+SCons/Node/Python.py
+SCons/Options/__init__.py
+SCons/Options/BoolOption.py
+SCons/Options/EnumOption.py
+SCons/Options/ListOption.py
+SCons/Options/PackageOption.py
+SCons/Options/PathOption.py
+SCons/PathList.py
+SCons/Platform/__init__.py
+SCons/Platform/aix.py
+SCons/Platform/cygwin.py
+SCons/Platform/darwin.py
+SCons/Platform/hpux.py
+SCons/Platform/irix.py
+SCons/Platform/os2.py
+SCons/Platform/posix.py
+SCons/Platform/sunos.py
+SCons/Platform/win32.py
+SCons/Scanner/__init__.py
+SCons/Scanner/C.py
+SCons/Scanner/D.py
+SCons/Scanner/Dir.py
+SCons/Scanner/Fortran.py
+SCons/Scanner/IDL.py
+SCons/Scanner/LaTeX.py
+SCons/Scanner/Prog.py
+SCons/Scanner/RC.py
+SCons/SConf.py
+SCons/SConsign.py
+SCons/Script/__init__.py
+SCons/Script/Interactive.py
+SCons/Script/Main.py
+SCons/Script/SConscript.py
+SCons/Script/SConsOptions.py
+SCons/Sig.py
+SCons/Subst.py
+SCons/Taskmaster.py
+SCons/Tool/__init__.py
+SCons/Tool/386asm.py
+SCons/Tool/aixc++.py
+SCons/Tool/aixcc.py
+SCons/Tool/aixf77.py
+SCons/Tool/aixlink.py
+SCons/Tool/applelink.py
+SCons/Tool/ar.py
+SCons/Tool/as.py
+SCons/Tool/bcc32.py
+SCons/Tool/BitKeeper.py
+SCons/Tool/c++.py
+SCons/Tool/cc.py
+SCons/Tool/cvf.py
+SCons/Tool/CVS.py
+SCons/Tool/default.py
+SCons/Tool/dmd.py
+SCons/Tool/dvi.py
+SCons/Tool/dvipdf.py
+SCons/Tool/dvips.py
+SCons/Tool/f77.py
+SCons/Tool/f90.py
+SCons/Tool/f95.py
+SCons/Tool/filesystem.py
+SCons/Tool/fortran.py
+SCons/Tool/FortranCommon.py
+SCons/Tool/g++.py
+SCons/Tool/g77.py
+SCons/Tool/gas.py
+SCons/Tool/gcc.py
+SCons/Tool/gfortran.py
+SCons/Tool/gnulink.py
+SCons/Tool/gs.py
+SCons/Tool/hpc++.py
+SCons/Tool/hpcc.py
+SCons/Tool/hplink.py
+SCons/Tool/icc.py
+SCons/Tool/icl.py
+SCons/Tool/ifl.py
+SCons/Tool/ifort.py
+SCons/Tool/ilink.py
+SCons/Tool/ilink32.py
+SCons/Tool/install.py
+SCons/Tool/intelc.py
+SCons/Tool/ipkg.py
+SCons/Tool/jar.py
+SCons/Tool/JavaCommon.py
+SCons/Tool/javac.py
+SCons/Tool/javah.py
+SCons/Tool/latex.py
+SCons/Tool/lex.py
+SCons/Tool/link.py
+SCons/Tool/linkloc.py
+SCons/Tool/MSCommon/__init__.py
+SCons/Tool/MSCommon/arch.py
+SCons/Tool/MSCommon/common.py
+SCons/Tool/MSCommon/netframework.py
+SCons/Tool/MSCommon/sdk.py
+SCons/Tool/MSCommon/vs.py
+SCons/Tool/MSCommon/vc.py
+SCons/Tool/m4.py
+SCons/Tool/masm.py
+SCons/Tool/midl.py
+SCons/Tool/mingw.py
+SCons/Tool/mslib.py
+SCons/Tool/mslink.py
+SCons/Tool/mssdk.py
+SCons/Tool/msvc.py
+SCons/Tool/msvs.py
+SCons/Tool/mwcc.py
+SCons/Tool/mwld.py
+SCons/Tool/nasm.py
+SCons/Tool/packaging/__init__.py
+SCons/Tool/packaging/ipk.py
+SCons/Tool/packaging/msi.py
+SCons/Tool/packaging/rpm.py
+SCons/Tool/packaging/src_tarbz2.py
+SCons/Tool/packaging/src_targz.py
+SCons/Tool/packaging/src_zip.py
+SCons/Tool/packaging/tarbz2.py
+SCons/Tool/packaging/targz.py
+SCons/Tool/packaging/zip.py
+SCons/Tool/pdf.py
+SCons/Tool/pdflatex.py
+SCons/Tool/pdftex.py
+SCons/Tool/Perforce.py
+SCons/Tool/PharLapCommon.py
+SCons/Tool/qt.py
+SCons/Tool/RCS.py
+SCons/Tool/rmic.py
+SCons/Tool/rpcgen.py
+SCons/Tool/rpm.py
+SCons/Tool/SCCS.py
+SCons/Tool/sgiar.py
+SCons/Tool/sgic++.py
+SCons/Tool/sgicc.py
+SCons/Tool/sgilink.py
+SCons/Tool/Subversion.py
+SCons/Tool/sunar.py
+SCons/Tool/sunc++.py
+SCons/Tool/suncc.py
+SCons/Tool/sunf77.py
+SCons/Tool/sunf90.py
+SCons/Tool/sunf95.py
+SCons/Tool/sunlink.py
+SCons/Tool/swig.py
+SCons/Tool/tar.py
+SCons/Tool/tex.py
+SCons/Tool/textfile.py
+SCons/Tool/tlib.py
+SCons/Tool/wix.py
+SCons/Tool/yacc.py
+SCons/Tool/zip.py
+SCons/Util.py
+SCons/Variables/__init__.py
+SCons/Variables/BoolVariable.py
+SCons/Variables/EnumVariable.py
+SCons/Variables/ListVariable.py
+SCons/Variables/PackageVariable.py
+SCons/Variables/PathVariable.py
+SCons/Warnings.py
diff --git a/src/engine/README.txt b/src/engine/README.txt
new file mode 100644
index 0000000..3d6281f
--- /dev/null
+++ b/src/engine/README.txt
@@ -0,0 +1,167 @@
+###
+### THIS FILE IS NO LONGER USED. THIS IS THE README FILE FOR THE
+### SEPARATE BUILD ENGINE PACKAGE FROM THE ORIGINAL (DRAFT) PACKAGING
+### SCHEME. WE'RE SAVING THIS IN CASE WE NEED OR WANT TO RESURRECT
+### A SEPARATE BUILD ENGINE PACKAGE IN THE FUTURE.
+###
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+# src/engine/README.txt 4577 2009/12/27 19:44:43 scons
+
+
+ SCons - a software construction tool
+
+ Version 1.2.0.d20091224
+
+
+This is an alpha release of the SCons build engine, a Python extension
+module for building software (and other files). The SCons build engine
+manages dependencies and executes commands or Python functions to update
+out-of-date files (or other objects).
+
+
+LATEST VERSION
+==============
+
+Before going further, you can check that this package you have is
+the latest version by checking the SCons download page at:
+
+ http://www.scons.org/download.html
+
+
+ABOUT SCONS PACKAGES
+====================
+
+The complete SCons system is comprised of three separate packages:
+
+ scons
+ The scons script itself, plus the SCons build engine
+ installed into an SCons-specific library directory.
+
+ python-scons [THIS PACKAGE]
+ The SCons build engine, installed into the standard
+ Python library directory.
+
+ scons-script
+ Only the scons script itself.
+
+Depending on what you want to do with SCons, you may need to install
+additional (or other) packages:
+
+ If you just want to use scons (the script) to build software:
+
+ Do not install this package. Install the scons package instead,
+ which contains a copy of both the script and the build engine.
+ You will not need to install any other packages.
+
+ If you do NOT want to use the scons script, but you want to use the
+ SCons build engine in other Python software:
+
+ Install this package. You do not need to install any other
+ packages.
+
+ If you want to use the scons script AND you want to use the SCons
+ build engine in other Python software:
+
+ Install this package AND the scons package.
+
+ Note that this installs two separate copies of the build engine,
+ one (in an SCons-specific library directory) used by the scons
+ script itself and one (in the standard Python library) used by
+ other software. This allows you the flexibility to upgrade
+ one build engine without affecting the other.
+
+ If you want the scons script and other Python software to use the
+ same version of the build engine:
+
+ Install this package AND the scons-script package.
+
+
+INSTALLATION
+============
+
+To install this package, simply run the provided Python-standard setup
+script as follows:
+
+ # python setup.py
+
+You should have system installation privileges (that is, "root" or
+"Administrator") when running the setup.py script.
+
+
+DOCUMENTATION
+=============
+
+Documentation for SCons is available at:
+
+ http://www.scons.org/doc.html
+
+
+LICENSING
+=========
+
+SCons is distributed under the MIT license, a full copy of which is
+available in the LICENSE.txt file. The MIT license is an approved Open
+Source license, which means:
+
+ This software is OSI Certified Open Source Software. OSI
+ Certified is a certification mark of the Open Source Initiative.
+
+More information about OSI certifications and Open Source software is
+available at:
+
+ http://www.opensource.org/
+
+
+REPORTING BUGS
+==============
+
+You can report bugs either by following the "Tracker - Bugs" link
+on the SCons project page:
+
+ http://sourceforge.net/projects/scons/
+
+or by sending mail to the SCons developers mailing list:
+
+ scons-devel@lists.sourceforge.net
+
+
+MAILING LISTS
+=============
+
+A mailing list for users of SCons is available. You may send
+questions or comments to the list at:
+
+ scons-users@lists.sourceforge.net
+
+You may subscribe to the mailing list at:
+
+ http://lists.sourceforge.net/lists/listinfo/scons-users
+
+There is also a low-volume mailing list available for announcements
+about SCons. Subscribe at:
+
+ http://lists.sourceforge.net/lists/listinfo/scons-announce
+
+
+FOR MORE INFORMATION
+====================
+
+Check the SCons web site at:
+
+ http://www.scons.org/
+
+
+AUTHOR INFO
+===========
+
+Steven Knight
+knight at baldmt dot com
+http://www.baldmt.com/~knight/
+
+With more than a little help from:
+ Chad Austin
+ Charles Crain
+ Steve Leblanc
+ Anthony Roach
+ Steven Shaw
+
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
new file mode 100644
index 0000000..a0a35b7
--- /dev/null
+++ b/src/engine/SCons/Action.py
@@ -0,0 +1,1240 @@
+"""SCons.Action
+
+This encapsulates information about executing any sort of action that
+can build one or more target Nodes (typically files) from one or more
+source Nodes (also typically files) given a specific Environment.
+
+The base class here is ActionBase. The base class supplies just a few
+OO utility methods and some generic methods for displaying information
+about an Action in response to the various commands that control printing.
+
+A second-level base class is _ActionAction. This extends ActionBase
+by providing the methods that can be used to show and perform an
+action. True Action objects will subclass _ActionAction; Action
+factory class objects will subclass ActionBase.
+
+The heavy lifting is handled by subclasses for the different types of
+actions we might execute:
+
+ CommandAction
+ CommandGeneratorAction
+ FunctionAction
+ ListAction
+
+The subclasses supply the following public interface methods used by
+other modules:
+
+ __call__()
+ THE public interface, "calling" an Action object executes the
+ command or Python function. This also takes care of printing
+ a pre-substitution command for debugging purposes.
+
+ get_contents()
+ Fetches the "contents" of an Action for signature calculation
+ plus the varlist. This is what gets MD5 checksummed to decide
+ if a target needs to be rebuilt because its action changed.
+
+ genstring()
+ Returns a string representation of the Action *without*
+ command substitution, but allows a CommandGeneratorAction to
+ generate the right action based on the specified target,
+ source and env. This is used by the Signature subsystem
+ (through the Executor) to obtain an (imprecise) representation
+ of the Action operation for informative purposes.
+
+
+Subclasses also supply the following methods for internal use within
+this module:
+
+ __str__()
+ Returns a string approximation of the Action; no variable
+ substitution is performed.
+
+ execute()
+ The internal method that really, truly, actually handles the
+ execution of a command or Python function. This is used so
+ that the __call__() methods can take care of displaying any
+ pre-substitution representations, and *then* execute an action
+ without worrying about the specific Actions involved.
+
+ get_presig()
+ Fetches the "contents" of a subclass for signature calculation.
+ The varlist is added to this to produce the Action's contents.
+
+ strfunction()
+ Returns a substituted string representation of the Action.
+ This is used by the _ActionAction.show() command to display the
+ command/function that will be executed to generate the target(s).
+
+There is a related independent ActionCaller class that looks like a
+regular Action, and which serves as a wrapper for arbitrary functions
+that we want to let the user specify the arguments to now, but actually
+execute later (when an out-of-date check determines that it's needed to
+be executed, for example). Objects of this class are returned by an
+ActionFactory class that provides a __call__() method as a convenient
+way for wrapping up the functions.
+
+"""
+
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+__revision__ = "src/engine/SCons/Action.py 4577 2009/12/27 19:44:43 scons"
+
+import cPickle
+import dis
+import os
+import re
+import string
+import sys
+import subprocess
+
+from SCons.Debug import logInstanceCreation
+import SCons.Errors
+import SCons.Executor
+import SCons.Util
+import SCons.Subst
+
+# we use these a lot, so try to optimize them
+is_String = SCons.Util.is_String
+is_List = SCons.Util.is_List
+
+class _null:
+ pass
+
+print_actions = 1
+execute_actions = 1
+print_actions_presub = 0
+
+def rfile(n):
+ try:
+ return n.rfile()
+ except AttributeError:
+ return n
+
+def default_exitstatfunc(s):
+ return s
+
+try:
+ SET_LINENO = dis.SET_LINENO
+ HAVE_ARGUMENT = dis.HAVE_ARGUMENT
+except AttributeError:
+ remove_set_lineno_codes = lambda x: x
+else:
+ def remove_set_lineno_codes(code):
+ result = []
+ n = len(code)
+ i = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ if op >= HAVE_ARGUMENT:
+ if op != SET_LINENO:
+ result.append(code[i:i+3])
+ i = i+3
+ else:
+ result.append(c)
+ i = i+1
+ return string.join(result, '')
+
+strip_quotes = re.compile('^[\'"](.*)[\'"]$')
+
+
+def _callable_contents(obj):
+ """Return the signature contents of a callable Python object.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+
+def _object_contents(obj):
+ """Return the signature contents of any Python object.
+
+ We have to handle the case where object contains a code object
+ since it can be pickled directly.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ try:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+ except AttributeError:
+ # Should be a pickable Python object.
+ try:
+ return cPickle.dumps(obj)
+ except (cPickle.PicklingError, TypeError):
+ # This is weird, but it seems that nested classes
+ # are unpickable. The Python docs say it should
+ # always be a PicklingError, but some Python
+ # versions seem to return TypeError. Just do
+ # the best we can.
+ return str(obj)
+
+
+def _code_contents(code):
+ """Return the signature contents of a code object.
+
+ By providing direct access to the code object of the
+ function, Python makes this extremely easy. Hooray!
+
+ Unfortunately, older versions of Python include line
+ number indications in the compiled byte code. Boo!
+ So we remove the line number byte codes to prevent
+ recompilations from moving a Python function.
+ """
+
+ contents = []
+
+ # The code contents depends on the number of local variables
+ # but not their actual names.
+ contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
+ try:
+ contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ contents.append(",0,0")
+
+ # The code contents depends on any constants accessed by the
+ # function. Note that we have to call _object_contents on each
+ # constants because the code object of nested functions can
+ # show-up among the constants.
+ #
+ # Note that we also always ignore the first entry of co_consts
+ # which contains the function doc string. We assume that the
+ # function does not access its doc string.
+ contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
+
+ # The code contents depends on the variable names used to
+ # accessed global variable, as changing the variable name changes
+ # the variable actually accessed and therefore changes the
+ # function result.
+ contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
+
+
+ # The code contents depends on its actual code!!!
+ contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
+
+ return string.join(contents, '')
+
+
+def _function_contents(func):
+ """Return the signature contents of a function."""
+
+ contents = [_code_contents(func.func_code)]
+
+ # The function contents depends on the value of defaults arguments
+ if func.func_defaults:
+ contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
+ else:
+ contents.append(',()')
+
+ # The function contents depends on the closure captured cell values.
+ try:
+ closure = func.func_closure or []
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ closure = []
+
+ #xxx = [_object_contents(x.cell_contents) for x in closure]
+ try:
+ xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+ except AttributeError:
+ xxx = []
+ contents.append(',(' + string.join(xxx, ',') + ')')
+
+ return string.join(contents, '')
+
+
+def _actionAppend(act1, act2):
+ # This function knows how to slap two actions together.
+ # Mainly, it handles ListActions by concatenating into
+ # a single ListAction.
+ a1 = Action(act1)
+ a2 = Action(act2)
+ if a1 is None or a2 is None:
+ raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
+ if isinstance(a1, ListAction):
+ if isinstance(a2, ListAction):
+ return ListAction(a1.list + a2.list)
+ else:
+ return ListAction(a1.list + [ a2 ])
+ else:
+ if isinstance(a2, ListAction):
+ return ListAction([ a1 ] + a2.list)
+ else:
+ return ListAction([ a1, a2 ])
+
+def _do_create_keywords(args, kw):
+ """This converts any arguments after the action argument into
+ their equivalent keywords and adds them to the kw argument.
+ """
+ v = kw.get('varlist', ())
+ # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
+ if is_String(v): v = (v,)
+ kw['varlist'] = tuple(v)
+ if args:
+ # turn positional args into equivalent keywords
+ cmdstrfunc = args[0]
+ if cmdstrfunc is None or is_String(cmdstrfunc):
+ kw['cmdstr'] = cmdstrfunc
+ elif callable(cmdstrfunc):
+ kw['strfunction'] = cmdstrfunc
+ else:
+ raise SCons.Errors.UserError(
+ 'Invalid command display variable type. '
+ 'You must either pass a string or a callback which '
+ 'accepts (target, source, env) as parameters.')
+ if len(args) > 1:
+ kw['varlist'] = args[1:] + kw['varlist']
+ if kw.get('strfunction', _null) is not _null \
+ and kw.get('cmdstr', _null) is not _null:
+ raise SCons.Errors.UserError(
+ 'Cannot have both strfunction and cmdstr args to Action()')
+
+def _do_create_action(act, kw):
+ """This is the actual "implementation" for the
+ Action factory method, below. This handles the
+ fact that passing lists to Action() itself has
+ different semantics than passing lists as elements
+ of lists.
+
+ The former will create a ListAction, the latter
+ will create a CommandAction by converting the inner
+ list elements to strings."""
+
+ if isinstance(act, ActionBase):
+ return act
+
+ if is_List(act):
+ #TODO(1.5) return CommandAction(act, **kw)
+ return apply(CommandAction, (act,), kw)
+
+ if callable(act):
+ try:
+ gen = kw['generator']
+ del kw['generator']
+ except KeyError:
+ gen = 0
+ if gen:
+ action_type = CommandGeneratorAction
+ else:
+ action_type = FunctionAction
+ return action_type(act, kw)
+
+ if is_String(act):
+ var=SCons.Util.get_environment_var(act)
+ if var:
+ # This looks like a string that is purely an Environment
+ # variable reference, like "$FOO" or "${FOO}". We do
+ # something special here...we lazily evaluate the contents
+ # of that Environment variable, so a user could put something
+ # like a function or a CommandGenerator in that variable
+ # instead of a string.
+ return LazyAction(var, kw)
+ commands = string.split(str(act), '\n')
+ if len(commands) == 1:
+ #TODO(1.5) return CommandAction(commands[0], **kw)
+ return apply(CommandAction, (commands[0],), kw)
+ # The list of string commands may include a LazyAction, so we
+ # reprocess them via _do_create_list_action.
+ return _do_create_list_action(commands, kw)
+ return None
+
+def _do_create_list_action(act, kw):
+ """A factory for list actions. Convert the input list into Actions
+ and then wrap them in a ListAction."""
+ acts = []
+ for a in act:
+ aa = _do_create_action(a, kw)
+ if aa is not None: acts.append(aa)
+ if not acts:
+ return ListAction([])
+ elif len(acts) == 1:
+ return acts[0]
+ else:
+ return ListAction(acts)
+
+def Action(act, *args, **kw):
+ """A factory for action objects."""
+ # Really simple: the _do_create_* routines do the heavy lifting.
+ _do_create_keywords(args, kw)
+ if is_List(act):
+ return _do_create_list_action(act, kw)
+ return _do_create_action(act, kw)
+
+class ActionBase:
+ """Base class for all types of action objects that can be held by
+ other objects (Builders, Executors, etc.) This provides the
+ common methods for manipulating and combining those actions."""
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other)
+
+ def no_batch_key(self, env, target, source):
+ return None
+
+ batch_key = no_batch_key
+
+ def genstring(self, target, source, env):
+ return str(self)
+
+ def get_contents(self, target, source, env):
+ result = [ self.get_presig(target, source, env) ]
+ # This should never happen, as the Action() factory should wrap
+ # the varlist, but just in case an action is created directly,
+ # we duplicate this check here.
+ vl = self.varlist
+ if is_String(vl): vl = (vl,)
+ for v in vl:
+ result.append(env.subst('${'+v+'}'))
+ return string.join(result, '')
+
+ def __add__(self, other):
+ return _actionAppend(self, other)
+
+ def __radd__(self, other):
+ return _actionAppend(other, self)
+
+ def presub_lines(self, env):
+ # CommandGeneratorAction needs a real environment
+ # in order to return the proper string here, since
+ # it may call LazyAction, which looks up a key
+ # in that env. So we temporarily remember the env here,
+ # and CommandGeneratorAction will use this env
+ # when it calls its _generate method.
+ self.presub_env = env
+ lines = string.split(str(self), '\n')
+ self.presub_env = None # don't need this any more
+ return lines
+
+ def get_targets(self, env, executor):
+ """
+ Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
+ by this action.
+ """
+ return self.targets
+
+class _ActionAction(ActionBase):
+ """Base class for actions that create output objects."""
+ def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
+ presub=_null, chdir=None, exitstatfunc=None,
+ batch_key=None, targets='$TARGETS',
+ **kw):
+ self.cmdstr = cmdstr
+ if strfunction is not _null:
+ if strfunction is None:
+ self.cmdstr = None
+ else:
+ self.strfunction = strfunction
+ self.varlist = varlist
+ self.presub = presub
+ self.chdir = chdir
+ if not exitstatfunc:
+ exitstatfunc = default_exitstatfunc
+ self.exitstatfunc = exitstatfunc
+
+ self.targets = targets
+
+ if batch_key:
+ if not callable(batch_key):
+ # They have set batch_key, but not to their own
+ # callable. The default behavior here will batch
+ # *all* targets+sources using this action, separated
+ # for each construction environment.
+ def default_batch_key(self, env, target, source):
+ return (id(self), id(env))
+ batch_key = default_batch_key
+ SCons.Util.AddMethod(self, batch_key, 'batch_key')
+
+ def print_cmd_line(self, s, target, source, env):
+ sys.stdout.write(s + "\n")
+
+ def __call__(self, target, source, env,
+ exitstatfunc=_null,
+ presub=_null,
+ show=_null,
+ execute=_null,
+ chdir=_null,
+ executor=None):
+ if not is_List(target):
+ target = [target]
+ if not is_List(source):
+ source = [source]
+
+ if presub is _null:
+ presub = self.presub
+ if presub is _null:
+ presub = print_actions_presub
+ if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
+ if show is _null: show = print_actions
+ if execute is _null: execute = execute_actions
+ if chdir is _null: chdir = self.chdir
+ save_cwd = None
+ if chdir:
+ save_cwd = os.getcwd()
+ try:
+ chdir = str(chdir.abspath)
+ except AttributeError:
+ if not is_String(chdir):
+ if executor:
+ chdir = str(executor.batches[0].targets[0].dir)
+ else:
+ chdir = str(target[0].dir)
+ if presub:
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ t = string.join(map(str, target), ' and ')
+ l = string.join(self.presub_lines(env), '\n ')
+ out = "Building %s with action:\n %s\n" % (t, l)
+ sys.stdout.write(out)
+ cmd = None
+ if show and self.strfunction:
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ try:
+ cmd = self.strfunction(target, source, env, executor)
+ except TypeError:
+ cmd = self.strfunction(target, source, env)
+ if cmd:
+ if chdir:
+ cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
+ try:
+ get = env.get
+ except AttributeError:
+ print_func = self.print_cmd_line
+ else:
+ print_func = get('PRINT_CMD_LINE_FUNC')
+ if not print_func:
+ print_func = self.print_cmd_line
+ print_func(cmd, target, source, env)
+ stat = 0
+ if execute:
+ if chdir:
+ os.chdir(chdir)
+ try:
+ stat = self.execute(target, source, env, executor=executor)
+ if isinstance(stat, SCons.Errors.BuildError):
+ s = exitstatfunc(stat.status)
+ if s:
+ stat.status = s
+ else:
+ stat = s
+ else:
+ stat = exitstatfunc(stat)
+ finally:
+ if save_cwd:
+ os.chdir(save_cwd)
+ if cmd and save_cwd:
+ print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
+
+ return stat
+
+
+def _string_from_cmd_list(cmd_list):
+ """Takes a list of command line arguments and returns a pretty
+ representation for printing."""
+ cl = []
+ for arg in map(str, cmd_list):
+ if ' ' in arg or '\t' in arg:
+ arg = '"' + arg + '"'
+ cl.append(arg)
+ return string.join(cl)
+
+# A fiddlin' little function that has an 'import SCons.Environment' which
+# can't be moved to the top level without creating an import loop. Since
+# this import creates a local variable named 'SCons', it blocks access to
+# the global variable, so we move it here to prevent complaints about local
+# variables being used uninitialized.
+default_ENV = None
+def get_default_ENV(env):
+ global default_ENV
+ try:
+ return env['ENV']
+ except KeyError:
+ if not default_ENV:
+ import SCons.Environment
+ # This is a hideously expensive way to get a default shell
+ # environment. What it really should do is run the platform
+ # setup to get the default ENV. Fortunately, it's incredibly
+ # rare for an Environment not to have a shell environment, so
+ # we're not going to worry about it overmuch.
+ default_ENV = SCons.Environment.Environment()['ENV']
+ return default_ENV
+
+# This function is still in draft mode. We're going to need something like
+# it in the long run as more and more places use subprocess, but I'm sure
+# it'll have to be tweaked to get the full desired functionality.
+# one special arg (so far?), 'error', to tell what to do with exceptions.
+def _subproc(env, cmd, error = 'ignore', **kw):
+ """Do common setup for a subprocess.Popen() call"""
+ # allow std{in,out,err} to be "'devnull'"
+ io = kw.get('stdin')
+ if is_String(io) and io == 'devnull':
+ kw['stdin'] = open(os.devnull)
+ io = kw.get('stdout')
+ if is_String(io) and io == 'devnull':
+ kw['stdout'] = open(os.devnull, 'w')
+ io = kw.get('stderr')
+ if is_String(io) and io == 'devnull':
+ kw['stderr'] = open(os.devnull, 'w')
+
+ # Figure out what shell environment to use
+ ENV = kw.get('env', None)
+ if ENV is None: ENV = get_default_ENV(env)
+
+ # Ensure that the ENV values are all strings:
+ new_env = {}
+ for key, value in ENV.items():
+ if is_List(value):
+ # If the value is a list, then we assume it is a path list,
+ # because that's a pretty common list-like value to stick
+ # in an environment variable:
+ value = SCons.Util.flatten_sequence(value)
+ new_env[key] = string.join(map(str, value), os.pathsep)
+ else:
+ # It's either a string or something else. If it's a string,
+ # we still want to call str() because it might be a *Unicode*
+ # string, which makes subprocess.Popen() gag. If it isn't a
+ # string or a list, then we just coerce it to a string, which
+ # is the proper way to handle Dir and File instances and will
+ # produce something reasonable for just about everything else:
+ new_env[key] = str(value)
+ kw['env'] = new_env
+
+ try:
+ #FUTURE return subprocess.Popen(cmd, **kw)
+ return apply(subprocess.Popen, (cmd,), kw)
+ except EnvironmentError, e:
+ if error == 'raise': raise
+ # return a dummy Popen instance that only returns error
+ class dummyPopen:
+ def __init__(self, e): self.exception = e
+ def communicate(self): return ('','')
+ def wait(self): return -self.exception.errno
+ stdin = None
+ class f:
+ def read(self): return ''
+ def readline(self): return ''
+ stdout = stderr = f()
+ return dummyPopen(e)
+
+class CommandAction(_ActionAction):
+ """Class for command-execution actions."""
+ def __init__(self, cmd, **kw):
+ # Cmd can actually be a list or a single item; if it's a
+ # single item it should be the command string to execute; if a
+ # list then it should be the words of the command string to
+ # execute. Only a single command should be executed by this
+ # object; lists of commands should be handled by embedding
+ # these objects in a ListAction object (which the Action()
+ # factory above does). cmd will be passed to
+ # Environment.subst_list() for substituting environment
+ # variables.
+ if __debug__: logInstanceCreation(self, 'Action.CommandAction')
+
+ #TODO(1.5) _ActionAction.__init__(self, **kw)
+ apply(_ActionAction.__init__, (self,), kw)
+ if is_List(cmd):
+ if filter(is_List, cmd):
+ raise TypeError, "CommandAction should be given only " \
+ "a single command"
+ self.cmd_list = cmd
+
+ def __str__(self):
+ if is_List(self.cmd_list):
+ return string.join(map(str, self.cmd_list), ' ')
+ return str(self.cmd_list)
+
+ def process(self, target, source, env, executor=None):
+ if executor:
+ result = env.subst_list(self.cmd_list, 0, executor=executor)
+ else:
+ result = env.subst_list(self.cmd_list, 0, target, source)
+ silent = None
+ ignore = None
+ while 1:
+ try: c = result[0][0][0]
+ except IndexError: c = None
+ if c == '@': silent = 1
+ elif c == '-': ignore = 1
+ else: break
+ result[0][0] = result[0][0][1:]
+ try:
+ if not result[0][0]:
+ result[0] = result[0][1:]
+ except IndexError:
+ pass
+ return result, ignore, silent
+
+ def strfunction(self, target, source, env, executor=None):
+ if self.cmdstr is None:
+ return None
+ if self.cmdstr is not _null:
+ from SCons.Subst import SUBST_RAW
+ if executor:
+ c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+ else:
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ if c:
+ return c
+ cmd_list, ignore, silent = self.process(target, source, env, executor)
+ if silent:
+ return ''
+ return _string_from_cmd_list(cmd_list[0])
+
+ def execute(self, target, source, env, executor=None):
+ """Execute a command action.
+
+ This will handle lists of commands as well as individual commands,
+ because construction variable substitution may turn a single
+ "command" into a list. This means that this class can actually
+ handle lists of commands, even though that's not how we use it
+ externally.
+ """
+ escape_list = SCons.Subst.escape_list
+ flatten_sequence = SCons.Util.flatten_sequence
+
+ try:
+ shell = env['SHELL']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SHELL construction variable.')
+
+ try:
+ spawn = env['SPAWN']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+ else:
+ if is_String(spawn):
+ spawn = env.subst(spawn, raw=1, conv=lambda x: x)
+
+ escape = env.get('ESCAPE', lambda x: x)
+
+ ENV = get_default_ENV(env)
+
+ # Ensure that the ENV values are all strings:
+ for key, value in ENV.items():
+ if not is_String(value):
+ if is_List(value):
+ # If the value is a list, then we assume it is a
+ # path list, because that's a pretty common list-like
+ # value to stick in an environment variable:
+ value = flatten_sequence(value)
+ ENV[key] = string.join(map(str, value), os.pathsep)
+ else:
+ # If it isn't a string or a list, then we just coerce
+ # it to a string, which is the proper way to handle
+ # Dir and File instances and will produce something
+ # reasonable for just about everything else:
+ ENV[key] = str(value)
+
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ cmd_list, ignore, silent = self.process(target, map(rfile, source), env, executor)
+
+ # Use len() to filter out any "command" that's zero-length.
+ for cmd_line in filter(len, cmd_list):
+ # Escape the command line for the interpreter we are using.
+ cmd_line = escape_list(cmd_line, escape)
+ result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
+ if not ignore and result:
+ msg = "Error %s" % result
+ return SCons.Errors.BuildError(errstr=msg,
+ status=result,
+ action=self,
+ command=cmd_line)
+ return 0
+
+ def get_presig(self, target, source, env, executor=None):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ from SCons.Subst import SUBST_SIG
+ cmd = self.cmd_list
+ if is_List(cmd):
+ cmd = string.join(map(str, cmd))
+ else:
+ cmd = str(cmd)
+ if executor:
+ return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
+ else:
+ return env.subst_target_source(cmd, SUBST_SIG, target, source)
+
+ def get_implicit_deps(self, target, source, env, executor=None):
+ icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
+ if is_String(icd) and icd[:1] == '$':
+ icd = env.subst(icd)
+ if not icd or icd in ('0', 'None'):
+ return []
+ from SCons.Subst import SUBST_SIG
+ if executor:
+ cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
+ else:
+ cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
+ res = []
+ for cmd_line in cmd_list:
+ if cmd_line:
+ d = str(cmd_line[0])
+ m = strip_quotes.match(d)
+ if m:
+ d = m.group(1)
+ d = env.WhereIs(d)
+ if d:
+ res.append(env.fs.File(d))
+ return res
+
+class CommandGeneratorAction(ActionBase):
+ """Class for command-generator actions."""
+ def __init__(self, generator, kw):
+ if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
+ self.generator = generator
+ self.gen_kw = kw
+ self.varlist = kw.get('varlist', ())
+ self.targets = kw.get('targets', '$TARGETS')
+
+ def _generate(self, target, source, env, for_signature, executor=None):
+ # ensure that target is a list, to make it easier to write
+ # generator functions:
+ if not is_List(target):
+ target = [target]
+
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ ret = self.generator(target=target,
+ source=source,
+ env=env,
+ for_signature=for_signature)
+ #TODO(1.5) gen_cmd = Action(ret, **self.gen_kw)
+ gen_cmd = apply(Action, (ret,), self.gen_kw)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
+ return gen_cmd
+
+ def __str__(self):
+ try:
+ env = self.presub_env
+ except AttributeError:
+ env = None
+ if env is None:
+ env = SCons.Defaults.DefaultEnvironment()
+ act = self._generate([], [], env, 1)
+ return str(act)
+
+ def batch_key(self, env, target, source):
+ return self._generate(target, source, env, 1).batch_key(env, target, source)
+
+ def genstring(self, target, source, env, executor=None):
+ return self._generate(target, source, env, 1, executor).genstring(target, source, env)
+
+ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
+ show=_null, execute=_null, chdir=_null, executor=None):
+ act = self._generate(target, source, env, 0, executor)
+ if act is None:
+ raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
+ return act(target, source, env, exitstatfunc, presub,
+ show, execute, chdir, executor)
+
+ def get_presig(self, target, source, env, executor=None):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
+
+ def get_implicit_deps(self, target, source, env, executor=None):
+ return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
+
+ def get_targets(self, env, executor):
+ return self._generate(None, None, env, 1, executor).get_targets(env, executor)
+
+
+
+# A LazyAction is a kind of hybrid generator and command action for
+# strings of the form "$VAR". These strings normally expand to other
+# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
+# want to be able to replace them with functions in the construction
+# environment. Consequently, we want lazy evaluation and creation of
+# an Action in the case of the function, but that's overkill in the more
+# normal case of expansion to other strings.
+#
+# So we do this with a subclass that's both a generator *and*
+# a command action. The overridden methods all do a quick check
+# of the construction variable, and if it's a string we just call
+# the corresponding CommandAction method to do the heavy lifting.
+# If not, then we call the same-named CommandGeneratorAction method.
+# The CommandGeneratorAction methods work by using the overridden
+# _generate() method, that is, our own way of handling "generation" of
+# an action based on what's in the construction variable.
+
+class LazyAction(CommandGeneratorAction, CommandAction):
+
+ def __init__(self, var, kw):
+ if __debug__: logInstanceCreation(self, 'Action.LazyAction')
+ #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
+ apply(CommandAction.__init__, (self, '${'+var+'}'), kw)
+ self.var = SCons.Util.to_String(var)
+ self.gen_kw = kw
+
+ def get_parent_class(self, env):
+ c = env.get(self.var)
+ if is_String(c) and not '\n' in c:
+ return CommandAction
+ return CommandGeneratorAction
+
+ def _generate_cache(self, env):
+ if env:
+ c = env.get(self.var, '')
+ else:
+ c = ''
+ #TODO(1.5) gen_cmd = Action(c, **self.gen_kw)
+ gen_cmd = apply(Action, (c,), self.gen_kw)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
+ return gen_cmd
+
+ def _generate(self, target, source, env, for_signature, executor=None):
+ return self._generate_cache(env)
+
+ def __call__(self, target, source, env, *args, **kw):
+ args = (self, target, source, env) + args
+ c = self.get_parent_class(env)
+ #TODO(1.5) return c.__call__(*args, **kw)
+ return apply(c.__call__, args, kw)
+
+ def get_presig(self, target, source, env):
+ c = self.get_parent_class(env)
+ return c.get_presig(self, target, source, env)
+
+
+
+class FunctionAction(_ActionAction):
+ """Class for Python function actions."""
+
+ def __init__(self, execfunction, kw):
+ if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
+
+ self.execfunction = execfunction
+ try:
+ self.funccontents = _callable_contents(execfunction)
+ except AttributeError:
+ try:
+ # See if execfunction will do the heavy lifting for us.
+ self.gc = execfunction.get_contents
+ except AttributeError:
+ # This is weird, just do the best we can.
+ self.funccontents = _object_contents(execfunction)
+
+ #TODO(1.5) _ActionAction.__init__(self, **kw)
+ apply(_ActionAction.__init__, (self,), kw)
+
+ def function_name(self):
+ try:
+ return self.execfunction.__name__
+ except AttributeError:
+ try:
+ return self.execfunction.__class__.__name__
+ except AttributeError:
+ return "unknown_python_function"
+
+ def strfunction(self, target, source, env, executor=None):
+ if self.cmdstr is None:
+ return None
+ if self.cmdstr is not _null:
+ from SCons.Subst import SUBST_RAW
+ if executor:
+ c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+ else:
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ if c:
+ return c
+ def array(a):
+ def quote(s):
+ try:
+ str_for_display = s.str_for_display
+ except AttributeError:
+ s = repr(s)
+ else:
+ s = str_for_display()
+ return s
+ return '[' + string.join(map(quote, a), ", ") + ']'
+ try:
+ strfunc = self.execfunction.strfunction
+ except AttributeError:
+ pass
+ else:
+ if strfunc is None:
+ return None
+ if callable(strfunc):
+ return strfunc(target, source, env)
+ name = self.function_name()
+ tstr = array(target)
+ sstr = array(source)
+ return "%s(%s, %s)" % (name, tstr, sstr)
+
+ def __str__(self):
+ name = self.function_name()
+ if name == 'ActionCaller':
+ return str(self.execfunction)
+ return "%s(target, source, env)" % name
+
+ def execute(self, target, source, env, executor=None):
+ exc_info = (None,None,None)
+ try:
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ rsources = map(rfile, source)
+ try:
+ result = self.execfunction(target=target, source=rsources, env=env)
+ except KeyboardInterrupt, e:
+ raise
+ except SystemExit, e:
+ raise
+ except Exception, e:
+ result = e
+ exc_info = sys.exc_info()
+
+ if result:
+ result = SCons.Errors.convert_to_BuildError(result, exc_info)
+ result.node=target
+ result.action=self
+ try:
+ result.command=self.strfunction(target, source, env, executor)
+ except TypeError:
+ result.command=self.strfunction(target, source, env)
+
+ # FIXME: This maintains backward compatibility with respect to
+ # which type of exceptions were returned by raising an
+ # exception and which ones were returned by value. It would
+ # probably be best to always return them by value here, but
+ # some codes do not check the return value of Actions and I do
+ # not have the time to modify them at this point.
+ if (exc_info[1] and
+ not isinstance(exc_info[1],EnvironmentError)):
+ raise result
+
+ return result
+ finally:
+ # Break the cycle between the traceback object and this
+ # function stack frame. See the sys.exc_info() doc info for
+ # more information about this issue.
+ del exc_info
+
+
+ def get_presig(self, target, source, env):
+ """Return the signature contents of this callable action."""
+ try:
+ return self.gc(target, source, env)
+ except AttributeError:
+ return self.funccontents
+
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class ListAction(ActionBase):
+ """Class for lists of other actions."""
+ def __init__(self, list):
+ if __debug__: logInstanceCreation(self, 'Action.ListAction')
+ def list_of_actions(x):
+ if isinstance(x, ActionBase):
+ return x
+ return Action(x)
+ self.list = map(list_of_actions, list)
+ # our children will have had any varlist
+ # applied; we don't need to do it again
+ self.varlist = ()
+ self.targets = '$TARGETS'
+
+ def genstring(self, target, source, env):
+ return string.join(map(lambda a, t=target, s=source, e=env:
+ a.genstring(t, s, e),
+ self.list),
+ '\n')
+
+ def __str__(self):
+ return string.join(map(str, self.list), '\n')
+
+ def presub_lines(self, env):
+ return SCons.Util.flatten_sequence(
+ map(lambda a, env=env: a.presub_lines(env), self.list))
+
+ def get_presig(self, target, source, env):
+ """Return the signature contents of this action list.
+
+ Simple concatenation of the signatures of the elements.
+ """
+ return string.join(map(lambda x, t=target, s=source, e=env:
+ x.get_contents(t, s, e),
+ self.list),
+ "")
+
+ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
+ show=_null, execute=_null, chdir=_null, executor=None):
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ for act in self.list:
+ stat = act(target, source, env, exitstatfunc, presub,
+ show, execute, chdir, executor)
+ if stat:
+ return stat
+ return 0
+
+ def get_implicit_deps(self, target, source, env):
+ result = []
+ for act in self.list:
+ result.extend(act.get_implicit_deps(target, source, env))
+ return result
+
+class ActionCaller:
+ """A class for delaying calling an Action function with specific
+ (positional and keyword) arguments until the Action is actually
+ executed.
+
+ This class looks to the rest of the world like a normal Action object,
+ but what it's really doing is hanging on to the arguments until we
+ have a target, source and env to use for the expansion.
+ """
+ def __init__(self, parent, args, kw):
+ self.parent = parent
+ self.args = args
+ self.kw = kw
+
+ def get_contents(self, target, source, env):
+ actfunc = self.parent.actfunc
+ try:
+ # "self.actfunc" is a function.
+ contents = str(actfunc.func_code.co_code)
+ except AttributeError:
+ # "self.actfunc" is a callable object.
+ try:
+ contents = str(actfunc.__call__.im_func.func_code.co_code)
+ except AttributeError:
+ # No __call__() method, so it might be a builtin
+ # or something like that. Do the best we can.
+ contents = str(actfunc)
+ contents = remove_set_lineno_codes(contents)
+ return contents
+
+ def subst(self, s, target, source, env):
+ # If s is a list, recursively apply subst()
+ # to every element in the list
+ if is_List(s):
+ result = []
+ for elem in s:
+ result.append(self.subst(elem, target, source, env))
+ return self.parent.convert(result)
+
+ # Special-case hack: Let a custom function wrapped in an
+ # ActionCaller get at the environment through which the action
+ # was called by using this hard-coded value as a special return.
+ if s == '$__env__':
+ return env
+ elif is_String(s):
+ return env.subst(s, 1, target, source)
+ return self.parent.convert(s)
+
+ def subst_args(self, target, source, env):
+ return map(lambda x, self=self, t=target, s=source, e=env:
+ self.subst(x, t, s, e),
+ self.args)
+
+ def subst_kw(self, target, source, env):
+ kw = {}
+ for key in self.kw.keys():
+ kw[key] = self.subst(self.kw[key], target, source, env)
+ return kw
+
+ def __call__(self, target, source, env, executor=None):
+ args = self.subst_args(target, source, env)
+ kw = self.subst_kw(target, source, env)
+ #TODO(1.5) return self.parent.actfunc(*args, **kw)
+ return apply(self.parent.actfunc, args, kw)
+
+ def strfunction(self, target, source, env):
+ args = self.subst_args(target, source, env)
+ kw = self.subst_kw(target, source, env)
+ #TODO(1.5) return self.parent.strfunc(*args, **kw)
+ return apply(self.parent.strfunc, args, kw)
+
+ def __str__(self):
+ #TODO(1.5) return self.parent.strfunc(*self.args, **self.kw)
+ return apply(self.parent.strfunc, self.args, self.kw)
+
+class ActionFactory:
+ """A factory class that will wrap up an arbitrary function
+ as an SCons-executable Action object.
+
+ The real heavy lifting here is done by the ActionCaller class.
+ We just collect the (positional and keyword) arguments that we're
+ called with and give them to the ActionCaller object we create,
+ so it can hang onto them until it needs them.
+ """
+ def __init__(self, actfunc, strfunc, convert=lambda x: x):
+ self.actfunc = actfunc
+ self.strfunc = strfunc
+ self.convert = convert
+
+ def __call__(self, *args, **kw):
+ ac = ActionCaller(self, args, kw)
+ action = Action(ac, strfunction=ac.strfunction)
+ return action
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Action.xml b/src/engine/SCons/Action.xml
new file mode 100644
index 0000000..a4eb12d
--- /dev/null
+++ b/src/engine/SCons/Action.xml
@@ -0,0 +1,107 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<cvar name="IMPLICIT_COMMAND_DEPENDENCIES">
+<summary>
+Controls whether or not SCons will
+add implicit dependencies for the commands
+executed to build targets.
+
+By default, SCons will add
+to each target
+an implicit dependency on the command
+represented by the first argument on any
+command line it executes.
+The specific file for the dependency is
+found by searching the
+<varname>PATH</varname>
+variable in the
+<varname>ENV</varname>
+environment used to execute the command.
+
+If the construction variable
+&cv-IMPLICIT_COMMAND_DEPENDENCIES;
+is set to a false value
+(<literal>None</literal>,
+<literal>False</literal>,
+<literal>0</literal>,
+etc.),
+then the implicit dependency will
+not be added to the targets
+built with that construction environment.
+
+<example>
+env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0)
+</example>
+</summary>
+</cvar>
+
+<cvar name="PRINT_CMD_LINE_FUNC">
+<summary>
+A Python function used to print the command lines as they are executed
+(assuming command printing is not disabled by the
+<option>-q</option>
+or
+<option>-s</option>
+options or their equivalents).
+The function should take four arguments:
+<varname>s</varname>,
+the command being executed (a string),
+<varname>target</varname>,
+the target being built (file node, list, or string name(s)),
+<varname>source</varname>,
+the source(s) used (file node, list, or string name(s)), and
+<varname>env</varname>,
+the environment being used.
+
+The function must do the printing itself. The default implementation,
+used if this variable is not set or is None, is:
+<example>
+def print_cmd_line(s, target, source, env):
+ sys.stdout.write(s + "\n")
+</example>
+
+Here's an example of a more interesting function:
+
+<example>
+def print_cmd_line(s, target, source, env):
+ sys.stdout.write("Building %s -> %s...\n" %
+ (' and '.join([str(x) for x in source]),
+ ' and '.join([str(x) for x in target])))
+env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
+env.Program('foo', 'foo.c')
+</example>
+
+This just prints "Building <varname>targetname</varname> from <varname>sourcename</varname>..." instead
+of the actual commands.
+Such a function could also log the actual commands to a log file,
+for example.
+</summary>
+</cvar>
+
+<cvar name="SPAWN">
+<summary>
+A command interpreter function that will be called to execute command line
+strings. The function must expect the following arguments:
+
+<example>
+def spawn(shell, escape, cmd, args, env):
+</example>
+
+<varname>sh</varname>
+is a string naming the shell program to use.
+<varname>escape</varname>
+is a function that can be called to escape shell special characters in
+the command line.
+<varname>cmd</varname>
+is the path to the command to be executed.
+<varname>args</varname>
+is the arguments to the command.
+<varname>env</varname>
+is a dictionary of the environment variables
+in which the command should be executed.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
new file mode 100644
index 0000000..6d4863e
--- /dev/null
+++ b/src/engine/SCons/ActionTests.py
@@ -0,0 +1,2013 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/ActionTests.py 4577 2009/12/27 19:44:43 scons"
+
+# Define a null function and a null class for use as builder actions.
+# Where these are defined in the file seems to affect their byte-code
+# contents, so try to minimize changes by defining them here, before we
+# even import anything.
+def GlobalFunc():
+ pass
+
+class GlobalActFunc:
+ def __call__(self):
+ pass
+
+import os
+import re
+import StringIO
+import string
+import sys
+import types
+import unittest
+import UserDict
+
+import SCons.Action
+import SCons.Environment
+import SCons.Errors
+
+import TestCmd
+
+# Initial setup of the common environment for all tests,
+# a temporary working directory containing a
+# script for writing arguments to an output file.
+#
+# We don't do this as a setUp() method because it's
+# unnecessary to create a separate directory and script
+# for each test, they can just use the one.
+test = TestCmd.TestCmd(workdir = '')
+
+test.write('act.py', """\
+import os, string, sys
+f = open(sys.argv[1], 'w')
+f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
+try:
+ if sys.argv[3]:
+ f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
+except:
+ pass
+f.close()
+if os.environ.has_key( 'ACTPY_PIPE' ):
+ if os.environ.has_key( 'PIPE_STDOUT_FILE' ):
+ stdout_msg = open(os.environ['PIPE_STDOUT_FILE'], 'r').read()
+ else:
+ stdout_msg = "act.py: stdout: executed act.py %s\\n" % string.join(sys.argv[1:])
+ sys.stdout.write( stdout_msg )
+ if os.environ.has_key( 'PIPE_STDERR_FILE' ):
+ stderr_msg = open(os.environ['PIPE_STDERR_FILE'], 'r').read()
+ else:
+ stderr_msg = "act.py: stderr: executed act.py %s\\n" % string.join(sys.argv[1:])
+ sys.stderr.write( stderr_msg )
+sys.exit(0)
+""")
+
+test.write('exit.py', """\
+import sys
+sys.exit(int(sys.argv[1]))
+""")
+
+act_py = test.workpath('act.py')
+exit_py = test.workpath('exit.py')
+
+outfile = test.workpath('outfile')
+outfile2 = test.workpath('outfile2')
+pipe_file = test.workpath('pipe.out')
+
+scons_env = SCons.Environment.Environment()
+
+# Capture all the stuff the Actions will print,
+# so it doesn't clutter the output.
+sys.stdout = StringIO.StringIO()
+
+class CmdStringHolder:
+ def __init__(self, cmd, literal=None):
+ self.data = str(cmd)
+ self.literal = literal
+
+ def is_literal(self):
+ return self.literal
+
+ def escape(self, escape_func):
+ """Escape the string with the supplied function. The
+ function is expected to take an arbitrary string, then
+ return it with all special characters escaped and ready
+ for passing to the command interpreter.
+
+ After calling this function, the next call to str() will
+ return the escaped string.
+ """
+
+ if self.is_literal():
+ return escape_func(self.data)
+ elif ' ' in self.data or '\t' in self.data:
+ return '"%s"' % self.data
+ else:
+ return self.data
+
+class Environment:
+ def __init__(self, **kw):
+ self.d = {}
+ self.d['SHELL'] = scons_env['SHELL']
+ self.d['SPAWN'] = scons_env['SPAWN']
+ self.d['PSPAWN'] = scons_env['PSPAWN']
+ self.d['ESCAPE'] = scons_env['ESCAPE']
+ for k, v in kw.items():
+ self.d[k] = v
+ # Just use the underlying scons_subst*() utility methods.
+ def subst(self, strSubst, raw=0, target=[], source=[], conv=None):
+ return SCons.Subst.scons_subst(strSubst, self, raw,
+ target, source, self.d, conv=conv)
+ subst_target_source = subst
+ def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None):
+ return SCons.Subst.scons_subst_list(strSubst, self, raw,
+ target, source, self.d, conv=conv)
+ def __getitem__(self, item):
+ return self.d[item]
+ def __setitem__(self, item, value):
+ self.d[item] = value
+ def has_key(self, item):
+ return self.d.has_key(item)
+ def get(self, key, value=None):
+ return self.d.get(key, value)
+ def items(self):
+ return self.d.items()
+ def Dictionary(self):
+ return self.d
+ def Clone(self, **kw):
+ res = Environment()
+ res.d = SCons.Util.semi_deepcopy(self.d)
+ for k, v in kw.items():
+ res.d[k] = v
+ return res
+ def sig_dict(self):
+ d = {}
+ for k,v in self.items(): d[k] = v
+ d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
+ d['TARGET'] = d['TARGETS'][0]
+ d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
+ d['SOURCE'] = d['SOURCES'][0]
+ return d
+
+class DummyNode:
+ def __init__(self, name):
+ self.name = name
+ def str_for_display(self):
+ return '"' + self.name + '"'
+ def __str__(self):
+ return self.name
+ def rfile(self):
+ return self
+ def get_subst_proxy(self):
+ return self
+
+if os.name == 'java':
+ python = os.path.join(sys.prefix, 'jython')
+else:
+ python = sys.executable
+
+_python_ = '"' + python + '"'
+
+_null = SCons.Action._null
+
+def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw):
+ def call_action(a, pos_call=pos_call, str_call=str_call, kw=kw):
+ #FUTURE a = SCons.Action.Action(*a, **kw)
+ a = apply(SCons.Action.Action, a, kw)
+ # returned object must provide these entry points
+ assert hasattr(a, '__call__')
+ assert hasattr(a, 'get_contents')
+ assert hasattr(a, 'genstring')
+ pos_call(a)
+ str_call(a)
+ return a
+
+ a = call_action((cmd, cmdstrfunc))
+ assert a.varlist == (), a.varlist
+
+ a = call_action((cmd, cmdstrfunc, 'foo'))
+ assert a.varlist == ('foo',), a.varlist
+
+ a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c'))
+ assert a.varlist == ('a', 'b', 'c'), a.varlist
+
+ kw['varlist'] = 'foo'
+ a = call_action((cmd, cmdstrfunc))
+ assert a.varlist == ('foo',), a.varlist
+
+ kw['varlist'] = ['x', 'y', 'z']
+ a = call_action((cmd, cmdstrfunc))
+ assert a.varlist == ('x', 'y', 'z'), a.varlist
+
+ a = call_action((cmd, cmdstrfunc, 'foo'))
+ assert a.varlist == ('foo', 'x', 'y', 'z'), a.varlist
+
+ a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c'))
+ assert a.varlist == ('a', 'b', 'c', 'x', 'y', 'z'), a.varlist
+
+def test_positional_args(pos_callback, cmd, **kw):
+ """Test that Action() returns the expected type and that positional args work.
+ """
+ #FUTURE act = SCons.Action.Action(cmd, **kw)
+ act = apply(SCons.Action.Action, (cmd,), kw)
+ pos_callback(act)
+ assert act.varlist is (), act.varlist
+
+ if not isinstance(act, SCons.Action._ActionAction):
+ # only valid cmdstrfunc is None
+ def none(a): pass
+ #FUTURE test_varlist(pos_callback, none, cmd, None, **kw)
+ apply(test_varlist, (pos_callback, none, cmd, None), kw)
+ else:
+ # _ActionAction should have set these
+ assert hasattr(act, 'strfunction')
+ assert act.cmdstr is _null, act.cmdstr
+ assert act.presub is _null, act.presub
+ assert act.chdir is None, act.chdir
+ assert act.exitstatfunc is SCons.Action.default_exitstatfunc, \
+ act.exitstatfunc
+
+ def cmdstr(a):
+ assert hasattr(a, 'strfunction')
+ assert a.cmdstr == 'cmdstr', a.cmdstr
+ #FUTURE test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw)
+ apply(test_varlist, (pos_callback, cmdstr, cmd, 'cmdstr'), kw)
+
+ def fun(): pass
+ def strfun(a, fun=fun):
+ assert a.strfunction is fun, a.strfunction
+ assert a.cmdstr == _null, a.cmdstr
+ #FUTURE test_varlist(pos_callback, strfun, cmd, fun, **kw)
+ apply(test_varlist, (pos_callback, strfun, cmd, fun), kw)
+
+ def none(a):
+ assert hasattr(a, 'strfunction')
+ assert a.cmdstr is None, a.cmdstr
+ #FUTURE test_varlist(pos_callback, none, cmd, None, **kw)
+ apply(test_varlist, (pos_callback, none, cmd, None), kw)
+
+ """Test handling of bad cmdstrfunc arguments """
+ try:
+ #FUTURE a = SCons.Action.Action(cmd, [], **kw)
+ a = apply(SCons.Action.Action, (cmd, []), kw)
+ except SCons.Errors.UserError, e:
+ s = str(e)
+ m = 'Invalid command display variable'
+ assert string.find(s, m) != -1, 'Unexpected string: %s' % s
+ else:
+ raise Exception, "did not catch expected UserError"
+
+ return act
+
+class ActionTestCase(unittest.TestCase):
+ """Test the Action() factory function"""
+
+ def test_FunctionAction(self):
+ """Test the Action() factory's creation of FunctionAction objects
+ """
+ def foo():
+ pass
+
+ def func_action(a, foo=foo):
+ assert isinstance(a, SCons.Action.FunctionAction), a
+ assert a.execfunction == foo, a.execfunction
+ test_positional_args(func_action, foo)
+ # a singleton list returns the contained action
+ test_positional_args(func_action, [foo])
+
+ def test_CommandAction(self):
+ """Test the Action() factory's creation of CommandAction objects
+ """
+ def cmd_action(a):
+ assert isinstance(a, SCons.Action.CommandAction), a
+ assert a.cmd_list == "string", a.cmd_list
+ test_positional_args(cmd_action, "string")
+ # a singleton list returns the contained action
+ test_positional_args(cmd_action, ["string"])
+
+ if hasattr(types, 'UnicodeType'):
+ a2 = eval("SCons.Action.Action(u'string')")
+ assert isinstance(a2, SCons.Action.CommandAction), a2
+
+ def line_action(a):
+ assert isinstance(a, SCons.Action.CommandAction), a
+ assert a.cmd_list == [ "explicit", "command", "line" ], a.cmd_list
+ test_positional_args(line_action, [[ "explicit", "command", "line" ]])
+
+ def test_ListAction(self):
+ """Test the Action() factory's creation of ListAction objects
+ """
+ a1 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]])
+ assert isinstance(a1, SCons.Action.ListAction), a1
+ assert a1.varlist is (), a1.varlist
+ assert isinstance(a1.list[0], SCons.Action.CommandAction), a1.list[0]
+ assert a1.list[0].cmd_list == "x", a1.list[0].cmd_list
+ assert isinstance(a1.list[1], SCons.Action.CommandAction), a1.list[1]
+ assert a1.list[1].cmd_list == "y", a1.list[1].cmd_list
+ assert isinstance(a1.list[2], SCons.Action.CommandAction), a1.list[2]
+ assert a1.list[2].cmd_list == "z", a1.list[2].cmd_list
+ assert isinstance(a1.list[3], SCons.Action.CommandAction), a1.list[3]
+ assert a1.list[3].cmd_list == [ "a", "b", "c" ], a1.list[3].cmd_list
+
+ a2 = SCons.Action.Action("x\ny\nz")
+ assert isinstance(a2, SCons.Action.ListAction), a2
+ assert a2.varlist is (), a2.varlist
+ assert isinstance(a2.list[0], SCons.Action.CommandAction), a2.list[0]
+ assert a2.list[0].cmd_list == "x", a2.list[0].cmd_list
+ assert isinstance(a2.list[1], SCons.Action.CommandAction), a2.list[1]
+ assert a2.list[1].cmd_list == "y", a2.list[1].cmd_list
+ assert isinstance(a2.list[2], SCons.Action.CommandAction), a2.list[2]
+ assert a2.list[2].cmd_list == "z", a2.list[2].cmd_list
+
+ def foo():
+ pass
+
+ a3 = SCons.Action.Action(["x", foo, "z"])
+ assert isinstance(a3, SCons.Action.ListAction), a3
+ assert a3.varlist is (), a3.varlist
+ assert isinstance(a3.list[0], SCons.Action.CommandAction), a3.list[0]
+ assert a3.list[0].cmd_list == "x", a3.list[0].cmd_list
+ assert isinstance(a3.list[1], SCons.Action.FunctionAction), a3.list[1]
+ assert a3.list[1].execfunction == foo, a3.list[1].execfunction
+ assert isinstance(a3.list[2], SCons.Action.CommandAction), a3.list[2]
+ assert a3.list[2].cmd_list == "z", a3.list[2].cmd_list
+
+ a4 = SCons.Action.Action(["x", "y"], strfunction=foo)
+ assert isinstance(a4, SCons.Action.ListAction), a4
+ assert a4.varlist is (), a4.varlist
+ assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0]
+ assert a4.list[0].cmd_list == "x", a4.list[0].cmd_list
+ assert a4.list[0].strfunction == foo, a4.list[0].strfunction
+ assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1]
+ assert a4.list[1].cmd_list == "y", a4.list[1].cmd_list
+ assert a4.list[1].strfunction == foo, a4.list[1].strfunction
+
+ a5 = SCons.Action.Action("x\ny", strfunction=foo)
+ assert isinstance(a5, SCons.Action.ListAction), a5
+ assert a5.varlist is (), a5.varlist
+ assert isinstance(a5.list[0], SCons.Action.CommandAction), a5.list[0]
+ assert a5.list[0].cmd_list == "x", a5.list[0].cmd_list
+ assert a5.list[0].strfunction == foo, a5.list[0].strfunction
+ assert isinstance(a5.list[1], SCons.Action.CommandAction), a5.list[1]
+ assert a5.list[1].cmd_list == "y", a5.list[1].cmd_list
+ assert a5.list[1].strfunction == foo, a5.list[1].strfunction
+
+ def test_CommandGeneratorAction(self):
+ """Test the Action() factory's creation of CommandGeneratorAction objects
+ """
+ def foo(): pass
+
+ def gen_action(a, foo=foo):
+ assert isinstance(a, SCons.Action.CommandGeneratorAction), a
+ assert a.generator is foo, a.generator
+ test_positional_args(gen_action, foo, generator=1)
+
+ def test_LazyCmdGeneratorAction(self):
+ """Test the Action() factory's creation of lazy CommandGeneratorAction objects
+ """
+ def lazy_action(a):
+ assert isinstance(a, SCons.Action.LazyAction), a
+ assert a.var == "FOO", a.var
+ assert a.cmd_list == "${FOO}", a.cmd_list
+ test_positional_args(lazy_action, "$FOO")
+ test_positional_args(lazy_action, "${FOO}")
+
+ def test_no_action(self):
+ """Test when the Action() factory can't create an action object
+ """
+ a5 = SCons.Action.Action(1)
+ assert a5 is None, a5
+
+ def test_reentrance(self):
+ """Test the Action() factory when the action is already an Action object
+ """
+ a1 = SCons.Action.Action("foo")
+ a2 = SCons.Action.Action(a1)
+ assert a2 is a1, a2
+
+class _ActionActionTestCase(unittest.TestCase):
+
+ def test__init__(self):
+ """Test creation of _ActionAction objects
+ """
+
+ def func1():
+ pass
+
+ def func2():
+ pass
+
+ def func3():
+ pass
+
+ a = SCons.Action._ActionAction()
+ assert not hasattr(a, 'strfunction')
+ assert a.cmdstr is _null, a.cmdstr
+ assert a.varlist == (), a.varlist
+ assert a.presub is _null, a.presub
+ assert a.chdir is None, a.chdir
+ assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc
+
+ assert SCons.Action._ActionAction(kwarg = 1)
+ assert not hasattr(a, 'kwarg')
+ assert not hasattr(a, 'strfunction')
+ assert a.cmdstr is _null, a.cmdstr
+ assert a.varlist == (), a.varlist
+ assert a.presub is _null, a.presub
+ assert a.chdir is None, a.chdir
+ assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc
+
+ a = SCons.Action._ActionAction(strfunction=func1)
+ assert a.strfunction is func1, a.strfunction
+
+ a = SCons.Action._ActionAction(strfunction=None)
+ assert not hasattr(a, 'strfunction')
+ assert a.cmdstr is None, a.cmdstr
+
+ a = SCons.Action._ActionAction(cmdstr='cmdstr')
+ assert not hasattr(a, 'strfunction')
+ assert a.cmdstr is 'cmdstr', a.cmdstr
+
+ a = SCons.Action._ActionAction(cmdstr=None)
+ assert not hasattr(a, 'strfunction')
+ assert a.cmdstr is None, a.cmdstr
+
+ t = ('a','b','c')
+ a = SCons.Action._ActionAction(varlist=t)
+ assert a.varlist == t, a.varlist
+
+ a = SCons.Action._ActionAction(presub=func1)
+ assert a.presub is func1, a.presub
+
+ a = SCons.Action._ActionAction(chdir=1)
+ assert a.chdir is 1, a.chdir
+
+ a = SCons.Action._ActionAction(exitstatfunc=func1)
+ assert a.exitstatfunc is func1, a.exitstatfunc
+
+ a = SCons.Action._ActionAction(
+ # alphabetical order ...
+ chdir='x',
+ cmdstr='cmdstr',
+ exitstatfunc=func3,
+ presub=func2,
+ strfunction=func1,
+ varlist=t,
+ )
+ assert a.chdir is 'x', a.chdir
+ assert a.cmdstr is 'cmdstr', a.cmdstr
+ assert a.exitstatfunc is func3, a.exitstatfunc
+ assert a.presub is func2, a.presub
+ assert a.strfunction is func1, a.strfunction
+ assert a.varlist is t, a.varlist
+
+ def test_dup_keywords(self):
+ """Test handling of both cmdstr and strfunction arguments
+ """
+ def func(): pass
+ try:
+ a = SCons.Action.Action('foo', cmdstr='string', strfunction=func)
+ except SCons.Errors.UserError, e:
+ s = str(e)
+ m = 'Cannot have both strfunction and cmdstr args to Action()'
+ assert string.find(s, m) != -1, 'Unexpected string: %s' % s
+ else:
+ raise Exception, "did not catch expected UserError"
+
+ def test___cmp__(self):
+ """Test Action comparison
+ """
+ a1 = SCons.Action.Action("x")
+ a2 = SCons.Action.Action("x")
+ assert a1 == a2
+ a3 = SCons.Action.Action("y")
+ assert a1 != a3
+ assert a2 != a3
+
+ def test_print_cmd_lines(self):
+ """Test the print_cmd_lines() method
+ """
+ save_stdout = sys.stdout
+
+ try:
+ def execfunc(target, source, env):
+ pass
+ a = SCons.Action.Action(execfunc)
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ a.print_cmd_line("foo bar", None, None, None)
+ s = sio.getvalue()
+ assert s == "foo bar\n", s
+
+ finally:
+ sys.stdout = save_stdout
+
+ def test___call__(self):
+ """Test calling an Action
+ """
+ save_stdout = sys.stdout
+
+ save_print_actions = SCons.Action.print_actions
+ save_print_actions_presub = SCons.Action.print_actions_presub
+ save_execute_actions = SCons.Action.execute_actions
+ #SCons.Action.print_actions = 0
+
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub', 'xyz')
+ os.chdir(test.workpath())
+
+ try:
+ env = Environment()
+
+ def execfunc(target, source, env):
+ assert type(target) is type([]), type(target)
+ assert type(source) is type([]), type(source)
+ return 7
+ a = SCons.Action.Action(execfunc)
+
+ def firstfunc(target, source, env):
+ assert type(target) is type([]), type(target)
+ assert type(source) is type([]), type(source)
+ return 0
+ def lastfunc(target, source, env):
+ assert type(target) is type([]), type(target)
+ assert type(source) is type([]), type(source)
+ return 9
+ b = SCons.Action.Action([firstfunc, execfunc, lastfunc])
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env)
+ assert result.status == 7, result
+ s = sio.getvalue()
+ assert s == "execfunc(['out'], ['in'])\n", s
+
+ a.chdir = 'xyz'
+ expect = "os.chdir(%s)\nexecfunc(['out'], ['in'])\nos.chdir(%s)\n"
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == expect % (repr('xyz'), repr(test.workpath())), s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env, chdir='sub')
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == expect % (repr('sub'), repr(test.workpath())), s
+
+ a.chdir = None
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = b("out", "in", env)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\n", s
+
+ SCons.Action.execute_actions = 0
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env)
+ assert result == 0, result
+ s = sio.getvalue()
+ assert s == "execfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = b("out", "in", env)
+ assert result == 0, result
+ s = sio.getvalue()
+ assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\nlastfunc(['out'], ['in'])\n", s
+
+ SCons.Action.print_actions_presub = 1
+ SCons.Action.execute_actions = 1
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env, presub=0)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "execfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env, presub=1)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = b(["out"], "in", env, presub=1)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "Building out with action:\n firstfunc(target, source, env)\nfirstfunc(['out'], ['in'])\nBuilding out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = b(["out", "list"], "in", env, presub=1)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "Building out and list with action:\n firstfunc(target, source, env)\nfirstfunc(['out', 'list'], ['in'])\nBuilding out and list with action:\n execfunc(target, source, env)\nexecfunc(['out', 'list'], ['in'])\n", s
+
+ a2 = SCons.Action.Action(execfunc)
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a2("out", "in", env)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a2("out", "in", env, presub=0)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == "execfunc(['out'], ['in'])\n", s
+
+ SCons.Action.execute_actions = 0
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a2("out", "in", env, presub=0)
+ assert result == 0, result
+ s = sio.getvalue()
+ assert s == "execfunc(['out'], ['in'])\n", s
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ result = a("out", "in", env, presub=0, execute=1, show=0)
+ assert result.status == 7, result.status
+ s = sio.getvalue()
+ assert s == '', s
+
+ sys.stdout = save_stdout
+ exitstatfunc_result = []
+
+ def exitstatfunc(stat, result=exitstatfunc_result):
+ result.append(stat)
+ return stat
+
+ result = a("out", "in", env, exitstatfunc=exitstatfunc)
+ assert result == 0, result
+ assert exitstatfunc_result == [], exitstatfunc_result
+
+ result = a("out", "in", env, execute=1, exitstatfunc=exitstatfunc)
+ assert result.status == 7, result.status
+ assert exitstatfunc_result == [7], exitstatfunc_result
+
+ SCons.Action.execute_actions = 1
+
+ result = []
+ def my_print_cmd_line(s, target, source, env, result=result):
+ result.append(s)
+ env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line
+ a("output", "input", env)
+ assert result == ["execfunc(['output'], ['input'])"], result
+
+
+ finally:
+ sys.stdout = save_stdout
+ SCons.Action.print_actions = save_print_actions
+ SCons.Action.print_actions_presub = save_print_actions_presub
+ SCons.Action.execute_actions = save_execute_actions
+
+ def test_presub_lines(self):
+ """Test the presub_lines() method
+ """
+ env = Environment()
+ a = SCons.Action.Action("x")
+ s = a.presub_lines(env)
+ assert s == ['x'], s
+
+ a = SCons.Action.Action(["y", "z"])
+ s = a.presub_lines(env)
+ assert s == ['y', 'z'], s
+
+ def func():
+ pass
+ a = SCons.Action.Action(func)
+ s = a.presub_lines(env)
+ assert s == ["func(target, source, env)"], s
+
+ def gen(target, source, env, for_signature):
+ return 'generat' + env.get('GEN', 'or')
+ a = SCons.Action.Action(gen, generator=1)
+ s = a.presub_lines(env)
+ assert s == ["generator"], s
+ s = a.presub_lines(Environment(GEN = 'ed'))
+ assert s == ["generated"], s
+
+ a = SCons.Action.Action("$ACT")
+ s = a.presub_lines(env)
+ assert s == [''], s
+ s = a.presub_lines(Environment(ACT = 'expanded action'))
+ assert s == ['expanded action'], s
+
+ def test_add(self):
+ """Test adding Actions to stuff."""
+ # Adding actions to other Actions or to stuff that can
+ # be converted into an Action should produce a ListAction
+ # containing all the Actions.
+ def bar():
+ return None
+ baz = SCons.Action.Action(bar, generator=1)
+ act1 = SCons.Action.Action('foo bar')
+ act2 = SCons.Action.Action([ 'foo', bar ])
+
+ sum = act1 + act2
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 3, len(sum.list)
+ assert map(lambda x: isinstance(x, SCons.Action.ActionBase),
+ sum.list) == [ 1, 1, 1 ]
+
+ sum = act1 + act1
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 2, len(sum.list)
+
+ sum = act2 + act2
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 4, len(sum.list)
+
+ # Should also be able to add command generators to each other
+ # or to actions
+ sum = baz + baz
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 2, len(sum.list)
+
+ sum = baz + act1
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 2, len(sum.list)
+
+ sum = act2 + baz
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 3, len(sum.list)
+
+ # Also should be able to add Actions to anything that can
+ # be converted into an action.
+ sum = act1 + bar
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 2, len(sum.list)
+ assert isinstance(sum.list[1], SCons.Action.FunctionAction)
+
+ sum = 'foo bar' + act2
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 3, len(sum.list)
+ assert isinstance(sum.list[0], SCons.Action.CommandAction)
+
+ sum = [ 'foo', 'bar' ] + act1
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 3, sum.list
+ assert isinstance(sum.list[0], SCons.Action.CommandAction)
+ assert isinstance(sum.list[1], SCons.Action.CommandAction)
+
+ sum = act2 + [ baz, bar ]
+ assert isinstance(sum, SCons.Action.ListAction), str(sum)
+ assert len(sum.list) == 4, len(sum.list)
+ assert isinstance(sum.list[2], SCons.Action.CommandGeneratorAction)
+ assert isinstance(sum.list[3], SCons.Action.FunctionAction)
+
+ try:
+ sum = act2 + 1
+ except TypeError:
+ pass
+ else:
+ assert 0, "Should have thrown a TypeError adding to an int."
+
+ try:
+ sum = 1 + act2
+ except TypeError:
+ pass
+ else:
+ assert 0, "Should have thrown a TypeError adding to an int."
+
+class CommandActionTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test creation of a command Action
+ """
+ a = SCons.Action.CommandAction(["xyzzy"])
+ assert a.cmd_list == [ "xyzzy" ], a.cmd_list
+ assert a.cmdstr is _null, a.cmdstr
+
+ a = SCons.Action.CommandAction(["abra"], cmdstr="cadabra")
+ assert a.cmd_list == [ "abra" ], a.cmd_list
+ assert a.cmdstr == "cadabra", a.cmdstr
+
+ def test___str__(self):
+ """Test fetching the pre-substitution string for command Actions
+ """
+ env = Environment()
+ act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
+ s = str(act)
+ assert s == 'xyzzy $TARGET $SOURCE', s
+
+ act = SCons.Action.CommandAction(['xyzzy',
+ '$TARGET', '$SOURCE',
+ '$TARGETS', '$SOURCES'])
+ s = str(act)
+ assert s == "xyzzy $TARGET $SOURCE $TARGETS $SOURCES", s
+
+ def test_genstring(self):
+ """Test the genstring() method for command Actions
+ """
+
+ env = Environment()
+ t1 = DummyNode('t1')
+ t2 = DummyNode('t2')
+ s1 = DummyNode('s1')
+ s2 = DummyNode('s2')
+ act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
+ expect = 'xyzzy $TARGET $SOURCE'
+ s = act.genstring([], [], env)
+ assert s == expect, s
+ s = act.genstring([t1], [s1], env)
+ assert s == expect, s
+ s = act.genstring([t1, t2], [s1, s2], env)
+ assert s == expect, s
+
+ act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
+ expect = 'xyzzy $TARGETS $SOURCES'
+ s = act.genstring([], [], env)
+ assert s == expect, s
+ s = act.genstring([t1], [s1], env)
+ assert s == expect, s
+ s = act.genstring([t1, t2], [s1, s2], env)
+ assert s == expect, s
+
+ act = SCons.Action.CommandAction(['xyzzy',
+ '$TARGET', '$SOURCE',
+ '$TARGETS', '$SOURCES'])
+ expect = "xyzzy $TARGET $SOURCE $TARGETS $SOURCES"
+ s = act.genstring([], [], env)
+ assert s == expect, s
+ s = act.genstring([t1], [s1], env)
+ assert s == expect, s
+ s = act.genstring([t1, t2], [s1, s2], env)
+ assert s == expect, s
+
+ def test_strfunction(self):
+ """Test fetching the string representation of command Actions
+ """
+
+ env = Environment()
+ t1 = DummyNode('t1')
+ t2 = DummyNode('t2')
+ s1 = DummyNode('s1')
+ s2 = DummyNode('s2')
+ act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
+ s = act.strfunction([], [], env)
+ assert s == 'xyzzy', s
+ s = act.strfunction([t1], [s1], env)
+ assert s == 'xyzzy t1 s1', s
+ s = act.strfunction([t1, t2], [s1, s2], env)
+ assert s == 'xyzzy t1 s1', s
+
+ act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE',
+ cmdstr='cmdstr - $SOURCE - $TARGET -')
+ s = act.strfunction([], [], env)
+ assert s == 'cmdstr - - -', s
+ s = act.strfunction([t1], [s1], env)
+ assert s == 'cmdstr - s1 - t1 -', s
+ s = act.strfunction([t1, t2], [s1, s2], env)
+ assert s == 'cmdstr - s1 - t1 -', s
+
+ act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
+ s = act.strfunction([], [], env)
+ assert s == 'xyzzy', s
+ s = act.strfunction([t1], [s1], env)
+ assert s == 'xyzzy t1 s1', s
+ s = act.strfunction([t1, t2], [s1, s2], env)
+ assert s == 'xyzzy t1 t2 s1 s2', s
+
+ act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES',
+ cmdstr='cmdstr = $SOURCES = $TARGETS =')
+ s = act.strfunction([], [], env)
+ assert s == 'cmdstr = = =', s
+ s = act.strfunction([t1], [s1], env)
+ assert s == 'cmdstr = s1 = t1 =', s
+ s = act.strfunction([t1, t2], [s1, s2], env)
+ assert s == 'cmdstr = s1 s2 = t1 t2 =', s
+
+ act = SCons.Action.CommandAction(['xyzzy',
+ '$TARGET', '$SOURCE',
+ '$TARGETS', '$SOURCES'])
+ s = act.strfunction([], [], env)
+ assert s == 'xyzzy', s
+ s = act.strfunction([t1], [s1], env)
+ assert s == 'xyzzy t1 s1 t1 s1', s
+ s = act.strfunction([t1, t2], [s1, s2], env)
+ assert s == 'xyzzy t1 s1 t1 t2 s1 s2', s
+
+ act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES',
+ cmdstr='cmdstr\t$TARGETS\n$SOURCES ')
+
+ s = act.strfunction([], [], env)
+ assert s == 'cmdstr\t\n ', s
+ s = act.strfunction([t1], [s1], env)
+ assert s == 'cmdstr\tt1\ns1 ', s
+ s = act.strfunction([t1, t2], [s1, s2], env)
+ assert s == 'cmdstr\tt1 t2\ns1 s2 ', s
+
+ def sf(target, source, env):
+ return "sf was called"
+ act = SCons.Action.CommandAction('foo', strfunction=sf)
+ s = act.strfunction([], [], env)
+ assert s == "sf was called", s
+
+ class actclass1:
+ def __init__(self, targets, sources, env):
+ pass
+ def __call__(self):
+ return 1
+ class actclass2:
+ def __init__(self, targets, sources, env):
+ self.strfunction = 5
+ def __call__(self):
+ return 2
+ class actclass3:
+ def __init__(self, targets, sources, env):
+ pass
+ def __call__(self):
+ return 3
+ def strfunction(self, targets, sources, env):
+ return 'actclass3 on %s to get %s'%(str(sources[0]),
+ str(targets[0]))
+ class actclass4:
+ def __init__(self, targets, sources, env):
+ pass
+ def __call__(self):
+ return 4
+ strfunction = None
+
+ act1 = SCons.Action.Action(actclass1([t1], [s1], env))
+ s = act1.strfunction([t1], [s1], env)
+ assert s == 'actclass1(["t1"], ["s1"])', s
+
+ act2 = SCons.Action.Action(actclass2([t1], [s1], env))
+ s = act2.strfunction([t1], [s1], env)
+ assert s == 'actclass2(["t1"], ["s1"])', s
+
+ act3 = SCons.Action.Action(actclass3([t1], [s1], env))
+ s = act3.strfunction([t1], [s1], env)
+ assert s == 'actclass3 on s1 to get t1', s
+
+ act4 = SCons.Action.Action(actclass4([t1], [s1], env))
+ s = act4.strfunction([t1], [s1], env)
+ assert s is None, s
+
+ act = SCons.Action.CommandAction("@foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "", s
+
+ act = SCons.Action.CommandAction("@-foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "", s
+
+ act = SCons.Action.CommandAction("-@foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "", s
+
+ act = SCons.Action.CommandAction("-foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "foo bar", s
+
+ act = SCons.Action.CommandAction("@ foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "", s
+
+ act = SCons.Action.CommandAction("@- foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "", s
+
+ act = SCons.Action.CommandAction("-@ foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "", s
+
+ act = SCons.Action.CommandAction("- foo bar")
+ s = act.strfunction([], [], env)
+ assert s == "foo bar", s
+
+ def test_execute(self):
+ """Test execution of command Actions
+
+ """
+ try:
+ env = self.env
+ except AttributeError:
+ env = Environment()
+
+ cmd1 = r'%s %s %s xyzzy' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd1)
+ r = act([], [], env.Clone())
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'xyzzy'\n", c
+
+ cmd2 = r'%s %s %s $TARGET' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd2)
+ r = act(DummyNode('foo'), [], env.Clone())
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'foo'\n", c
+
+ cmd3 = r'%s %s %s ${TARGETS}' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd3)
+ r = act(map(DummyNode, ['aaa', 'bbb']), [], env.Clone())
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'aaa' 'bbb'\n", c
+
+ cmd4 = r'%s %s %s $SOURCES' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd4)
+ r = act([], [DummyNode('one'), DummyNode('two')], env.Clone())
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'one' 'two'\n", c
+
+ cmd4 = r'%s %s %s ${SOURCES[:2]}' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd4)
+ sources = [DummyNode('three'), DummyNode('four'), DummyNode('five')]
+ env2 = env.Clone()
+ r = act([], source = sources, env = env2)
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'three' 'four'\n", c
+
+ cmd5 = r'%s %s %s $TARGET XYZZY' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd5)
+ env5 = Environment()
+ if scons_env.has_key('ENV'):
+ env5['ENV'] = scons_env['ENV']
+ PATH = scons_env['ENV'].get('PATH', '')
+ else:
+ env5['ENV'] = {}
+ PATH = ''
+
+ env5['ENV']['XYZZY'] = 'xyzzy'
+ r = act(target = DummyNode('out5'), source = [], env = env5)
+
+ act = SCons.Action.CommandAction(cmd5)
+ r = act(target = DummyNode('out5'),
+ source = [],
+ env = env.Clone(ENV = {'XYZZY' : 'xyzzy5',
+ 'PATH' : PATH}))
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c
+
+ class Obj:
+ def __init__(self, str):
+ self._str = str
+ def __str__(self):
+ return self._str
+ def rfile(self):
+ return self
+ def get_subst_proxy(self):
+ return self
+
+ cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (_python_, act_py, outfile)
+
+ act = SCons.Action.CommandAction(cmd6)
+ r = act(target = [Obj('111'), Obj('222')],
+ source = [Obj('333'), Obj('444'), Obj('555')],
+ env = env.Clone())
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: '222' '111' '333' '444'\n", c
+
+ if os.name == 'nt':
+ # NT treats execs of directories and non-executable files
+ # as "file not found" errors
+ expect_nonexistent = 1
+ expect_nonexecutable_file = 1
+ expect_nonexecutable_dir = 1
+ elif sys.platform == 'cygwin':
+ expect_nonexistent = 127
+ # Newer cygwin seems to return 126 for following
+ expect_nonexecutable_file = 126
+ expect_nonexecutable_dir = 127
+ else:
+ expect_nonexistent = 127
+ expect_nonexecutable_file = 126
+ expect_nonexecutable_dir = 126
+
+ # Test that a nonexistent command returns 127
+ act = SCons.Action.CommandAction(python + "_no_such_command_")
+ r = act([], [], env.Clone(out = outfile))
+ assert r.status == expect_nonexistent, r.status
+
+ # Test that trying to execute a directory returns 126
+ dir, tail = os.path.split(python)
+ act = SCons.Action.CommandAction(dir)
+ r = act([], [], env.Clone(out = outfile))
+ assert r.status == expect_nonexecutable_file, r.status
+
+ # Test that trying to execute a non-executable file returns 126
+ act = SCons.Action.CommandAction(outfile)
+ r = act([], [], env.Clone(out = outfile))
+ assert r.status == expect_nonexecutable_dir, r.status
+
+ act = SCons.Action.CommandAction('%s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r.status == 1, r.status
+
+ act = SCons.Action.CommandAction('@%s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r.status == 1, r.status
+
+ act = SCons.Action.CommandAction('@-%s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r == 0, r
+
+ act = SCons.Action.CommandAction('-%s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r == 0, r
+
+ act = SCons.Action.CommandAction('@ %s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r.status == 1, r.status
+
+ act = SCons.Action.CommandAction('@- %s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r == 0, r
+
+ act = SCons.Action.CommandAction('- %s %s 1' % (_python_, exit_py))
+ r = act([], [], env)
+ assert r == 0, r
+
+ def _DO_NOT_EXECUTE_test_pipe_execute(self):
+ """Test capturing piped output from an action
+
+ We used to have PIPE_BUILD support built right into
+ Action.execute() for the benefit of the SConf subsystem, but we've
+ moved that logic back into SConf itself. We'll leave this code
+ here, just in case we ever want to resurrect this functionality
+ in the future, but change the name of the test so it doesn't
+ get executed as part of the normal test suite.
+ """
+ pipe = open( pipe_file, "w" )
+ self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1,
+ PSTDOUT = pipe, PSTDERR = pipe)
+ # everything should also work when piping output
+ self.test_execute()
+ self.env['PSTDOUT'].close()
+ pipe_out = test.read( pipe_file )
+
+ act_out = "act.py: stdout: executed act.py"
+ act_err = "act.py: stderr: executed act.py"
+
+ # Since we are now using select(), stdout and stderr can be
+ # intermixed, so count the lines separately.
+ outlines = re.findall(act_out, pipe_out)
+ errlines = re.findall(act_err, pipe_out)
+ assert len(outlines) == 6, pipe_out + repr(outlines)
+ assert len(errlines) == 6, pipe_out + repr(errlines)
+
+ # test redirection operators
+ def test_redirect(self, redir, stdout_msg, stderr_msg):
+ cmd = r'%s %s %s xyzzy %s' % (_python_, act_py, outfile, redir)
+ # Write the output and error messages to files because
+ # Windows can't handle strings that are too big in its
+ # external environment (os.spawnve() returns EINVAL,
+ # "Invalid argument").
+ stdout_file = test.workpath('stdout_msg')
+ stderr_file = test.workpath('stderr_msg')
+ open(stdout_file, 'w').write(stdout_msg)
+ open(stderr_file, 'w').write(stderr_msg)
+ pipe = open( pipe_file, "w" )
+ act = SCons.Action.CommandAction(cmd)
+ env = Environment( ENV = {'ACTPY_PIPE' : '1',
+ 'PIPE_STDOUT_FILE' : stdout_file,
+ 'PIPE_STDERR_FILE' : stderr_file},
+ PIPE_BUILD = 1,
+ PSTDOUT = pipe, PSTDERR = pipe )
+ r = act([], [], env)
+ pipe.close()
+ assert r == 0
+ return (test.read(outfile2, 'r'), test.read(pipe_file, 'r'))
+
+ (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
+ act_out, act_err)
+ assert redirected == act_out
+ assert pipe_out == act_err
+
+ (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2,
+ act_out, act_err)
+ assert redirected == act_err
+ assert pipe_out == act_out
+
+ (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2,
+ act_out, act_err)
+ assert (redirected == act_out + act_err or
+ redirected == act_err + act_out)
+ assert pipe_out == ""
+
+ act_err = "Long Command Output\n"*3000
+ # the size of the string should exceed the system's default block size
+ act_out = ""
+ (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
+ act_out, act_err)
+ assert (redirected == act_out)
+ assert (pipe_out == act_err)
+
+ def test_set_handler(self):
+ """Test setting the command handler...
+ """
+ class Test:
+ def __init__(self):
+ self.executed = 0
+ t=Test()
+ def func(sh, escape, cmd, args, env, test=t):
+ test.executed = args
+ test.shell = sh
+ return 0
+ def escape_func(cmd):
+ return '**' + cmd + '**'
+
+ class LiteralStr:
+ def __init__(self, x):
+ self.data = x
+ def __str__(self):
+ return self.data
+ def escape(self, escape_func):
+ return escape_func(self.data)
+ def is_literal(self):
+ return 1
+
+ a = SCons.Action.CommandAction(["xyzzy"])
+ e = Environment(SPAWN = func)
+ a([], [], e)
+ assert t.executed == [ 'xyzzy' ], t.executed
+
+ a = SCons.Action.CommandAction(["xyzzy"])
+ e = Environment(SPAWN = '$FUNC', FUNC = func)
+ a([], [], e)
+ assert t.executed == [ 'xyzzy' ], t.executed
+
+ a = SCons.Action.CommandAction(["xyzzy"])
+ e = Environment(SPAWN = func, SHELL = 'fake shell')
+ a([], [], e)
+ assert t.executed == [ 'xyzzy' ], t.executed
+ assert t.shell == 'fake shell', t.shell
+
+ a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
+ e = Environment(SPAWN = func, ESCAPE = escape_func)
+ a([], [], e)
+ assert t.executed == [ '**xyzzy**' ], t.executed
+
+ def test_get_contents(self):
+ """Test fetching the contents of a command Action
+ """
+ def CmdGen(target, source, env, for_signature):
+ assert for_signature
+ return "%s %s" % \
+ (env["foo"], env["bar"])
+
+ # The number 1 is there to make sure all args get converted to strings.
+ a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
+ "$)", "|", "$baz", 1])
+ c = a.get_contents(target=[], source=[],
+ env=Environment(foo = 'FFF', bar = 'BBB',
+ baz = CmdGen))
+ assert c == "| | FFF BBB 1", c
+
+ # Make sure that CommandActions use an Environment's
+ # subst_target_source() method for substitution.
+ class SpecialEnvironment(Environment):
+ def subst_target_source(self, strSubst, raw=0, target=[], source=[]):
+ return 'subst_target_source: ' + strSubst
+
+ c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'),
+ env=SpecialEnvironment(foo = 'GGG', bar = 'CCC',
+ baz = 'ZZZ'))
+ assert c == 'subst_target_source: | $( $foo | $bar $) | $baz 1', c
+
+ # We've discussed using the real target and source names in a
+ # CommandAction's signature contents. This would have have the
+ # advantage of recompiling when a file's name changes (keeping
+ # debug info current), but it would currently break repository
+ # logic that will change the file name based on whether the
+ # files come from a repository or locally. If we ever move to
+ # that scheme, then all of the '__t1__' and '__s6__' file names
+ # in the asserts below would change to 't1' and 's6' and the
+ # like.
+ t = map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6'])
+ s = map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6'])
+ env = Environment()
+
+ a = SCons.Action.CommandAction(["$TARGET"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "t1", c
+
+ a = SCons.Action.CommandAction(["$TARGETS"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "t1 t2 t3 t4 t5 t6", c
+
+ a = SCons.Action.CommandAction(["${TARGETS[2]}"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "t3", c
+
+ a = SCons.Action.CommandAction(["${TARGETS[3:5]}"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "t4 t5", c
+
+ a = SCons.Action.CommandAction(["$SOURCE"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "s1", c
+
+ a = SCons.Action.CommandAction(["$SOURCES"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "s1 s2 s3 s4 s5 s6", c
+
+ a = SCons.Action.CommandAction(["${SOURCES[2]}"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "s3", c
+
+ a = SCons.Action.CommandAction(["${SOURCES[3:5]}"])
+ c = a.get_contents(target=t, source=s, env=env)
+ assert c == "s4 s5", c
+
+class CommandGeneratorActionTestCase(unittest.TestCase):
+
+ def factory(self, act, **kw):
+ """Pass any keywords as a dict"""
+ return SCons.Action.CommandGeneratorAction(act, kw)
+
+ def test___init__(self):
+ """Test creation of a command generator Action
+ """
+ def f(target, source, env):
+ pass
+ a = self.factory(f)
+ assert a.generator == f
+
+ def test___str__(self):
+ """Test the pre-substitution strings for command generator Actions
+ """
+ def f(target, source, env, for_signature, self=self):
+
+ # See if "env" is really a construction environment (or
+ # looks like one) by accessing the FindIxes attribute.
+ # (The Tool/mingw.py module has a generator that uses this,
+ # and the __str__() method used to cause problems by passing
+ # us a regular dictionary as a fallback.)
+
+ env.FindIxes
+ return "FOO"
+ a = self.factory(f)
+ s = str(a)
+ assert s == 'FOO', s
+
+ def test_genstring(self):
+ """Test the command generator Action genstring() method
+ """
+ def f(target, source, env, for_signature, self=self):
+ dummy = env['dummy']
+ self.dummy = dummy
+ return "$FOO $TARGET $SOURCE $TARGETS $SOURCES"
+ a = self.factory(f)
+ self.dummy = 0
+ s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1))
+ assert self.dummy == 1, self.dummy
+ assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s
+
+ def test_execute(self):
+ """Test executing a command generator Action
+ """
+
+ def f(target, source, env, for_signature, self=self):
+ dummy = env['dummy']
+ self.dummy = dummy
+ s = env.subst("$FOO")
+ assert s == 'foo baz\nbar ack', s
+ return "$FOO"
+ def func_action(target, source, env, self=self):
+ dummy=env['dummy']
+ s = env.subst('$foo')
+ assert s == 'bar', s
+ self.dummy=dummy
+ def f2(target, source, env, for_signature, f=func_action):
+ return f
+ def ch(sh, escape, cmd, args, env, self=self):
+ self.cmd.append(cmd)
+ self.args.append(args)
+
+ a = self.factory(f)
+ self.dummy = 0
+ self.cmd = []
+ self.args = []
+ a([], [], env=Environment(FOO = 'foo baz\nbar ack',
+ dummy = 1,
+ SPAWN = ch))
+ assert self.dummy == 1, self.dummy
+ assert self.cmd == ['foo', 'bar'], self.cmd
+ assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
+
+ b = self.factory(f2)
+ self.dummy = 0
+ b(target=[], source=[], env=Environment(foo = 'bar',
+ dummy = 2 ))
+ assert self.dummy==2, self.dummy
+ del self.dummy
+
+ class DummyFile:
+ def __init__(self, t):
+ self.t = t
+ def rfile(self):
+ self.t.rfile_called = 1
+ return self
+ def get_subst_proxy(self):
+ return self
+ def f3(target, source, env, for_signature):
+ return ''
+ c = self.factory(f3)
+ c(target=[], source=DummyFile(self), env=Environment())
+ assert self.rfile_called
+
+ def test_get_contents(self):
+ """Test fetching the contents of a command generator Action
+ """
+ def f(target, source, env, for_signature):
+ foo = env['foo']
+ bar = env['bar']
+ assert for_signature, for_signature
+ return [["guux", foo, "$(", "$ignore", "$)", bar,
+ '${test("$( foo $bar $)")}' ]]
+
+ def test(mystr):
+ assert mystr == "$( foo $bar $)", mystr
+ return "test"
+
+ env = Environment(foo = 'FFF', bar = 'BBB',
+ ignore = 'foo', test=test)
+ a = self.factory(f)
+ c = a.get_contents(target=[], source=[], env=env)
+ assert c == "guux FFF BBB test", c
+
+
+class FunctionActionTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test creation of a function Action
+ """
+ def func1():
+ pass
+ def func2():
+ pass
+ def func3():
+ pass
+ def func4():
+ pass
+
+ a = SCons.Action.FunctionAction(func1, {})
+ assert a.execfunction == func1, a.execfunction
+ assert isinstance(a.strfunction, types.MethodType), type(a.strfunction)
+
+ a = SCons.Action.FunctionAction(func2, { 'strfunction' : func3 })
+ assert a.execfunction == func2, a.execfunction
+ assert a.strfunction == func3, a.strfunction
+
+ def test___str__(self):
+ """Test the __str__() method for function Actions
+ """
+ def func1():
+ pass
+ a = SCons.Action.FunctionAction(func1, {})
+ s = str(a)
+ assert s == "func1(target, source, env)", s
+
+ class class1:
+ def __call__(self):
+ pass
+ a = SCons.Action.FunctionAction(class1(), {})
+ s = str(a)
+ assert s == "class1(target, source, env)", s
+
+ def test_execute(self):
+ """Test executing a function Action
+ """
+ self.inc = 0
+ def f(target, source, env):
+ s = env['s']
+ s.inc = s.inc + 1
+ s.target = target
+ s.source=source
+ assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
+ return 0
+ a = SCons.Action.FunctionAction(f, {})
+ a(target=1, source=2, env=Environment(BAR = 'foo bar',
+ s = self))
+ assert self.inc == 1, self.inc
+ assert self.source == [2], self.source
+ assert self.target == [1], self.target
+
+ global count
+ count = 0
+ def function1(target, source, env):
+ global count
+ count = count + 1
+ for t in target:
+ open(t, 'w').write("function1\n")
+ return 1
+
+ act = SCons.Action.FunctionAction(function1, {})
+ r = act(target = [outfile, outfile2], source=[], env=Environment())
+ assert r.status == 1, r.status
+
+ assert count == 1, count
+ c = test.read(outfile, 'r')
+ assert c == "function1\n", c
+ c = test.read(outfile2, 'r')
+ assert c == "function1\n", c
+
+ class class1a:
+ def __init__(self, target, source, env):
+ open(env['out'], 'w').write("class1a\n")
+
+ act = SCons.Action.FunctionAction(class1a, {})
+ r = act([], [], Environment(out = outfile))
+ assert isinstance(r.status, class1a), r.status
+ c = test.read(outfile, 'r')
+ assert c == "class1a\n", c
+
+ class class1b:
+ def __call__(self, target, source, env):
+ open(env['out'], 'w').write("class1b\n")
+ return 2
+
+ act = SCons.Action.FunctionAction(class1b(), {})
+ r = act([], [], Environment(out = outfile))
+ assert r.status == 2, r.status
+ c = test.read(outfile, 'r')
+ assert c == "class1b\n", c
+
+ def build_it(target, source, env, executor=None, self=self):
+ self.build_it = 1
+ return 0
+ def string_it(target, source, env, executor=None, self=self):
+ self.string_it = 1
+ return None
+ act = SCons.Action.FunctionAction(build_it,
+ { 'strfunction' : string_it })
+ r = act([], [], Environment())
+ assert r == 0, r
+ assert self.build_it
+ assert self.string_it
+
+ def test_get_contents(self):
+ """Test fetching the contents of a function Action
+ """
+
+ def LocalFunc():
+ pass
+
+ func_matches = [
+ "0,0,0,0,(),(),(d\000\000S),(),()",
+ "0,0,0,0,(),(),(d\x00\x00S),(),()",
+ ]
+
+ meth_matches = [
+ "1,1,0,0,(),(),(d\000\000S),(),()",
+ "1,1,0,0,(),(),(d\x00\x00S),(),()",
+ ]
+
+ def factory(act, **kw):
+ return SCons.Action.FunctionAction(act, kw)
+
+ a = factory(GlobalFunc)
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c in func_matches, repr(c)
+
+ a = factory(LocalFunc)
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c in func_matches, repr(c)
+
+ matches_foo = map(lambda x: x + "foo", func_matches)
+
+ a = factory(GlobalFunc, varlist=['XYZ'])
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c in func_matches, repr(c)
+ c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
+ assert c in matches_foo, repr(c)
+
+ ##TODO: is this set of tests still needed?
+ # Make sure a bare string varlist works
+ a = factory(GlobalFunc, varlist='XYZ')
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c in func_matches, repr(c)
+ c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
+ assert c in matches_foo, repr(c)
+
+ class Foo:
+ def get_contents(self, target, source, env):
+ return 'xyzzy'
+ a = factory(Foo())
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c == 'xyzzy', repr(c)
+
+ class LocalClass:
+ def LocalMethod(self):
+ pass
+ lc = LocalClass()
+ a = factory(lc.LocalMethod)
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c in meth_matches, repr(c)
+
+ def test_strfunction(self):
+ """Test the FunctionAction.strfunction() method
+ """
+ def func():
+ pass
+
+ def factory(act, **kw):
+ return SCons.Action.FunctionAction(act, kw)
+
+ a = factory(func)
+ s = a.strfunction(target=[], source=[], env=Environment())
+ assert s == 'func([], [])', s
+
+ a = factory(func, strfunction=None)
+ s = a.strfunction(target=[], source=[], env=Environment())
+ assert s is None, s
+
+ a = factory(func, cmdstr='function')
+ s = a.strfunction(target=[], source=[], env=Environment())
+ assert s == 'function', s
+
+class ListActionTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test creation of a list of subsidiary Actions
+ """
+ def func():
+ pass
+ a = SCons.Action.ListAction(["x", func, ["y", "z"]])
+ assert isinstance(a.list[0], SCons.Action.CommandAction)
+ assert isinstance(a.list[1], SCons.Action.FunctionAction)
+ assert isinstance(a.list[2], SCons.Action.ListAction)
+ assert a.list[2].list[0].cmd_list == 'y'
+
+ def test___str__(self):
+ """Test the __str__() method for a list of subsidiary Actions
+ """
+ def f(target,source,env):
+ pass
+ def g(target,source,env):
+ pass
+ a = SCons.Action.ListAction([f, g, "XXX", f])
+ s = str(a)
+ assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s
+
+ def test_genstring(self):
+ """Test the genstring() method for a list of subsidiary Actions
+ """
+ def f(target,source,env):
+ pass
+ def g(target,source,env,for_signature):
+ return 'generated %s %s' % (target[0], source[0])
+ g = SCons.Action.Action(g, generator=1)
+ a = SCons.Action.ListAction([f, g, "XXX", f])
+ s = a.genstring(['foo.x'], ['bar.y'], Environment())
+ assert s == "f(target, source, env)\ngenerated foo.x bar.y\nXXX\nf(target, source, env)", s
+
+ def test_execute(self):
+ """Test executing a list of subsidiary Actions
+ """
+ self.inc = 0
+ def f(target,source,env):
+ s = env['s']
+ s.inc = s.inc + 1
+ a = SCons.Action.ListAction([f, f, f])
+ a([], [], Environment(s = self))
+ assert self.inc == 3, self.inc
+
+ cmd2 = r'%s %s %s syzygy' % (_python_, act_py, outfile)
+
+ def function2(target, source, env):
+ open(env['out'], 'a').write("function2\n")
+ return 0
+
+ class class2a:
+ def __call__(self, target, source, env):
+ open(env['out'], 'a').write("class2a\n")
+ return 0
+
+ class class2b:
+ def __init__(self, target, source, env):
+ open(env['out'], 'a').write("class2b\n")
+ act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b])
+ r = act([], [], Environment(out = outfile))
+ assert isinstance(r.status, class2b), r.status
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
+
+ def test_get_contents(self):
+ """Test fetching the contents of a list of subsidiary Actions
+ """
+ self.foo=0
+ def gen(target, source, env, for_signature):
+ s = env['s']
+ s.foo=1
+ return "y"
+ a = SCons.Action.ListAction(["x",
+ SCons.Action.Action(gen, generator=1),
+ "z"])
+ c = a.get_contents(target=[], source=[], env=Environment(s = self))
+ assert self.foo==1, self.foo
+ assert c == "xyz", c
+
+class LazyActionTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test creation of a lazy-evaluation Action
+ """
+ # Environment variable references should create a special type
+ # of LazyAction that lazily evaluates the variable for whether
+ # it's a string or something else before doing anything.
+ a9 = SCons.Action.Action('$FOO')
+ assert isinstance(a9, SCons.Action.LazyAction), a9
+ assert a9.var == 'FOO', a9.var
+
+ a10 = SCons.Action.Action('${FOO}')
+ assert isinstance(a10, SCons.Action.LazyAction), a10
+ assert a10.var == 'FOO', a10.var
+
+ def test_genstring(self):
+ """Test the lazy-evaluation Action genstring() method
+ """
+ def f(target, source, env):
+ pass
+ a = SCons.Action.Action('$BAR')
+ env1 = Environment(BAR=f, s=self)
+ env2 = Environment(BAR='xxx', s=self)
+ s = a.genstring([], [], env=env1)
+ assert s == "f(target, source, env)", s
+ s = a.genstring([], [], env=env2)
+ assert s == 'xxx', s
+
+ def test_execute(self):
+ """Test executing a lazy-evaluation Action
+ """
+ def f(target, source, env):
+ s = env['s']
+ s.test=1
+ return 0
+ a = SCons.Action.Action('$BAR')
+ a([], [], env=Environment(BAR = f, s = self))
+ assert self.test == 1, self.test
+ cmd = r'%s %s %s lazy' % (_python_, act_py, outfile)
+ a([], [], env=Environment(BAR = cmd, s = self))
+ c = test.read(outfile, 'r')
+ assert c == "act.py: 'lazy'\n", c
+
+ def test_get_contents(self):
+ """Test fetching the contents of a lazy-evaluation Action
+ """
+ a = SCons.Action.Action("${FOO}")
+ env = Environment(FOO = [["This", "is", "a", "test"]])
+ c = a.get_contents(target=[], source=[], env=env)
+ assert c == "This is a test", c
+
+class ActionCallerTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test creation of an ActionCaller"""
+ ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO' : 4, 'BAR' : 5})
+ assert ac.parent == 1, ac.parent
+ assert ac.args == [2, 3], ac.args
+ assert ac.kw == {'FOO' : 4, 'BAR' : 5}, ac.kw
+
+ def test_get_contents(self):
+ """Test fetching the contents of an ActionCaller"""
+ def strfunc():
+ pass
+
+ def LocalFunc():
+ pass
+
+ matches = [
+ "d\000\000S",
+ "d\x00\x00S"
+ ]
+
+ af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c in matches, repr(c)
+
+ af = SCons.Action.ActionFactory(LocalFunc, strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c in matches, repr(c)
+
+ matches = [
+ 'd\000\000S',
+ "d\x00\x00S"
+ ]
+
+ class LocalActFunc:
+ def __call__(self):
+ pass
+
+ af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c in matches, repr(c)
+
+ af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c in matches, repr(c)
+
+ matches = [
+ "<built-in function str>",
+ "<type 'str'>",
+ ]
+
+ af = SCons.Action.ActionFactory(str, strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c == "<built-in function str>" or \
+ c == "<type 'str'>", repr(c)
+
+ def test___call__(self):
+ """Test calling an ActionCaller"""
+ actfunc_args = []
+ def actfunc(a1, a2, a3, args=actfunc_args):
+ args.extend([a1, a2, a3])
+ def strfunc(a1, a2, a3):
+ pass
+
+ e = Environment(FOO = 2, BAR = 5)
+
+ af = SCons.Action.ActionFactory(actfunc, strfunc)
+ ac = SCons.Action.ActionCaller(af, ['$__env__', '$FOO', 3], {})
+ ac([], [], e)
+ assert actfunc_args[0] is e, actfunc_args
+ assert actfunc_args[1] == '2', actfunc_args
+ assert actfunc_args[2] == 3, actfunc_args
+ del actfunc_args[:]
+
+ ac = SCons.Action.ActionCaller(af, [], {'a3' : '$__env__', 'a2' : '$BAR', 'a1' : 4})
+ ac([], [], e)
+ assert actfunc_args[0] == 4, actfunc_args
+ assert actfunc_args[1] == '5', actfunc_args
+ assert actfunc_args[2] is e, actfunc_args
+ del actfunc_args[:]
+
+ def test_strfunction(self):
+ """Test calling the ActionCaller strfunction() method"""
+ strfunc_args = []
+ def actfunc(a1, a2, a3, a4):
+ pass
+ def strfunc(a1, a2, a3, a4, args=strfunc_args):
+ args.extend([a1, a2, a3, a4])
+
+ af = SCons.Action.ActionFactory(actfunc, strfunc)
+ ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3, '$WS'], {})
+ ac.strfunction([], [], Environment(FOO = 2, WS='white space'))
+ assert strfunc_args == [1, '2', 3, 'white space'], strfunc_args
+
+ del strfunc_args[:]
+ d = {'a3' : 6, 'a2' : '$BAR', 'a1' : 4, 'a4' : '$WS'}
+ ac = SCons.Action.ActionCaller(af, [], d)
+ ac.strfunction([], [], Environment(BAR = 5, WS='w s'))
+ assert strfunc_args == [4, '5', 6, 'w s'], strfunc_args
+
+class ActionFactoryTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test creation of an ActionFactory"""
+ def actfunc():
+ pass
+ def strfunc():
+ pass
+ ac = SCons.Action.ActionFactory(actfunc, strfunc)
+ assert ac.actfunc is actfunc, ac.actfunc
+ assert ac.strfunc is strfunc, ac.strfunc
+
+ def test___call__(self):
+ """Test calling whatever's returned from an ActionFactory"""
+ actfunc_args = []
+ strfunc_args = []
+ def actfunc(a1, a2, a3, args=actfunc_args):
+ args.extend([a1, a2, a3])
+ def strfunc(a1, a2, a3, args=strfunc_args):
+ args.extend([a1, a2, a3])
+ af = SCons.Action.ActionFactory(actfunc, strfunc)
+ af(3, 6, 9)([], [], Environment())
+ assert actfunc_args == [3, 6, 9], actfunc_args
+ assert strfunc_args == [3, 6, 9], strfunc_args
+
+
+class ActionCompareTestCase(unittest.TestCase):
+
+ def test_1_solo_name(self):
+ """Test Lazy Cmd Generator Action get_name alone.
+
+ Basically ensures we can locate the builder, comparing it to
+ itself along the way."""
+ bar = SCons.Builder.Builder(action = {})
+ env = Environment( BUILDERS = {'BAR' : bar} )
+ name = bar.get_name(env)
+ assert name == 'BAR', name
+
+ def test_2_multi_name(self):
+ """Test LazyCmdGenerator Action get_name multi builders.
+
+ Ensure that we can compare builders (and thereby actions) to
+ each other safely."""
+ foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo')
+ bar = SCons.Builder.Builder(action = {})
+ assert foo != bar
+ assert foo.action != bar.action
+ env = Environment( BUILDERS = {'FOO' : foo,
+ 'BAR' : bar} )
+ name = foo.get_name(env)
+ assert name == 'FOO', name
+ name = bar.get_name(env)
+ assert name == 'BAR', name
+
+ def test_3_dict_names(self):
+ """Test Action/Suffix dicts with get_name.
+
+ Verifies that Action/Suffix dictionaries work correctly,
+ especially two builders that can generate the same suffix,
+ where one of the builders has a suffix dictionary with a None
+ key."""
+
+ foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo')
+ bar = SCons.Builder.Builder(action = {}, suffix={None:'.bar'})
+ bar.add_action('.cow', "$MOO")
+ dog = SCons.Builder.Builder(suffix = '.bar')
+
+ env = Environment( BUILDERS = {'FOO' : foo,
+ 'BAR' : bar,
+ 'DOG' : dog} )
+
+ assert foo.get_name(env) == 'FOO', foo.get_name(env)
+ assert bar.get_name(env) == 'BAR', bar.get_name(env)
+ assert dog.get_name(env) == 'DOG', dog.get_name(env)
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ _ActionActionTestCase,
+ ActionTestCase,
+ CommandActionTestCase,
+ CommandGeneratorActionTestCase,
+ FunctionActionTestCase,
+ ListActionTestCase,
+ LazyActionTestCase,
+ ActionCallerTestCase,
+ ActionFactoryTestCase,
+ ActionCompareTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
new file mode 100644
index 0000000..0d7e065
--- /dev/null
+++ b/src/engine/SCons/Builder.py
@@ -0,0 +1,868 @@
+"""SCons.Builder
+
+Builder object subsystem.
+
+A Builder object is a callable that encapsulates information about how
+to execute actions to create a target Node (file) from source Nodes
+(files), and how to create those dependencies for tracking.
+
+The main entry point here is the Builder() factory method. This provides
+a procedural interface that creates the right underlying Builder object
+based on the keyword arguments supplied and the types of the arguments.
+
+The goal is for this external interface to be simple enough that the
+vast majority of users can create new Builders as necessary to support
+building new types of files in their configurations, without having to
+dive any deeper into this subsystem.
+
+The base class here is BuilderBase. This is a concrete base class which
+does, in fact, represent the Builder objects that we (or users) create.
+
+There is also a proxy that looks like a Builder:
+
+ CompositeBuilder
+
+ This proxies for a Builder with an action that is actually a
+ dictionary that knows how to map file suffixes to a specific
+ action. This is so that we can invoke different actions
+ (compilers, compile options) for different flavors of source
+ files.
+
+Builders and their proxies have the following public interface methods
+used by other modules:
+
+ __call__()
+ THE public interface. Calling a Builder object (with the
+ use of internal helper methods) sets up the target and source
+ dependencies, appropriate mapping to a specific action, and the
+ environment manipulation necessary for overridden construction
+ variable. This also takes care of warning about possible mistakes
+ in keyword arguments.
+
+ add_emitter()
+ Adds an emitter for a specific file suffix, used by some Tool
+ modules to specify that (for example) a yacc invocation on a .y
+ can create a .h *and* a .c file.
+
+ add_action()
+ Adds an action for a specific file suffix, heavily used by
+ Tool modules to add their specific action(s) for turning
+ a source file into an object file to the global static
+ and shared object file Builders.
+
+There are the following methods for internal use within this module:
+
+ _execute()
+ The internal method that handles the heavily lifting when a
+ Builder is called. This is used so that the __call__() methods
+ can set up warning about possible mistakes in keyword-argument
+ overrides, and *then* execute all of the steps necessary so that
+ the warnings only occur once.
+
+ get_name()
+ Returns the Builder's name within a specific Environment,
+ primarily used to try to return helpful information in error
+ messages.
+
+ adjust_suffix()
+ get_prefix()
+ get_suffix()
+ get_src_suffix()
+ set_src_suffix()
+ Miscellaneous stuff for handling the prefix and suffix
+ manipulation we use in turning source file names into target
+ file names.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Builder.py 4577 2009/12/27 19:44:43 scons"
+
+import UserDict
+import UserList
+
+import SCons.Action
+from SCons.Debug import logInstanceCreation
+from SCons.Errors import InternalError, UserError
+import SCons.Executor
+import SCons.Memoize
+import SCons.Node
+import SCons.Node.FS
+import SCons.Util
+import SCons.Warnings
+
+class _Null:
+ pass
+
+_null = _Null
+
+def match_splitext(path, suffixes = []):
+ if suffixes:
+ matchsuf = filter(lambda S,path=path: path[-len(S):] == S,
+ suffixes)
+ if matchsuf:
+ suf = max(map(None, map(len, matchsuf), matchsuf))[1]
+ return [path[:-len(suf)], path[-len(suf):]]
+ return SCons.Util.splitext(path)
+
+class DictCmdGenerator(SCons.Util.Selector):
+ """This is a callable class that can be used as a
+ command generator function. It holds on to a dictionary
+ mapping file suffixes to Actions. It uses that dictionary
+ to return the proper action based on the file suffix of
+ the source file."""
+
+ def __init__(self, dict=None, source_ext_match=1):
+ SCons.Util.Selector.__init__(self, dict)
+ self.source_ext_match = source_ext_match
+
+ def src_suffixes(self):
+ return self.keys()
+
+ def add_action(self, suffix, action):
+ """Add a suffix-action pair to the mapping.
+ """
+ self[suffix] = action
+
+ def __call__(self, target, source, env, for_signature):
+ if not source:
+ return []
+
+ if self.source_ext_match:
+ suffixes = self.src_suffixes()
+ ext = None
+ for src in map(str, source):
+ my_ext = match_splitext(src, suffixes)[1]
+ if ext and my_ext != ext:
+ raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
+ ext = my_ext
+ else:
+ ext = match_splitext(str(source[0]), self.src_suffixes())[1]
+
+ if not ext:
+ #return ext
+ raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
+
+ try:
+ ret = SCons.Util.Selector.__call__(self, env, source, ext)
+ except KeyError, e:
+ raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
+ if ret is None:
+ raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \
+ (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys())))
+ return ret
+
+class CallableSelector(SCons.Util.Selector):
+ """A callable dictionary that will, in turn, call the value it
+ finds if it can."""
+ def __call__(self, env, source):
+ value = SCons.Util.Selector.__call__(self, env, source)
+ if callable(value):
+ value = value(env, source)
+ return value
+
+class DictEmitter(SCons.Util.Selector):
+ """A callable dictionary that maps file suffixes to emitters.
+ When called, it finds the right emitter in its dictionary for the
+ suffix of the first source file, and calls that emitter to get the
+ right lists of targets and sources to return. If there's no emitter
+ for the suffix in its dictionary, the original target and source are
+ returned.
+ """
+ def __call__(self, target, source, env):
+ emitter = SCons.Util.Selector.__call__(self, env, source)
+ if emitter:
+ target, source = emitter(target, source, env)
+ return (target, source)
+
+class ListEmitter(UserList.UserList):
+ """A callable list of emitters that calls each in sequence,
+ returning the result.
+ """
+ def __call__(self, target, source, env):
+ for e in self.data:
+ target, source = e(target, source, env)
+ return (target, source)
+
+# These are a common errors when calling a Builder;
+# they are similar to the 'target' and 'source' keyword args to builders,
+# so we issue warnings when we see them. The warnings can, of course,
+# be disabled.
+misleading_keywords = {
+ 'targets' : 'target',
+ 'sources' : 'source',
+}
+
+class OverrideWarner(UserDict.UserDict):
+ """A class for warning about keyword arguments that we use as
+ overrides in a Builder call.
+
+ This class exists to handle the fact that a single Builder call
+ can actually invoke multiple builders. This class only emits the
+ warnings once, no matter how many Builders are invoked.
+ """
+ def __init__(self, dict):
+ UserDict.UserDict.__init__(self, dict)
+ if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
+ self.already_warned = None
+ def warn(self):
+ if self.already_warned:
+ return
+ for k in self.keys():
+ if misleading_keywords.has_key(k):
+ alt = misleading_keywords[k]
+ msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
+ SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
+ self.already_warned = 1
+
+def Builder(**kw):
+ """A factory for builder objects."""
+ composite = None
+ if kw.has_key('generator'):
+ if kw.has_key('action'):
+ raise UserError, "You must not specify both an action and a generator."
+ kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
+ del kw['generator']
+ elif kw.has_key('action'):
+ source_ext_match = kw.get('source_ext_match', 1)
+ if kw.has_key('source_ext_match'):
+ del kw['source_ext_match']
+ if SCons.Util.is_Dict(kw['action']):
+ composite = DictCmdGenerator(kw['action'], source_ext_match)
+ kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
+ kw['src_suffix'] = composite.src_suffixes()
+ else:
+ kw['action'] = SCons.Action.Action(kw['action'])
+
+ if kw.has_key('emitter'):
+ emitter = kw['emitter']
+ if SCons.Util.is_String(emitter):
+ # This allows users to pass in an Environment
+ # variable reference (like "$FOO") as an emitter.
+ # We will look in that Environment variable for
+ # a callable to use as the actual emitter.
+ var = SCons.Util.get_environment_var(emitter)
+ if not var:
+ raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
+ kw['emitter'] = EmitterProxy(var)
+ elif SCons.Util.is_Dict(emitter):
+ kw['emitter'] = DictEmitter(emitter)
+ elif SCons.Util.is_List(emitter):
+ kw['emitter'] = ListEmitter(emitter)
+
+ result = apply(BuilderBase, (), kw)
+
+ if not composite is None:
+ result = CompositeBuilder(result, composite)
+
+ return result
+
+def _node_errors(builder, env, tlist, slist):
+ """Validate that the lists of target and source nodes are
+ legal for this builder and environment. Raise errors or
+ issue warnings as appropriate.
+ """
+
+ # First, figure out if there are any errors in the way the targets
+ # were specified.
+ for t in tlist:
+ if t.side_effect:
+ raise UserError, "Multiple ways to build the same target were specified for: %s" % t
+ if t.has_explicit_builder():
+ if not t.env is None and not t.env is env:
+ action = t.builder.action
+ t_contents = action.get_contents(tlist, slist, t.env)
+ contents = action.get_contents(tlist, slist, env)
+
+ if t_contents == contents:
+ msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env))
+ SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
+ else:
+ msg = "Two environments with different actions were specified for the same target: %s" % t
+ raise UserError, msg
+ if builder.multi:
+ if t.builder != builder:
+ msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
+ raise UserError, msg
+ # TODO(batch): list constructed each time!
+ if t.get_executor().get_all_targets() != tlist:
+ msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, map(str, t.get_executor().get_all_targets()), map(str, tlist))
+ raise UserError, msg
+ elif t.sources != slist:
+ msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist))
+ raise UserError, msg
+
+ if builder.single_source:
+ if len(slist) > 1:
+ raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
+
+class EmitterProxy:
+ """This is a callable class that can act as a
+ Builder emitter. It holds on to a string that
+ is a key into an Environment dictionary, and will
+ look there at actual build time to see if it holds
+ a callable. If so, we will call that as the actual
+ emitter."""
+ def __init__(self, var):
+ self.var = SCons.Util.to_String(var)
+
+ def __call__(self, target, source, env):
+ emitter = self.var
+
+ # Recursively substitute the variable.
+ # We can't use env.subst() because it deals only
+ # in strings. Maybe we should change that?
+ while SCons.Util.is_String(emitter) and env.has_key(emitter):
+ emitter = env[emitter]
+ if callable(emitter):
+ target, source = emitter(target, source, env)
+ elif SCons.Util.is_List(emitter):
+ for e in emitter:
+ target, source = e(target, source, env)
+
+ return (target, source)
+
+
+ def __cmp__(self, other):
+ return cmp(self.var, other.var)
+
+class BuilderBase:
+ """Base class for Builders, objects that create output
+ nodes (files) from input nodes (files).
+ """
+
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ def __init__(self, action = None,
+ prefix = '',
+ suffix = '',
+ src_suffix = '',
+ target_factory = None,
+ source_factory = None,
+ target_scanner = None,
+ source_scanner = None,
+ emitter = None,
+ multi = 0,
+ env = None,
+ single_source = 0,
+ name = None,
+ chdir = _null,
+ is_explicit = 1,
+ src_builder = None,
+ ensure_suffix = False,
+ **overrides):
+ if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
+ self._memo = {}
+ self.action = action
+ self.multi = multi
+ if SCons.Util.is_Dict(prefix):
+ prefix = CallableSelector(prefix)
+ self.prefix = prefix
+ if SCons.Util.is_Dict(suffix):
+ suffix = CallableSelector(suffix)
+ self.env = env
+ self.single_source = single_source
+ if overrides.has_key('overrides'):
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
+ "\tspecify the items as keyword arguments to the Builder() call instead.")
+ overrides.update(overrides['overrides'])
+ del overrides['overrides']
+ if overrides.has_key('scanner'):
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
+ "\tuse: source_scanner or target_scanner as appropriate.")
+ del overrides['scanner']
+ self.overrides = overrides
+
+ self.set_suffix(suffix)
+ self.set_src_suffix(src_suffix)
+ self.ensure_suffix = ensure_suffix
+
+ self.target_factory = target_factory
+ self.source_factory = source_factory
+ self.target_scanner = target_scanner
+ self.source_scanner = source_scanner
+
+ self.emitter = emitter
+
+ # Optional Builder name should only be used for Builders
+ # that don't get attached to construction environments.
+ if name:
+ self.name = name
+ self.executor_kw = {}
+ if not chdir is _null:
+ self.executor_kw['chdir'] = chdir
+ self.is_explicit = is_explicit
+
+ if src_builder is None:
+ src_builder = []
+ elif not SCons.Util.is_List(src_builder):
+ src_builder = [ src_builder ]
+ self.src_builder = src_builder
+
+ def __nonzero__(self):
+ raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
+
+ def get_name(self, env):
+ """Attempts to get the name of the Builder.
+
+ Look at the BUILDERS variable of env, expecting it to be a
+ dictionary containing this Builder, and return the key of the
+ dictionary. If there's no key, then return a directly-configured
+ name (if there is one) or the name of the class (by default)."""
+
+ try:
+ index = env['BUILDERS'].values().index(self)
+ return env['BUILDERS'].keys()[index]
+ except (AttributeError, KeyError, TypeError, ValueError):
+ try:
+ return self.name
+ except AttributeError:
+ return str(self.__class__)
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+
+ def splitext(self, path, env=None):
+ if not env:
+ env = self.env
+ if env:
+ suffixes = self.src_suffixes(env)
+ else:
+ suffixes = []
+ return match_splitext(path, suffixes)
+
+ def _adjustixes(self, files, pre, suf, ensure_suffix=False):
+ if not files:
+ return []
+ result = []
+ if not SCons.Util.is_List(files):
+ files = [files]
+
+ for f in files:
+ if SCons.Util.is_String(f):
+ f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
+ result.append(f)
+ return result
+
+ def _create_nodes(self, env, target = None, source = None):
+ """Create and return lists of target and source nodes.
+ """
+ src_suf = self.get_src_suffix(env)
+
+ target_factory = env.get_factory(self.target_factory)
+ source_factory = env.get_factory(self.source_factory)
+
+ source = self._adjustixes(source, None, src_suf)
+ slist = env.arg2nodes(source, source_factory)
+
+ pre = self.get_prefix(env, slist)
+ suf = self.get_suffix(env, slist)
+
+ if target is None:
+ try:
+ t_from_s = slist[0].target_from_source
+ except AttributeError:
+ raise UserError("Do not know how to create a target from source `%s'" % slist[0])
+ except IndexError:
+ tlist = []
+ else:
+ splitext = lambda S,self=self,env=env: self.splitext(S,env)
+ tlist = [ t_from_s(pre, suf, splitext) ]
+ else:
+ target = self._adjustixes(target, pre, suf, self.ensure_suffix)
+ tlist = env.arg2nodes(target, target_factory, target=target, source=source)
+
+ if self.emitter:
+ # The emitter is going to do str(node), but because we're
+ # being called *from* a builder invocation, the new targets
+ # don't yet have a builder set on them and will look like
+ # source files. Fool the emitter's str() calls by setting
+ # up a temporary builder on the new targets.
+ new_targets = []
+ for t in tlist:
+ if not t.is_derived():
+ t.builder_set(self)
+ new_targets.append(t)
+
+ orig_tlist = tlist[:]
+ orig_slist = slist[:]
+
+ target, source = self.emitter(target=tlist, source=slist, env=env)
+
+ # Now delete the temporary builders that we attached to any
+ # new targets, so that _node_errors() doesn't do weird stuff
+ # to them because it thinks they already have builders.
+ for t in new_targets:
+ if t.builder is self:
+ # Only delete the temporary builder if the emitter
+ # didn't change it on us.
+ t.builder_set(None)
+
+ # Have to call arg2nodes yet again, since it is legal for
+ # emitters to spit out strings as well as Node instances.
+ tlist = env.arg2nodes(target, target_factory,
+ target=orig_tlist, source=orig_slist)
+ slist = env.arg2nodes(source, source_factory,
+ target=orig_tlist, source=orig_slist)
+
+ return tlist, slist
+
+ def _execute(self, env, target, source, overwarn={}, executor_kw={}):
+ # We now assume that target and source are lists or None.
+ if self.src_builder:
+ source = self.src_builder_sources(env, source, overwarn)
+
+ if self.single_source and len(source) > 1 and target is None:
+ result = []
+ if target is None: target = [None]*len(source)
+ for tgt, src in zip(target, source):
+ if not tgt is None: tgt = [tgt]
+ if not src is None: src = [src]
+ result.extend(self._execute(env, tgt, src, overwarn))
+ return SCons.Node.NodeList(result)
+
+ overwarn.warn()
+
+ tlist, slist = self._create_nodes(env, target, source)
+
+ # Check for errors with the specified target/source lists.
+ _node_errors(self, env, tlist, slist)
+
+ # The targets are fine, so find or make the appropriate Executor to
+ # build this particular list of targets from this particular list of
+ # sources.
+
+ executor = None
+ key = None
+
+ if self.multi:
+ try:
+ executor = tlist[0].get_executor(create = 0)
+ except (AttributeError, IndexError):
+ pass
+ else:
+ executor.add_sources(slist)
+
+ if executor is None:
+ if not self.action:
+ fmt = "Builder %s must have an action to build %s."
+ raise UserError, fmt % (self.get_name(env or self.env),
+ map(str,tlist))
+ key = self.action.batch_key(env or self.env, tlist, slist)
+ if key:
+ try:
+ executor = SCons.Executor.GetBatchExecutor(key)
+ except KeyError:
+ pass
+ else:
+ executor.add_batch(tlist, slist)
+
+ if executor is None:
+ executor = SCons.Executor.Executor(self.action, env, [],
+ tlist, slist, executor_kw)
+ if key:
+ SCons.Executor.AddBatchExecutor(key, executor)
+
+ # Now set up the relevant information in the target Nodes themselves.
+ for t in tlist:
+ t.cwd = env.fs.getcwd()
+ t.builder_set(self)
+ t.env_set(env)
+ t.add_source(slist)
+ t.set_executor(executor)
+ t.set_explicit(self.is_explicit)
+
+ return SCons.Node.NodeList(tlist)
+
+ def __call__(self, env, target=None, source=None, chdir=_null, **kw):
+ # We now assume that target and source are lists or None.
+ # The caller (typically Environment.BuilderWrapper) is
+ # responsible for converting any scalar values to lists.
+ if chdir is _null:
+ ekw = self.executor_kw
+ else:
+ ekw = self.executor_kw.copy()
+ ekw['chdir'] = chdir
+ if kw:
+ if kw.has_key('srcdir'):
+ def prependDirIfRelative(f, srcdir=kw['srcdir']):
+ import os.path
+ if SCons.Util.is_String(f) and not os.path.isabs(f):
+ f = os.path.join(srcdir, f)
+ return f
+ if not SCons.Util.is_List(source):
+ source = [source]
+ source = map(prependDirIfRelative, source)
+ del kw['srcdir']
+ if self.overrides:
+ env_kw = self.overrides.copy()
+ env_kw.update(kw)
+ else:
+ env_kw = kw
+ else:
+ env_kw = self.overrides
+ env = env.Override(env_kw)
+ return self._execute(env, target, source, OverrideWarner(kw), ekw)
+
+ def adjust_suffix(self, suff):
+ if suff and not suff[0] in [ '.', '_', '$' ]:
+ return '.' + suff
+ return suff
+
+ def get_prefix(self, env, sources=[]):
+ prefix = self.prefix
+ if callable(prefix):
+ prefix = prefix(env, sources)
+ return env.subst(prefix)
+
+ def set_suffix(self, suffix):
+ if not callable(suffix):
+ suffix = self.adjust_suffix(suffix)
+ self.suffix = suffix
+
+ def get_suffix(self, env, sources=[]):
+ suffix = self.suffix
+ if callable(suffix):
+ suffix = suffix(env, sources)
+ return env.subst(suffix)
+
+ def set_src_suffix(self, src_suffix):
+ if not src_suffix:
+ src_suffix = []
+ elif not SCons.Util.is_List(src_suffix):
+ src_suffix = [ src_suffix ]
+ adjust = lambda suf, s=self: \
+ callable(suf) and suf or s.adjust_suffix(suf)
+ self.src_suffix = map(adjust, src_suffix)
+
+ def get_src_suffix(self, env):
+ """Get the first src_suffix in the list of src_suffixes."""
+ ret = self.src_suffixes(env)
+ if not ret:
+ return ''
+ return ret[0]
+
+ def add_emitter(self, suffix, emitter):
+ """Add a suffix-emitter mapping to this Builder.
+
+ This assumes that emitter has been initialized with an
+ appropriate dictionary type, and will throw a TypeError if
+ not, so the caller is responsible for knowing that this is an
+ appropriate method to call for the Builder in question.
+ """
+ self.emitter[suffix] = emitter
+
+ def add_src_builder(self, builder):
+ """
+ Add a new Builder to the list of src_builders.
+
+ This requires wiping out cached values so that the computed
+ lists of source suffixes get re-calculated.
+ """
+ self._memo = {}
+ self.src_builder.append(builder)
+
+ def _get_sdict(self, env):
+ """
+ Returns a dictionary mapping all of the source suffixes of all
+ src_builders of this Builder to the underlying Builder that
+ should be called first.
+
+ This dictionary is used for each target specified, so we save a
+ lot of extra computation by memoizing it for each construction
+ environment.
+
+ Note that this is re-computed each time, not cached, because there
+ might be changes to one of our source Builders (or one of their
+ source Builders, and so on, and so on...) that we can't "see."
+
+ The underlying methods we call cache their computed values,
+ though, so we hope repeatedly aggregating them into a dictionary
+ like this won't be too big a hit. We may need to look for a
+ better way to do this if performance data show this has turned
+ into a significant bottleneck.
+ """
+ sdict = {}
+ for bld in self.get_src_builders(env):
+ for suf in bld.src_suffixes(env):
+ sdict[suf] = bld
+ return sdict
+
+ def src_builder_sources(self, env, source, overwarn={}):
+ sdict = self._get_sdict(env)
+
+ src_suffixes = self.src_suffixes(env)
+
+ lengths = list(set(map(len, src_suffixes)))
+
+ def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
+ node_suffixes = map(lambda l, n=name: n[-l:], lengths)
+ for suf in src_suffixes:
+ if suf in node_suffixes:
+ return suf
+ return None
+
+ result = []
+ for s in SCons.Util.flatten(source):
+ if SCons.Util.is_String(s):
+ match_suffix = match_src_suffix(env.subst(s))
+ if not match_suffix and not '.' in s:
+ src_suf = self.get_src_suffix(env)
+ s = self._adjustixes(s, None, src_suf)[0]
+ else:
+ match_suffix = match_src_suffix(s.name)
+ if match_suffix:
+ try:
+ bld = sdict[match_suffix]
+ except KeyError:
+ result.append(s)
+ else:
+ tlist = bld._execute(env, None, [s], overwarn)
+ # If the subsidiary Builder returned more than one
+ # target, then filter out any sources that this
+ # Builder isn't capable of building.
+ if len(tlist) > 1:
+ mss = lambda t, m=match_src_suffix: m(t.name)
+ tlist = filter(mss, tlist)
+ result.extend(tlist)
+ else:
+ result.append(s)
+
+ source_factory = env.get_factory(self.source_factory)
+
+ return env.arg2nodes(result, source_factory)
+
+ def _get_src_builders_key(self, env):
+ return id(env)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
+
+ def get_src_builders(self, env):
+ """
+ Returns the list of source Builders for this Builder.
+
+ This exists mainly to look up Builders referenced as
+ strings in the 'BUILDER' variable of the construction
+ environment and cache the result.
+ """
+ memo_key = id(env)
+ try:
+ memo_dict = self._memo['get_src_builders']
+ except KeyError:
+ memo_dict = {}
+ self._memo['get_src_builders'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+
+ builders = []
+ for bld in self.src_builder:
+ if SCons.Util.is_String(bld):
+ try:
+ bld = env['BUILDERS'][bld]
+ except KeyError:
+ continue
+ builders.append(bld)
+
+ memo_dict[memo_key] = builders
+ return builders
+
+ def _subst_src_suffixes_key(self, env):
+ return id(env)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
+
+ def subst_src_suffixes(self, env):
+ """
+ The suffix list may contain construction variable expansions,
+ so we have to evaluate the individual strings. To avoid doing
+ this over and over, we memoize the results for each construction
+ environment.
+ """
+ memo_key = id(env)
+ try:
+ memo_dict = self._memo['subst_src_suffixes']
+ except KeyError:
+ memo_dict = {}
+ self._memo['subst_src_suffixes'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+ suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
+ memo_dict[memo_key] = suffixes
+ return suffixes
+
+ def src_suffixes(self, env):
+ """
+ Returns the list of source suffixes for all src_builders of this
+ Builder.
+
+ This is essentially a recursive descent of the src_builder "tree."
+ (This value isn't cached because there may be changes in a
+ src_builder many levels deep that we can't see.)
+ """
+ sdict = {}
+ suffixes = self.subst_src_suffixes(env)
+ for s in suffixes:
+ sdict[s] = 1
+ for builder in self.get_src_builders(env):
+ for s in builder.src_suffixes(env):
+ if not sdict.has_key(s):
+ sdict[s] = 1
+ suffixes.append(s)
+ return suffixes
+
+class CompositeBuilder(SCons.Util.Proxy):
+ """A Builder Proxy whose main purpose is to always have
+ a DictCmdGenerator as its action, and to provide access
+ to the DictCmdGenerator's add_action() method.
+ """
+
+ def __init__(self, builder, cmdgen):
+ if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
+ SCons.Util.Proxy.__init__(self, builder)
+
+ # cmdgen should always be an instance of DictCmdGenerator.
+ self.cmdgen = cmdgen
+ self.builder = builder
+
+ def add_action(self, suffix, action):
+ self.cmdgen.add_action(suffix, action)
+ self.set_src_suffix(self.cmdgen.src_suffixes())
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
new file mode 100644
index 0000000..2faaa2d
--- /dev/null
+++ b/src/engine/SCons/BuilderTests.py
@@ -0,0 +1,1650 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/BuilderTests.py 4577 2009/12/27 19:44:43 scons"
+
+# Define a null function for use as a builder action.
+# Where this is defined in the file seems to affect its
+# byte-code contents, so try to minimize changes by
+# defining it here, before we even import anything.
+def Func():
+ pass
+
+import os.path
+import re
+import sys
+import types
+import StringIO
+import unittest
+import UserList
+
+import TestCmd
+
+import SCons.Action
+import SCons.Builder
+import SCons.Environment
+import SCons.Errors
+import SCons.Subst
+import SCons.Util
+
+sys.stdout = StringIO.StringIO()
+
+# Initial setup of the common environment for all tests,
+# a temporary working directory containing a
+# script for writing arguments to an output file.
+#
+# We don't do this as a setUp() method because it's
+# unnecessary to create a separate directory and script
+# for each test, they can just use the one.
+test = TestCmd.TestCmd(workdir = '')
+
+outfile = test.workpath('outfile')
+outfile2 = test.workpath('outfile2')
+
+infile = test.workpath('infile')
+test.write(infile, "infile\n")
+
+show_string = None
+
+scons_env = SCons.Environment.Environment()
+
+env_arg2nodes_called = None
+
+class Environment:
+ def __init__(self, **kw):
+ self.d = {}
+ self.d['SHELL'] = scons_env['SHELL']
+ self.d['SPAWN'] = scons_env['SPAWN']
+ self.d['ESCAPE'] = scons_env['ESCAPE']
+ for k, v in kw.items():
+ self.d[k] = v
+ global env_arg2nodes_called
+ env_arg2nodes_called = None
+ self.scanner = None
+ self.fs = SCons.Node.FS.FS()
+ def subst(self, s):
+ if not SCons.Util.is_String(s):
+ return s
+ def substitute(m, d=self.d):
+ return d.get(m.group(1), '')
+ return re.sub(r'\$(\w+)', substitute, s)
+ def subst_target_source(self, string, raw=0, target=None,
+ source=None, dict=None, conv=None):
+ return SCons.Subst.scons_subst(string, self, raw, target,
+ source, dict, conv)
+ def subst_list(self, string, raw=0, target=None, source=None, conv=None):
+ return SCons.Subst.scons_subst_list(string, self, raw, target,
+ source, {}, {}, conv)
+ def arg2nodes(self, args, factory, **kw):
+ global env_arg2nodes_called
+ env_arg2nodes_called = 1
+ if not SCons.Util.is_List(args):
+ args = [args]
+ list = []
+ for a in args:
+ if SCons.Util.is_String(a):
+ a = factory(self.subst(a))
+ list.append(a)
+ return list
+ def get_factory(self, factory):
+ return factory or self.fs.File
+ def get_scanner(self, ext):
+ return self.scanner
+ def Dictionary(self):
+ return {}
+ def autogenerate(self, dir=''):
+ return {}
+ def __setitem__(self, item, var):
+ self.d[item] = var
+ def __getitem__(self, item):
+ return self.d[item]
+ def has_key(self, item):
+ return self.d.has_key(item)
+ def keys(self):
+ return self.d.keys()
+ def get(self, key, value=None):
+ return self.d.get(key, value)
+ def Override(self, overrides):
+ env = apply(Environment, (), self.d)
+ env.d.update(overrides)
+ env.scanner = self.scanner
+ return env
+ def _update(self, dict):
+ self.d.update(dict)
+ def items(self):
+ return self.d.items()
+ def sig_dict(self):
+ d = {}
+ for k,v in self.items(): d[k] = v
+ d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
+ d['TARGET'] = d['TARGETS'][0]
+ d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
+ d['SOURCE'] = d['SOURCES'][0]
+ return d
+ def __cmp__(self, other):
+ return cmp(self.scanner, other.scanner) or cmp(self.d, other.d)
+
+class MyAction:
+ def __init__(self, action):
+ self.action = action
+ def __call__(self, *args, **kw):
+ pass
+ def get_executor(self, env, overrides, tlist, slist, executor_kw):
+ return ['executor'] + [self.action]
+
+class MyNode_without_target_from_source:
+ def __init__(self, name):
+ self.name = name
+ self.sources = []
+ self.builder = None
+ self.is_explicit = None
+ self.side_effect = 0
+ self.suffix = os.path.splitext(name)[1]
+ def disambiguate(self):
+ return self
+ def __str__(self):
+ return self.name
+ def builder_set(self, builder):
+ self.builder = builder
+ def has_builder(self):
+ return not self.builder is None
+ def set_explicit(self, is_explicit):
+ self.is_explicit = is_explicit
+ def has_explicit_builder(self):
+ return self.is_explicit
+ def env_set(self, env, safe=0):
+ self.env = env
+ def add_source(self, source):
+ self.sources.extend(source)
+ def scanner_key(self):
+ return self.name
+ def is_derived(self):
+ return self.has_builder()
+ def generate_build_env(self, env):
+ return env
+ def get_build_env(self):
+ return self.executor.get_build_env()
+ def set_executor(self, executor):
+ self.executor = executor
+ def get_executor(self, create=1):
+ return self.executor
+
+class MyNode(MyNode_without_target_from_source):
+ def target_from_source(self, prefix, suffix, stripext):
+ return MyNode(prefix + stripext(str(self))[0] + suffix)
+
+class BuilderTestCase(unittest.TestCase):
+
+ def test__init__(self):
+ """Test simple Builder creation
+ """
+ builder = SCons.Builder.Builder(action="foo")
+ assert not builder is None, builder
+ builder = SCons.Builder.Builder(action="foo", OVERRIDE='x')
+ x = builder.overrides['OVERRIDE']
+ assert x == 'x', x
+
+ def test__nonzero__(self):
+ """Test a builder raising an exception when __nonzero__ is called
+ """
+ builder = SCons.Builder.Builder(action="foo")
+ exc_caught = None
+ try:
+ builder.__nonzero__()
+ except SCons.Errors.InternalError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected InternalError exception"
+
+ class Node:
+ pass
+
+ n = Node()
+ n.builder = builder
+ exc_caught = None
+ try:
+ if n.builder:
+ pass
+ except SCons.Errors.InternalError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected InternalError exception"
+
+ def test__call__(self):
+ """Test calling a builder to establish source dependencies
+ """
+ env = Environment()
+ builder = SCons.Builder.Builder(action="foo",
+ target_factory=MyNode,
+ source_factory=MyNode)
+
+ tgt = builder(env, source=[])
+ assert tgt == [], tgt
+
+ n1 = MyNode("n1")
+ n2 = MyNode("n2")
+ builder(env, target = n1, source = n2)
+ assert env_arg2nodes_called
+ assert n1.env == env, n1.env
+ assert n1.builder == builder, n1.builder
+ assert n1.sources == [n2], n1.sources
+ assert n1.executor, "no executor found"
+ assert not hasattr(n2, 'env')
+
+ l = [1]
+ ul = UserList.UserList([2])
+ try:
+ l.extend(ul)
+ except TypeError:
+ def mystr(l):
+ return str(map(str, l))
+ else:
+ mystr = str
+
+ nnn1 = MyNode("nnn1")
+ nnn2 = MyNode("nnn2")
+ tlist = builder(env, target = [nnn1, nnn2], source = [])
+ s = mystr(tlist)
+ assert s == "['nnn1', 'nnn2']", s
+ l = map(str, tlist)
+ assert l == ['nnn1', 'nnn2'], l
+
+ tlist = builder(env, target = 'n3', source = 'n4')
+ s = mystr(tlist)
+ assert s == "['n3']", s
+ target = tlist[0]
+ l = map(str, tlist)
+ assert l == ['n3'], l
+ assert target.name == 'n3'
+ assert target.sources[0].name == 'n4'
+
+ tlist = builder(env, target = 'n4 n5', source = ['n6 n7'])
+ s = mystr(tlist)
+ assert s == "['n4 n5']", s
+ l = map(str, tlist)
+ assert l == ['n4 n5'], l
+ target = tlist[0]
+ assert target.name == 'n4 n5'
+ assert target.sources[0].name == 'n6 n7'
+
+ tlist = builder(env, target = ['n8 n9'], source = 'n10 n11')
+ s = mystr(tlist)
+ assert s == "['n8 n9']", s
+ l = map(str, tlist)
+ assert l == ['n8 n9'], l
+ target = tlist[0]
+ assert target.name == 'n8 n9'
+ assert target.sources[0].name == 'n10 n11'
+
+ # A test to be uncommented when we freeze the environment
+ # as part of calling the builder.
+ #env1 = Environment(VAR='foo')
+ #target = builder(env1, target = 'n12', source = 'n13')
+ #env1['VAR'] = 'bar'
+ #be = target.get_build_env()
+ #assert be['VAR'] == 'foo', be['VAR']
+
+ if not hasattr(types, 'UnicodeType'):
+ uni = str
+ else:
+ uni = unicode
+
+ target = builder(env, target = uni('n12 n13'),
+ source = [uni('n14 n15')])[0]
+ assert target.name == uni('n12 n13')
+ assert target.sources[0].name == uni('n14 n15')
+
+ target = builder(env, target = [uni('n16 n17')],
+ source = uni('n18 n19'))[0]
+ assert target.name == uni('n16 n17')
+ assert target.sources[0].name == uni('n18 n19')
+
+ n20 = MyNode_without_target_from_source('n20')
+ flag = 0
+ try:
+ target = builder(env, None, source=n20)
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown if a source node can't create a target."
+
+ builder = SCons.Builder.Builder(action="foo",
+ target_factory=MyNode,
+ source_factory=MyNode,
+ prefix='p-',
+ suffix='.s')
+ target = builder(env, None, source='n21')[0]
+ assert target.name == 'p-n21.s', target
+
+ builder = SCons.Builder.Builder(misspelled_action="foo",
+ suffix = '.s')
+ try:
+ builder(env, target = 'n22', source = 'n22')
+ except SCons.Errors.UserError, e:
+ pass
+ else:
+ raise "Did not catch expected UserError."
+
+ builder = SCons.Builder.Builder(action="foo")
+ target = builder(env, None, source='n22', srcdir='src_dir')[0]
+ p = target.sources[0].path
+ assert p == os.path.join('src_dir', 'n22'), p
+
+ def test_mistaken_variables(self):
+ """Test keyword arguments that are often mistakes
+ """
+ import SCons.Warnings
+ env = Environment()
+ builder = SCons.Builder.Builder(action="foo")
+
+ save_warn = SCons.Warnings.warn
+ warned = []
+ def my_warn(exception, warning, warned=warned):
+ warned.append(warning)
+ SCons.Warnings.warn = my_warn
+
+ try:
+ target = builder(env, 'mistaken1', sources='mistaken1.c')
+ assert warned == ["Did you mean to use `source' instead of `sources'?"], warned
+ del warned[:]
+
+ target = builder(env, 'mistaken2', targets='mistaken2.c')
+ assert warned == ["Did you mean to use `target' instead of `targets'?"], warned
+ del warned[:]
+
+ target = builder(env, 'mistaken3', targets='mistaken3', sources='mistaken3.c')
+ assert "Did you mean to use `source' instead of `sources'?" in warned, warned
+ assert "Did you mean to use `target' instead of `targets'?" in warned, warned
+ del warned[:]
+ finally:
+ SCons.Warnings.warn = save_warn
+
+ def test_action(self):
+ """Test Builder creation
+
+ Verify that we can retrieve the supplied action attribute.
+ """
+ builder = SCons.Builder.Builder(action="foo")
+ assert builder.action.cmd_list == "foo"
+
+ def func():
+ pass
+ builder = SCons.Builder.Builder(action=func)
+ assert isinstance(builder.action, SCons.Action.FunctionAction)
+ # Preserve the following so that the baseline test will fail.
+ # Remove it in favor of the previous test at some convenient
+ # point in the future.
+ assert builder.action.execfunction == func
+
+ def test_generator(self):
+ """Test Builder creation given a generator function."""
+
+ def generator():
+ pass
+
+ builder = SCons.Builder.Builder(generator=generator)
+ assert builder.action.generator == generator
+
+ def test_get_name(self):
+ """Test the get_name() method
+ """
+
+ def test_cmp(self):
+ """Test simple comparisons of Builder objects
+ """
+ b1 = SCons.Builder.Builder(src_suffix = '.o')
+ b2 = SCons.Builder.Builder(src_suffix = '.o')
+ assert b1 == b2
+ b3 = SCons.Builder.Builder(src_suffix = '.x')
+ assert b1 != b3
+ assert b2 != b3
+
+ def test_target_factory(self):
+ """Test a Builder that creates target nodes of a specified class
+ """
+ class Foo:
+ pass
+ def FooFactory(target):
+ global Foo
+ return Foo(target)
+ builder = SCons.Builder.Builder(target_factory = FooFactory)
+ assert builder.target_factory is FooFactory
+ assert not builder.source_factory is FooFactory
+
+ def test_source_factory(self):
+ """Test a Builder that creates source nodes of a specified class
+ """
+ class Foo:
+ pass
+ def FooFactory(source):
+ global Foo
+ return Foo(source)
+ builder = SCons.Builder.Builder(source_factory = FooFactory)
+ assert not builder.target_factory is FooFactory
+ assert builder.source_factory is FooFactory
+
+ def test_splitext(self):
+ """Test the splitext() method attached to a Builder."""
+ b = SCons.Builder.Builder()
+ assert b.splitext('foo') == ('foo','')
+ assert b.splitext('foo.bar') == ('foo','.bar')
+ assert b.splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
+
+ class MyBuilder(SCons.Builder.BuilderBase):
+ def splitext(self, path):
+ return "called splitext()"
+
+ b = MyBuilder()
+ ret = b.splitext('xyz.c')
+ assert ret == "called splitext()", ret
+
+ def test_adjust_suffix(self):
+ """Test how a Builder adjusts file suffixes
+ """
+ b = SCons.Builder.Builder()
+ assert b.adjust_suffix('.foo') == '.foo'
+ assert b.adjust_suffix('foo') == '.foo'
+ assert b.adjust_suffix('$foo') == '$foo'
+
+ class MyBuilder(SCons.Builder.BuilderBase):
+ def adjust_suffix(self, suff):
+ return "called adjust_suffix()"
+
+ b = MyBuilder()
+ ret = b.adjust_suffix('.foo')
+ assert ret == "called adjust_suffix()", ret
+
+ def test_prefix(self):
+ """Test Builder creation with a specified target prefix
+
+ Make sure that there is no '.' separator appended.
+ """
+ env = Environment()
+ builder = SCons.Builder.Builder(prefix = 'lib.')
+ assert builder.get_prefix(env) == 'lib.'
+ builder = SCons.Builder.Builder(prefix = 'lib', action='')
+ assert builder.get_prefix(env) == 'lib'
+ tgt = builder(env, target = 'tgt1', source = 'src1')[0]
+ assert tgt.path == 'libtgt1', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
+ assert tgt.path == 'libtgt2a tgt2b', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = None, source = 'src3')[0]
+ assert tgt.path == 'libsrc3', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = None, source = 'lib/src4')[0]
+ assert tgt.path == os.path.join('lib', 'libsrc4'), \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
+ assert tgt.path == os.path.join('lib', 'libtgt5'), \
+ "Target has unexpected name: %s" % tgt.path
+
+ def gen_prefix(env, sources):
+ return "gen_prefix() says " + env['FOO']
+ my_env = Environment(FOO = 'xyzzy')
+ builder = SCons.Builder.Builder(prefix = gen_prefix)
+ assert builder.get_prefix(my_env) == "gen_prefix() says xyzzy"
+ my_env['FOO'] = 'abracadabra'
+ assert builder.get_prefix(my_env) == "gen_prefix() says abracadabra"
+
+ def my_emit(env, sources):
+ return env.subst('$EMIT')
+ my_env = Environment(FOO = '.foo', EMIT = 'emit-')
+ builder = SCons.Builder.Builder(prefix = {None : 'default-',
+ '.in' : 'out-',
+ '.x' : 'y-',
+ '$FOO' : 'foo-',
+ '.zzz' : my_emit},
+ action = '')
+ tgt = builder(my_env, target = None, source = 'f1')[0]
+ assert tgt.path == 'default-f1', tgt.path
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
+ assert tgt.path == 'default-f2', tgt.path
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
+ assert tgt.path == 'out-f3', tgt.path
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
+ assert tgt.path == 'y-f4', tgt.path
+ tgt = builder(my_env, target = None, source = 'f5.foo')[0]
+ assert tgt.path == 'foo-f5', tgt.path
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
+ assert tgt.path == 'emit-f6', tgt.path
+
+ def test_set_suffix(self):
+ """Test the set_suffix() method"""
+ b = SCons.Builder.Builder(action='')
+ env = Environment(XSUFFIX = '.x')
+
+ s = b.get_suffix(env)
+ assert s == '', s
+
+ b.set_suffix('.foo')
+ s = b.get_suffix(env)
+ assert s == '.foo', s
+
+ b.set_suffix('$XSUFFIX')
+ s = b.get_suffix(env)
+ assert s == '.x', s
+
+ def test_src_suffix(self):
+ """Test Builder creation with a specified source file suffix
+
+ Make sure that the '.' separator is appended to the
+ beginning if it isn't already present.
+ """
+ env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
+
+ b1 = SCons.Builder.Builder(src_suffix = '.c', action='')
+ assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
+
+ tgt = b1(env, target = 'tgt2', source = 'src2')[0]
+ assert tgt.sources[0].path == 'src2.c', \
+ "Source has unexpected name: %s" % tgt.sources[0].path
+
+ tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0]
+ assert len(tgt.sources) == 1
+ assert tgt.sources[0].path == 'src3a src3b.c', \
+ "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
+
+ b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1)
+ r = b2.src_suffixes(env)
+ r.sort()
+ assert r == ['.2', '.c'], r
+
+ b3 = SCons.Builder.Builder(action = {'.3a' : '', '.3b' : ''})
+ s = b3.src_suffixes(env)
+ s.sort()
+ assert s == ['.3a', '.3b'], s
+
+ b4 = SCons.Builder.Builder(src_suffix = '$XSUFFIX')
+ assert b4.src_suffixes(env) == ['.x'], b4.src_suffixes(env)
+
+ b5 = SCons.Builder.Builder(action = { '.y' : ''})
+ assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
+
+ def test_srcsuffix_nonext(self):
+ "Test target generation from non-extension source suffixes"
+ env = Environment()
+ b6 = SCons.Builder.Builder(action = '',
+ src_suffix='_src.a',
+ suffix='.b')
+ tgt = b6(env, target=None, source='foo_src.a')
+ assert str(tgt[0]) == 'foo.b', str(tgt[0])
+
+ b7 = SCons.Builder.Builder(action = '',
+ src_suffix='_source.a',
+ suffix='_obj.b')
+ b8 = SCons.Builder.Builder(action = '',
+ src_builder=b7,
+ suffix='.c')
+ tgt = b8(env, target=None, source='foo_source.a')
+ assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
+ src = env.fs.File('foo_source.a')
+ tgt = b8(env, target=None, source=src)
+ assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
+
+ b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'},
+ suffix='.c')
+ b9.add_action('_altsrc.b', 'altaction')
+ tgt = b9(env, target=None, source='foo_altsrc.b')
+ assert str(tgt[0]) == 'foo.c', str(tgt[0])
+
+ def test_src_suffix_expansion(self):
+ """Test handling source suffixes when an expansion is involved"""
+ env = Environment(OBJSUFFIX = '.obj')
+
+ b1 = SCons.Builder.Builder(action = '',
+ src_suffix='.c',
+ suffix='.obj')
+ b2 = SCons.Builder.Builder(action = '',
+ src_builder=b1,
+ src_suffix='.obj',
+ suffix='.exe')
+ tgt = b2(env, target=None, source=['foo$OBJSUFFIX'])
+ s = map(str, tgt[0].sources)
+ assert s == ['foo.obj'], s
+
+ def test_suffix(self):
+ """Test Builder creation with a specified target suffix
+
+ Make sure that the '.' separator is appended to the
+ beginning if it isn't already present.
+ """
+ env = Environment()
+ builder = SCons.Builder.Builder(suffix = '.o')
+ assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
+ builder = SCons.Builder.Builder(suffix = 'o', action='')
+ assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
+ tgt = builder(env, target = 'tgt3', source = 'src3')[0]
+ assert tgt.path == 'tgt3.o', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
+ assert tgt.path == 'tgt4a tgt4b.o', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = None, source = 'src5')[0]
+ assert tgt.path == 'src5.o', \
+ "Target has unexpected name: %s" % tgt.path
+
+ def gen_suffix(env, sources):
+ return "gen_suffix() says " + env['BAR']
+ my_env = Environment(BAR = 'hocus pocus')
+ builder = SCons.Builder.Builder(suffix = gen_suffix)
+ assert builder.get_suffix(my_env) == "gen_suffix() says hocus pocus", builder.get_suffix(my_env)
+ my_env['BAR'] = 'presto chango'
+ assert builder.get_suffix(my_env) == "gen_suffix() says presto chango"
+
+ def my_emit(env, sources):
+ return env.subst('$EMIT')
+ my_env = Environment(BAR = '.bar', EMIT = '.emit')
+ builder = SCons.Builder.Builder(suffix = {None : '.default',
+ '.in' : '.out',
+ '.x' : '.y',
+ '$BAR' : '.new',
+ '.zzz' : my_emit},
+ action='')
+ tgt = builder(my_env, target = None, source = 'f1')[0]
+ assert tgt.path == 'f1.default', tgt.path
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
+ assert tgt.path == 'f2.default', tgt.path
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
+ assert tgt.path == 'f3.out', tgt.path
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
+ assert tgt.path == 'f4.y', tgt.path
+ tgt = builder(my_env, target = None, source = 'f5.bar')[0]
+ assert tgt.path == 'f5.new', tgt.path
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
+ assert tgt.path == 'f6.emit', tgt.path
+
+ def test_single_source(self):
+ """Test Builder with single_source flag set"""
+ def func(target, source, env):
+ open(str(target[0]), "w")
+ if (len(source) == 1 and len(target) == 1):
+ env['CNT'][0] = env['CNT'][0] + 1
+
+ env = Environment()
+ infiles = []
+ outfiles = []
+ for i in range(10):
+ infiles.append(test.workpath('%d.in' % i))
+ outfiles.append(test.workpath('%d.out' % i))
+ test.write(infiles[-1], "\n")
+ builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None),
+ single_source = 1, suffix='.out')
+ env['CNT'] = [0]
+ tgt = builder(env, target=outfiles[0], source=infiles[0])[0]
+ s = str(tgt)
+ assert s == test.workpath('0.out'), s
+ tgt.prepare()
+ tgt.build()
+ assert env['CNT'][0] == 1, env['CNT'][0]
+ tgt = builder(env, outfiles[1], infiles[1])[0]
+ s = str(tgt)
+ assert s == test.workpath('1.out'), s
+ tgt.prepare()
+ tgt.build()
+ assert env['CNT'][0] == 2
+ tgts = builder(env, None, infiles[2:4])
+ try:
+ [].extend(UserList.UserList())
+ except TypeError:
+ # Old Python version (1.5.2) that can't handle extending
+ # a list with list-like objects. That means the return
+ # value from the builder call is a real list with Nodes,
+ # and doesn't have a __str__() method that stringifies
+ # the individual elements. Since we're gong to drop 1.5.2
+ # support anyway, don't bother trying to test for it.
+ pass
+ else:
+ s = str(tgts)
+ expect = str([test.workpath('2.out'), test.workpath('3.out')])
+ assert s == expect, s
+ for t in tgts: t.prepare()
+ tgts[0].build()
+ tgts[1].build()
+ assert env['CNT'][0] == 4
+ try:
+ tgt = builder(env, outfiles[4], infiles[4:6])
+ except SCons.Errors.UserError:
+ pass
+ else:
+ assert 0
+ try:
+ # The builder may output more than one target per input file.
+ tgt = builder(env, outfiles[4:6], infiles[4:6])
+ except SCons.Errors.UserError:
+ pass
+ else:
+ assert 0
+
+
+ def test_lists(self):
+ """Testing handling lists of targets and source"""
+ def function2(target, source, env, tlist = [outfile, outfile2], **kw):
+ for t in target:
+ open(str(t), 'w').write("function2\n")
+ for t in tlist:
+ if not t in map(str, target):
+ open(t, 'w').write("function2\n")
+ return 1
+
+ env = Environment()
+ builder = SCons.Builder.Builder(action = function2)
+
+ tgts = builder(env, source=[])
+ assert tgts == [], tgts
+
+ tgts = builder(env, target = [outfile, outfile2], source = infile)
+ for t in tgts:
+ t.prepare()
+ try:
+ tgts[0].build()
+ except SCons.Errors.BuildError:
+ pass
+ c = test.read(outfile, 'r')
+ assert c == "function2\n", c
+ c = test.read(outfile2, 'r')
+ assert c == "function2\n", c
+
+ sub1_out = test.workpath('sub1', 'out')
+ sub2_out = test.workpath('sub2', 'out')
+
+ def function3(target, source, env, tlist = [sub1_out, sub2_out]):
+ for t in target:
+ open(str(t), 'w').write("function3\n")
+ for t in tlist:
+ if not t in map(str, target):
+ open(t, 'w').write("function3\n")
+ return 1
+
+ builder = SCons.Builder.Builder(action = function3)
+ tgts = builder(env, target = [sub1_out, sub2_out], source = infile)
+ for t in tgts:
+ t.prepare()
+ try:
+ tgts[0].build()
+ except SCons.Errors.BuildError:
+ pass
+ c = test.read(sub1_out, 'r')
+ assert c == "function3\n", c
+ c = test.read(sub2_out, 'r')
+ assert c == "function3\n", c
+ assert os.path.exists(test.workpath('sub1'))
+ assert os.path.exists(test.workpath('sub2'))
+
+ def test_src_builder(self):
+ """Testing Builders with src_builder"""
+ # These used to be MultiStepBuilder objects until we
+ # eliminated it as a separate class
+ env = Environment()
+ builder1 = SCons.Builder.Builder(action='foo',
+ src_suffix='.bar',
+ suffix='.foo')
+ builder2 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder = builder1,
+ src_suffix = '.foo')
+
+ tgt = builder2(env, source=[])
+ assert tgt == [], tgt
+
+ sources = ['test.bar', 'test2.foo', 'test3.txt', 'test4']
+ tgt = builder2(env, target='baz', source=sources)[0]
+ s = str(tgt)
+ assert s == 'baz', s
+ s = map(str, tgt.sources)
+ assert s == ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['test.bar'], s
+
+ tgt = builder2(env, None, 'aaa.bar')[0]
+ s = str(tgt)
+ assert s == 'aaa', s
+ s = map(str, tgt.sources)
+ assert s == ['aaa.foo'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['aaa.bar'], s
+
+ builder3 = SCons.Builder.Builder(action='bld3')
+ assert not builder3.src_builder is builder1.src_builder
+
+ builder4 = SCons.Builder.Builder(action='bld4',
+ src_suffix='.i',
+ suffix='_wrap.c')
+ builder5 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder=builder4,
+ suffix='.obj',
+ src_suffix='.c')
+ builder6 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder=builder5,
+ suffix='.exe',
+ src_suffix='.obj')
+ tgt = builder6(env, 'test', 'test.i')[0]
+ s = str(tgt)
+ assert s == 'test.exe', s
+ s = map(str, tgt.sources)
+ assert s == ['test_wrap.obj'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['test_wrap.c'], s
+ s = map(str, tgt.sources[0].sources[0].sources)
+ assert s == ['test.i'], s
+
+ def test_target_scanner(self):
+ """Testing ability to set target and source scanners through a builder."""
+ global instanced
+ class TestScanner:
+ pass
+ tscan = TestScanner()
+ sscan = TestScanner()
+ env = Environment()
+ builder = SCons.Builder.Builder(target_scanner=tscan,
+ source_scanner=sscan,
+ action='')
+ tgt = builder(env, target='foo2', source='bar')[0]
+ assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner
+
+ builder1 = SCons.Builder.Builder(action='foo',
+ src_suffix='.bar',
+ suffix='.foo')
+ builder2 = SCons.Builder.Builder(action='foo',
+ src_builder = builder1,
+ target_scanner = tscan,
+ source_scanner = tscan)
+ tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
+ assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner
+
+ def test_actual_scanner(self):
+ """Test usage of actual Scanner objects."""
+
+ import SCons.Scanner
+
+ def func(self):
+ pass
+
+ scanner = SCons.Scanner.Base(func, name='fooscan')
+
+ b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
+ b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
+ b3 = SCons.Builder.Builder(action='bld')
+
+ assert b1 == b2
+ assert b1 != b3
+
+ def test_src_scanner(slf):
+ """Testing ability to set a source file scanner through a builder."""
+ class TestScanner:
+ def key(self, env):
+ return 'TestScannerkey'
+ def instance(self, env):
+ return self
+ def select(self, node):
+ return self
+ name = 'TestScanner'
+ def __str__(self):
+ return self.name
+
+ scanner = TestScanner()
+ builder = SCons.Builder.Builder(action='action')
+
+ # With no scanner specified, source_scanner and
+ # backup_source_scanner are None.
+ bar_y = MyNode('bar.y')
+ env1 = Environment()
+ tgt = builder(env1, target='foo1.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner is None, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), repr(s)
+
+ # An Environment that has suffix-specified SCANNERS should
+ # provide a source scanner to the target.
+ class EnvTestScanner:
+ def key(self, env):
+ return '.y'
+ def instance(self, env):
+ return self
+ name = 'EnvTestScanner'
+ def __str__(self):
+ return self.name
+ def select(self, node):
+ return self
+ def path(self, env, dir=None):
+ return ()
+ def __call__(self, node, env, path):
+ return []
+ env3 = Environment(SCANNERS = [EnvTestScanner()])
+ env3.scanner = EnvTestScanner() # test env's version of SCANNERS
+ tgt = builder(env3, target='foo2.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), repr(s)
+
+ # Can't simply specify the scanner as a builder argument; it's
+ # global to all invocations of this builder.
+ tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), s
+
+ # Now use a builder that actually has scanners and ensure that
+ # the target is set accordingly (using the specified scanner
+ # instead of the Environment's scanner)
+ builder = SCons.Builder.Builder(action='action',
+ source_scanner=scanner,
+ target_scanner=scanner)
+ tgt = builder(env3, target='foo4.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner
+ assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner)
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), s
+
+
+
+ def test_Builder_API(self):
+ """Test Builder interface.
+
+ Some of this is tested elsewhere in this file, but this is a
+ quick collection of common operations on builders with various
+ forms of component specifications."""
+
+ builder = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':builder})
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == '', r
+ r = builder.get_suffix(env)
+ assert r == '', r
+ r = builder.get_src_suffix(env)
+ assert r == '', r
+ r = builder.src_suffixes(env)
+ assert r == [], r
+
+ # src_suffix can be a single string or a list of strings
+ # src_suffixes() caches its return value, so we use a new
+ # Builder each time we do any of these tests
+
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix('.foo')
+ r = bld.get_src_suffix(env)
+ assert r == '.foo', r
+ r = bld.src_suffixes(env)
+ assert r == ['.foo'], r
+
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix(['.foo', '.bar'])
+ r = bld.get_src_suffix(env)
+ assert r == '.foo', r
+ r = bld.src_suffixes(env)
+ assert r == ['.foo', '.bar'], r
+
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix(['.bar', '.foo'])
+ r = bld.get_src_suffix(env)
+ assert r == '.bar', r
+ r = bld.src_suffixes(env)
+ r.sort()
+ assert r == ['.bar', '.foo'], r
+
+ # adjust_suffix normalizes the suffix, adding a `.' if needed
+
+ r = builder.adjust_suffix('.foo')
+ assert r == '.foo', r
+ r = builder.adjust_suffix('_foo')
+ assert r == '_foo', r
+ r = builder.adjust_suffix('$foo')
+ assert r == '$foo', r
+ r = builder.adjust_suffix('foo')
+ assert r == '.foo', r
+ r = builder.adjust_suffix('f._$oo')
+ assert r == '.f._$oo', r
+
+ # prefix and suffix can be one of:
+ # 1. a string (adjusted and env variables substituted),
+ # 2. a function (passed (env,sources), returns suffix string)
+ # 3. a dict of src_suffix:suffix settings, key==None is
+ # default suffix (special case of #2, so adjust_suffix
+ # not applied)
+
+ builder = SCons.Builder.Builder(prefix='lib', suffix='foo')
+
+ env = Environment(BUILDERS={'Bld':builder})
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'lib', r
+ r = builder.get_suffix(env)
+ assert r == '.foo', r
+
+ mkpref = lambda env,sources: 'Lib'
+ mksuff = lambda env,sources: '.Foo'
+ builder = SCons.Builder.Builder(prefix=mkpref, suffix=mksuff)
+
+ env = Environment(BUILDERS={'Bld':builder})
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'Lib', r
+ r = builder.get_suffix(env)
+ assert r == '.Foo', r
+
+ builder = SCons.Builder.Builder(prefix='$PREF', suffix='$SUFF')
+
+ env = Environment(BUILDERS={'Bld':builder},PREF="LIB",SUFF=".FOO")
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'LIB', r
+ r = builder.get_suffix(env)
+ assert r == '.FOO', r
+
+ builder = SCons.Builder.Builder(prefix={None:'A_',
+ '.C':'E_'},
+ suffix={None:'.B',
+ '.C':'.D'})
+
+ env = Environment(BUILDERS={'Bld':builder})
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r == '.B', r
+ r = builder.get_prefix(env, [MyNode('X.C')])
+ assert r == 'E_', r
+ r = builder.get_suffix(env, [MyNode('X.C')])
+ assert r == '.D', r
+
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r is None, r
+ r = builder.get_src_suffix(env)
+ assert r == '', r
+ r = builder.src_suffixes(env)
+ assert r == [], r
+
+ # Builder actions can be a string, a list, or a dictionary
+ # whose keys are the source suffix. The add_action()
+ # specifies a new source suffix/action binding.
+
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+ builder.add_action('.src_sfx1', 'FOO')
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r is None, r
+ r = builder.get_suffix(env, [MyNode('X.src_sfx1')])
+ assert r is None, r
+ r = builder.get_src_suffix(env)
+ assert r == '.src_sfx1', r
+ r = builder.src_suffixes(env)
+ assert r == ['.src_sfx1'], r
+
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+ builder.add_action('.src_sfx1', 'FOO')
+ builder.add_action('.src_sfx2', 'BAR')
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r is None, r
+ r = builder.get_src_suffix(env)
+ assert r == '.src_sfx1', r
+ r = builder.src_suffixes(env)
+ r.sort()
+ assert r == ['.src_sfx1', '.src_sfx2'], r
+
+
+ def test_Builder_Args(self):
+ """Testing passing extra args to a builder."""
+ def buildFunc(target, source, env, s=self):
+ s.foo=env['foo']
+ s.bar=env['bar']
+ assert env['CC'] == 'mycc'
+
+ env=Environment(CC='cc')
+
+ builder = SCons.Builder.Builder(action=buildFunc)
+ tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')[0]
+ tgt.build()
+ assert self.foo == 1, self.foo
+ assert self.bar == 2, self.bar
+
+ def test_emitter(self):
+ """Test emitter functions."""
+ def emit(target, source, env):
+ foo = env.get('foo', 0)
+ bar = env.get('bar', 0)
+ for t in target:
+ assert isinstance(t, MyNode)
+ assert t.has_builder()
+ for s in source:
+ assert isinstance(s, MyNode)
+ if foo:
+ target.append("bar%d"%foo)
+ if bar:
+ source.append("baz")
+ return ( target, source )
+
+ env = Environment()
+ builder = SCons.Builder.Builder(action='foo',
+ emitter=emit,
+ target_factory=MyNode,
+ source_factory=MyNode)
+ tgt = builder(env, target='foo2', source='bar')[0]
+ assert str(tgt) == 'foo2', str(tgt)
+ assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+ tgt = builder(env, target='foo3', source='bar', foo=1)
+ assert len(tgt) == 2, len(tgt)
+ assert 'foo3' in map(str, tgt), map(str, tgt)
+ assert 'bar1' in map(str, tgt), map(str, tgt)
+
+ tgt = builder(env, target='foo4', source='bar', bar=1)[0]
+ assert str(tgt) == 'foo4', str(tgt)
+ assert len(tgt.sources) == 2, len(tgt.sources)
+ assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
+ assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
+ env2=Environment(FOO=emit)
+ builder2=SCons.Builder.Builder(action='foo',
+ emitter="$FOO",
+ target_factory=MyNode,
+ source_factory=MyNode)
+
+ builder2a=SCons.Builder.Builder(action='foo',
+ emitter="$FOO",
+ target_factory=MyNode,
+ source_factory=MyNode)
+
+ assert builder2 == builder2a, repr(builder2.__dict__) + "\n" + repr(builder2a.__dict__)
+
+ tgt = builder2(env2, target='foo5', source='bar')[0]
+ assert str(tgt) == 'foo5', str(tgt)
+ assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+ tgt = builder2(env2, target='foo6', source='bar', foo=2)
+ assert len(tgt) == 2, len(tgt)
+ assert 'foo6' in map(str, tgt), map(str, tgt)
+ assert 'bar2' in map(str, tgt), map(str, tgt)
+
+ tgt = builder2(env2, target='foo7', source='bar', bar=1)[0]
+ assert str(tgt) == 'foo7', str(tgt)
+ assert len(tgt.sources) == 2, len(tgt.sources)
+ assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
+ assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
+ def test_emitter_preserve_builder(self):
+ """Test an emitter not overwriting a newly-set builder"""
+ env = Environment()
+
+ new_builder = SCons.Builder.Builder(action='new')
+ node = MyNode('foo8')
+ new_node = MyNode('foo8.new')
+
+ def emit(target, source, env, nb=new_builder, nn=new_node):
+ for t in target:
+ t.builder = nb
+ return [nn], source
+
+ builder=SCons.Builder.Builder(action='foo',
+ emitter=emit,
+ target_factory=MyNode,
+ source_factory=MyNode)
+ tgt = builder(env, target=node, source='bar')[0]
+ assert tgt is new_node, tgt
+ assert tgt.builder is builder, tgt.builder
+ assert node.builder is new_builder, node.builder
+
+ def test_emitter_suffix_map(self):
+ """Test mapping file suffixes to emitter functions"""
+ env = Environment()
+
+ def emit4a(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4a-' + x[:-3], source)
+ return (target, source)
+ def emit4b(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4b-' + x[:-3], source)
+ return (target, source)
+
+ builder = SCons.Builder.Builder(action='foo',
+ emitter={'.4a':emit4a,
+ '.4b':emit4b},
+ target_factory=MyNode,
+ source_factory=MyNode)
+ tgt = builder(env, None, source='aaa.4a')[0]
+ assert str(tgt) == 'emit4a-aaa', str(tgt)
+ tgt = builder(env, None, source='bbb.4b')[0]
+ assert str(tgt) == 'emit4b-bbb', str(tgt)
+ tgt = builder(env, None, source='ccc.4c')[0]
+ assert str(tgt) == 'ccc', str(tgt)
+
+ def emit4c(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4c-' + x[:-3], source)
+ return (target, source)
+
+ builder.add_emitter('.4c', emit4c)
+ tgt = builder(env, None, source='ccc.4c')[0]
+ assert str(tgt) == 'emit4c-ccc', str(tgt)
+
+ def test_emitter_function_list(self):
+ """Test lists of emitter functions"""
+ env = Environment()
+
+ def emit1a(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit1a-' + x[:-2], source)
+ return (target, source)
+ def emit1b(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit1b-' + x[:-2], source)
+ return (target, source)
+ builder1 = SCons.Builder.Builder(action='foo',
+ emitter=[emit1a, emit1b],
+ node_factory=MyNode)
+
+ tgts = builder1(env, target='target-1', source='aaa.1')
+ tgts = map(str, tgts)
+ assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts
+
+ # Test a list of emitter functions through the environment.
+ def emit2a(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit2a-' + x[:-2], source)
+ return (target, source)
+ def emit2b(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit2b-' + x[:-2], source)
+ return (target, source)
+ builder2 = SCons.Builder.Builder(action='foo',
+ emitter='$EMITTERLIST',
+ node_factory=MyNode)
+
+ env = Environment(EMITTERLIST = [emit2a, emit2b])
+
+ tgts = builder2(env, target='target-2', source='aaa.2')
+ tgts = map(str, tgts)
+ assert tgts == ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts
+
+ def test_emitter_TARGET_SOURCE(self):
+ """Test use of $TARGET and $SOURCE in emitter results"""
+
+ env = SCons.Environment.Environment()
+
+ def emit(target, source, env):
+ return (target + ['${SOURCE}.s1', '${TARGET}.t1'],
+ source + ['${TARGET}.t2', '${SOURCE}.s2'])
+
+ builder = SCons.Builder.Builder(action='foo',
+ emitter = emit,
+ node_factory = MyNode)
+
+ targets = builder(env, target = 'TTT', source ='SSS')
+ sources = targets[0].sources
+ targets = map(str, targets)
+ sources = map(str, sources)
+ assert targets == ['TTT', 'SSS.s1', 'TTT.t1'], targets
+ assert sources == ['SSS', 'TTT.t2', 'SSS.s2'], targets
+
+ def test_no_target(self):
+ """Test deducing the target from the source."""
+
+ env = Environment()
+ b = SCons.Builder.Builder(action='foo', suffix='.o')
+
+ tgt = b(env, None, 'aaa')[0]
+ assert str(tgt) == 'aaa.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'aaa', map(str, tgt.sources)
+
+ tgt = b(env, None, 'bbb.c')[0]
+ assert str(tgt) == 'bbb.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'bbb.c', map(str, tgt.sources)
+
+ tgt = b(env, None, 'ccc.x.c')[0]
+ assert str(tgt) == 'ccc.x.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'ccc.x.c', map(str, tgt.sources)
+
+ tgt = b(env, None, ['d0.c', 'd1.c'])[0]
+ assert str(tgt) == 'd0.o', str(tgt)
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'd0.c', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'd1.c', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source='eee')[0]
+ assert str(tgt) == 'eee.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'eee', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source='fff.c')[0]
+ assert str(tgt) == 'fff.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'fff.c', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source='ggg.x.c')[0]
+ assert str(tgt) == 'ggg.x.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'ggg.x.c', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0]
+ assert str(tgt) == 'h0.o', str(tgt)
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'h0.c', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'h1.c', map(str, tgt.sources)
+
+ w = b(env, target='i0.w', source=['i0.x'])[0]
+ y = b(env, target='i1.y', source=['i1.z'])[0]
+ tgt = b(env, None, source=[w, y])[0]
+ assert str(tgt) == 'i0.o', str(tgt)
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'i0.w', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'i1.y', map(str, tgt.sources)
+
+ def test_get_name(self):
+ """Test getting name of builder.
+
+ Each type of builder should return its environment-specific
+ name when queried appropriately. """
+
+ b1 = SCons.Builder.Builder(action='foo', suffix='.o')
+ b2 = SCons.Builder.Builder(action='foo', suffix='.c')
+ b3 = SCons.Builder.Builder(action='bar', src_suffix = '.foo',
+ src_builder = b1)
+ b4 = SCons.Builder.Builder(action={})
+ b5 = SCons.Builder.Builder(action='foo', name='builder5')
+ b6 = SCons.Builder.Builder(action='foo')
+ assert isinstance(b4, SCons.Builder.CompositeBuilder)
+ assert isinstance(b4.action, SCons.Action.CommandGeneratorAction)
+
+ env = Environment(BUILDERS={'bldr1': b1,
+ 'bldr2': b2,
+ 'bldr3': b3,
+ 'bldr4': b4})
+ env2 = Environment(BUILDERS={'B1': b1,
+ 'B2': b2,
+ 'B3': b3,
+ 'B4': b4})
+ # With no name, get_name will return the class. Allow
+ # for caching...
+ b6_names = [
+ 'SCons.Builder.BuilderBase',
+ "<class 'SCons.Builder.BuilderBase'>",
+ 'SCons.Memoize.BuilderBase',
+ "<class 'SCons.Memoize.BuilderBase'>",
+ ]
+
+ assert b1.get_name(env) == 'bldr1', b1.get_name(env)
+ assert b2.get_name(env) == 'bldr2', b2.get_name(env)
+ assert b3.get_name(env) == 'bldr3', b3.get_name(env)
+ assert b4.get_name(env) == 'bldr4', b4.get_name(env)
+ assert b5.get_name(env) == 'builder5', b5.get_name(env)
+ assert b6.get_name(env) in b6_names, b6.get_name(env)
+
+ assert b1.get_name(env2) == 'B1', b1.get_name(env2)
+ assert b2.get_name(env2) == 'B2', b2.get_name(env2)
+ assert b3.get_name(env2) == 'B3', b3.get_name(env2)
+ assert b4.get_name(env2) == 'B4', b4.get_name(env2)
+ assert b5.get_name(env2) == 'builder5', b5.get_name(env2)
+ assert b6.get_name(env2) in b6_names, b6.get_name(env2)
+
+ assert b5.get_name(None) == 'builder5', b5.get_name(None)
+ assert b6.get_name(None) in b6_names, b6.get_name(None)
+
+ # This test worked before adding batch builders, but we must now
+ # be able to disambiguate a CompositeAction into a more specific
+ # action based on file suffix at call time. Leave this commented
+ # out (for now) in case this reflects a real-world use case that
+ # we must accomodate and we want to resurrect this test.
+ #tgt = b4(env, target = 'moo', source='cow')
+ #assert tgt[0].builder.get_name(env) == 'bldr4'
+
+class CompositeBuilderTestCase(unittest.TestCase):
+
+ def setUp(self):
+ def func_action(target, source, env):
+ return 0
+
+ builder = SCons.Builder.Builder(action={ '.foo' : func_action,
+ '.bar' : func_action})
+
+ self.func_action = func_action
+ self.builder = builder
+
+ def test___init__(self):
+ """Test CompositeBuilder creation"""
+ env = Environment()
+ builder = SCons.Builder.Builder(action={})
+
+ tgt = builder(env, source=[])
+ assert tgt == [], tgt
+
+ assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
+ def test_target_action(self):
+ """Test CompositeBuilder setting of target builder actions"""
+ env = Environment()
+ builder = self.builder
+
+ tgt = builder(env, target='test1', source='test1.foo')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ assert tgt.builder.action is builder.action
+
+ tgt = builder(env, target='test2', source='test1.bar')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ assert tgt.builder.action is builder.action
+
+ def test_multiple_suffix_error(self):
+ """Test the CompositeBuilder multiple-source-suffix error"""
+ env = Environment()
+ builder = self.builder
+
+ flag = 0
+ try:
+ builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo"
+ assert str(e) == expect, e
+
+ def test_source_ext_match(self):
+ """Test the CompositeBuilder source_ext_match argument"""
+ env = Environment()
+ func_action = self.func_action
+ builder = SCons.Builder.Builder(action={ '.foo' : func_action,
+ '.bar' : func_action},
+ source_ext_match = None)
+
+ tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
+ tgt.build()
+
+ def test_suffix_variable(self):
+ """Test CompositeBuilder defining action suffixes through a variable"""
+ env = Environment(BAR_SUFFIX = '.BAR2', FOO_SUFFIX = '.FOO2')
+ func_action = self.func_action
+ builder = SCons.Builder.Builder(action={ '.foo' : func_action,
+ '.bar' : func_action,
+ '$BAR_SUFFIX' : func_action,
+ '$FOO_SUFFIX' : func_action })
+
+ tgt = builder(env, target='test4', source=['test4.BAR2'])[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ try:
+ tgt.build()
+ flag = 1
+ except SCons.Errors.UserError, e:
+ print e
+ flag = 0
+ assert flag, "It should be possible to define actions in composite builders using variables."
+ env['FOO_SUFFIX'] = '.BAR2'
+ builder.add_action('$NEW_SUFFIX', func_action)
+ flag = 0
+ try:
+ builder(env, target='test5', source=['test5.BAR2'])[0]
+ except SCons.Errors.UserError:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with ambigous suffixes."
+
+ def test_src_builder(self):
+ """Test CompositeBuilder's use of a src_builder"""
+ env = Environment()
+
+ foo_bld = SCons.Builder.Builder(action = 'a-foo',
+ src_suffix = '.ina',
+ suffix = '.foo')
+ assert isinstance(foo_bld, SCons.Builder.BuilderBase)
+ builder = SCons.Builder.Builder(action = { '.foo' : 'foo',
+ '.bar' : 'bar' },
+ src_builder = foo_bld)
+ assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
+ tgt = builder(env, target='t1', source='t1a.ina t1b.ina')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+ tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase), tgt.builder.__dict__
+
+ bar_bld = SCons.Builder.Builder(action = 'a-bar',
+ src_suffix = '.inb',
+ suffix = '.bar')
+ assert isinstance(bar_bld, SCons.Builder.BuilderBase)
+ builder = SCons.Builder.Builder(action = { '.foo' : 'foo'},
+ src_builder = [foo_bld, bar_bld])
+ assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
+ builder.add_action('.bar', 'bar')
+
+ tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+ tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+ flag = 0
+ try:
+ builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t5']' from `test5b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t6']' from `test6b.foo': Cannot build multiple sources with different extensions: .bar, .foo"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t4']' from `test4b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t7', source=[env.fs.File('test7')])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t7']': Cannot deduce file extension from source files: ['test7']"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t8', source=['test8.unknown'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder target with an unknown suffix."
+ expect = "While building `['t8']' from `['test8.unknown']': Don't know how to build from a source file with suffix `.unknown'. Expected a suffix in this list: ['.foo', '.bar']."
+ assert str(e) == expect, e
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ BuilderTestCase,
+ CompositeBuilderTestCase
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py
new file mode 100644
index 0000000..5181d49
--- /dev/null
+++ b/src/engine/SCons/CacheDir.py
@@ -0,0 +1,217 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/CacheDir.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+CacheDir support
+"""
+
+import os.path
+import stat
+import string
+import sys
+
+import SCons.Action
+
+cache_enabled = True
+cache_debug = False
+cache_force = False
+cache_show = False
+
+def CacheRetrieveFunc(target, source, env):
+ t = target[0]
+ fs = t.fs
+ cd = env.get_CacheDir()
+ cachedir, cachefile = cd.cachepath(t)
+ if not fs.exists(cachefile):
+ cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile)
+ return 1
+ cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile)
+ if SCons.Action.execute_actions:
+ if fs.islink(cachefile):
+ fs.symlink(fs.readlink(cachefile), t.path)
+ else:
+ env.copy_from_cache(cachefile, t.path)
+ st = fs.stat(cachefile)
+ fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ return 0
+
+def CacheRetrieveString(target, source, env):
+ t = target[0]
+ fs = t.fs
+ cd = env.get_CacheDir()
+ cachedir, cachefile = cd.cachepath(t)
+ if t.fs.exists(cachefile):
+ return "Retrieved `%s' from cache" % t.path
+ return None
+
+CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString)
+
+CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None)
+
+def CachePushFunc(target, source, env):
+ t = target[0]
+ if t.nocache:
+ return
+ fs = t.fs
+ cd = env.get_CacheDir()
+ cachedir, cachefile = cd.cachepath(t)
+ if fs.exists(cachefile):
+ # Don't bother copying it if it's already there. Note that
+ # usually this "shouldn't happen" because if the file already
+ # existed in cache, we'd have retrieved the file from there,
+ # not built it. This can happen, though, in a race, if some
+ # other person running the same build pushes their copy to
+ # the cache after we decide we need to build it but before our
+ # build completes.
+ cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile)
+ return
+
+ cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile)
+
+ tempfile = cachefile+'.tmp'+str(os.getpid())
+ errfmt = "Unable to copy %s to cache. Cache file is %s"
+
+ if not fs.isdir(cachedir):
+ try:
+ fs.makedirs(cachedir)
+ except EnvironmentError:
+ # We may have received an exception because another process
+ # has beaten us creating the directory.
+ if not fs.isdir(cachedir):
+ msg = errfmt % (str(target), cachefile)
+ raise SCons.Errors.EnvironmentError, msg
+
+ try:
+ if fs.islink(t.path):
+ fs.symlink(fs.readlink(t.path), tempfile)
+ else:
+ fs.copy2(t.path, tempfile)
+ fs.rename(tempfile, cachefile)
+ st = fs.stat(t.path)
+ fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ except EnvironmentError:
+ # It's possible someone else tried writing the file at the
+ # same time we did, or else that there was some problem like
+ # the CacheDir being on a separate file system that's full.
+ # In any case, inability to push a file to cache doesn't affect
+ # the correctness of the build, so just print a warning.
+ msg = errfmt % (str(target), cachefile)
+ SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg)
+
+CachePush = SCons.Action.Action(CachePushFunc, None)
+
+class CacheDir:
+
+ def __init__(self, path):
+ try:
+ import hashlib
+ except ImportError:
+ msg = "No hashlib or MD5 module available, CacheDir() not supported"
+ SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
+ self.path = None
+ else:
+ self.path = path
+ self.current_cache_debug = None
+ self.debugFP = None
+
+ def CacheDebug(self, fmt, target, cachefile):
+ if cache_debug != self.current_cache_debug:
+ if cache_debug == '-':
+ self.debugFP = sys.stdout
+ elif cache_debug:
+ self.debugFP = open(cache_debug, 'w')
+ else:
+ self.debugFP = None
+ self.current_cache_debug = cache_debug
+ if self.debugFP:
+ self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
+
+ def is_enabled(self):
+ return (cache_enabled and not self.path is None)
+
+ def cachepath(self, node):
+ """
+ """
+ if not self.is_enabled():
+ return None, None
+
+ sig = node.get_cachedir_bsig()
+ subdir = string.upper(sig[0])
+ dir = os.path.join(self.path, subdir)
+ return dir, os.path.join(dir, sig)
+
+ def retrieve(self, node):
+ """
+ This method is called from multiple threads in a parallel build,
+ so only do thread safe stuff here. Do thread unsafe stuff in
+ built().
+
+ Note that there's a special trick here with the execute flag
+ (one that's not normally done for other actions). Basically
+ if the user requested a no_exec (-n) build, then
+ SCons.Action.execute_actions is set to 0 and when any action
+ is called, it does its showing but then just returns zero
+ instead of actually calling the action execution operation.
+ The problem for caching is that if the file does NOT exist in
+ cache then the CacheRetrieveString won't return anything to
+ show for the task, but the Action.__call__ won't call
+ CacheRetrieveFunc; instead it just returns zero, which makes
+ the code below think that the file *was* successfully
+ retrieved from the cache, therefore it doesn't do any
+ subsequent building. However, the CacheRetrieveString didn't
+ print anything because it didn't actually exist in the cache,
+ and no more build actions will be performed, so the user just
+ sees nothing. The fix is to tell Action.__call__ to always
+ execute the CacheRetrieveFunc and then have the latter
+ explicitly check SCons.Action.execute_actions itself.
+ """
+ if not self.is_enabled():
+ return False
+
+ env = node.get_build_env()
+ if cache_show:
+ if CacheRetrieveSilent(node, [], env, execute=1) == 0:
+ node.build(presub=0, execute=0)
+ return True
+ else:
+ if CacheRetrieve(node, [], env, execute=1) == 0:
+ return True
+
+ return False
+
+ def push(self, node):
+ if not self.is_enabled():
+ return
+ return CachePush(node, [], node.get_build_env())
+
+ def push_if_forced(self, node):
+ if cache_force:
+ return self.push(node)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/CacheDirTests.py b/src/engine/SCons/CacheDirTests.py
new file mode 100644
index 0000000..0e28885
--- /dev/null
+++ b/src/engine/SCons/CacheDirTests.py
@@ -0,0 +1,297 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/CacheDirTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import shutil
+import sys
+import unittest
+
+from TestCmd import TestCmd
+
+import SCons.CacheDir
+
+built_it = None
+
+class Action:
+ def __call__(self, targets, sources, env, **kw):
+ global built_it
+ if kw.get('execute', 1):
+ built_it = 1
+ return 0
+ def genstring(self, target, source, env):
+ return str(self)
+ def get_contents(self, target, source, env):
+ return ''
+
+class Builder:
+ def __init__(self, environment, action):
+ self.env = environment
+ self.action = action
+ self.overrides = {}
+ self.source_scanner = None
+ self.target_scanner = None
+
+class Environment:
+ def __init__(self, cachedir):
+ self.cachedir = cachedir
+ def Override(self, overrides):
+ return self
+ def get_CacheDir(self):
+ return self.cachedir
+
+class BaseTestCase(unittest.TestCase):
+ """
+ Base fixtures common to our other unittest classes.
+ """
+ def setUp(self):
+ self.test = TestCmd(workdir='')
+
+ import SCons.Node.FS
+ self.fs = SCons.Node.FS.FS()
+
+ self._CacheDir = SCons.CacheDir.CacheDir('cache')
+
+ def File(self, name, bsig=None, action=Action()):
+ node = self.fs.File(name)
+ node.builder_set(Builder(Environment(self._CacheDir), action))
+ if bsig:
+ node.cachesig = bsig
+ #node.binfo = node.BuildInfo(node)
+ #node.binfo.ninfo.bsig = bsig
+ return node
+
+class CacheDirTestCase(BaseTestCase):
+ """
+ Test calling CacheDir code directly.
+ """
+ def test_cachepath(self):
+ """Test the cachepath() method"""
+
+ # Verify how the cachepath() method determines the name
+ # of the file in cache.
+ def my_collect(list):
+ return list[0]
+ save_collect = SCons.Util.MD5collect
+ SCons.Util.MD5collect = my_collect
+
+ try:
+ f5 = self.File("cd.f5", 'a_fake_bsig')
+ result = self._CacheDir.cachepath(f5)
+ dirname = os.path.join('cache', 'A')
+ filename = os.path.join(dirname, 'a_fake_bsig')
+ assert result == (dirname, filename), result
+ finally:
+ SCons.Util.MD5collect = save_collect
+
+class FileTestCase(BaseTestCase):
+ """
+ Test calling CacheDir code through Node.FS.File interfaces.
+ """
+ # These tests were originally in Nodes/FSTests.py and got moved
+ # when the CacheDir support was refactored into its own module.
+ # Look in the history for Node/FSTests.py if any of this needs
+ # to be re-examined.
+ def retrieve_succeed(self, target, source, env, execute=1):
+ self.retrieved.append(target)
+ return 0
+
+ def retrieve_fail(self, target, source, env, execute=1):
+ self.retrieved.append(target)
+ return 1
+
+ def push(self, target, source, env):
+ self.pushed.append(target)
+ return 0
+
+ def test_CacheRetrieve(self):
+ """Test the CacheRetrieve() function"""
+
+ save_CacheRetrieve = SCons.CacheDir.CacheRetrieve
+ self.retrieved = []
+
+ f1 = self.File("cd.f1")
+ try:
+ SCons.CacheDir.CacheRetrieve = self.retrieve_succeed
+ self.retrieved = []
+ built_it = None
+
+ r = f1.retrieve_from_cache()
+ assert r == 1, r
+ assert self.retrieved == [f1], self.retrieved
+ assert built_it is None, built_it
+
+ SCons.CacheDir.CacheRetrieve = self.retrieve_fail
+ self.retrieved = []
+ built_it = None
+
+ r = f1.retrieve_from_cache()
+ assert not r, r
+ assert self.retrieved == [f1], self.retrieved
+ assert built_it is None, built_it
+ finally:
+ SCons.CacheDir.CacheRetrieve = save_CacheRetrieve
+
+ def test_CacheRetrieveSilent(self):
+ """Test the CacheRetrieveSilent() function"""
+
+ save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent
+
+ SCons.CacheDir.cache_show = 1
+
+ f2 = self.File("cd.f2", 'f2_bsig')
+ try:
+ SCons.CacheDir.CacheRetrieveSilent = self.retrieve_succeed
+ self.retrieved = []
+ built_it = None
+
+ r = f2.retrieve_from_cache()
+ assert r == 1, r
+ assert self.retrieved == [f2], self.retrieved
+ assert built_it is None, built_it
+
+ SCons.CacheDir.CacheRetrieveSilent = self.retrieve_fail
+ self.retrieved = []
+ built_it = None
+
+ r = f2.retrieve_from_cache()
+ assert r is False, r
+ assert self.retrieved == [f2], self.retrieved
+ assert built_it is None, built_it
+ finally:
+ SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent
+
+ def test_CachePush(self):
+ """Test the CachePush() function"""
+
+ save_CachePush = SCons.CacheDir.CachePush
+
+ SCons.CacheDir.CachePush = self.push
+
+ try:
+ self.pushed = []
+
+ cd_f3 = self.test.workpath("cd.f3")
+ f3 = self.File(cd_f3)
+ f3.push_to_cache()
+ assert self.pushed == [], self.pushed
+ self.test.write(cd_f3, "cd.f3\n")
+ f3.push_to_cache()
+ assert self.pushed == [f3], self.pushed
+
+ self.pushed = []
+
+ cd_f4 = self.test.workpath("cd.f4")
+ f4 = self.File(cd_f4)
+ f4.visited()
+ assert self.pushed == [], self.pushed
+ self.test.write(cd_f4, "cd.f4\n")
+ f4.clear()
+ f4.visited()
+ assert self.pushed == [], self.pushed
+ SCons.CacheDir.cache_force = 1
+ f4.clear()
+ f4.visited()
+ assert self.pushed == [f4], self.pushed
+ finally:
+ SCons.CacheDir.CachePush = save_CachePush
+
+ def test_warning(self):
+ """Test raising a warning if we can't copy a file to cache."""
+
+ test = TestCmd(workdir='')
+
+ save_copy2 = shutil.copy2
+ def copy2(src, dst):
+ raise OSError
+ shutil.copy2 = copy2
+ save_mkdir = os.mkdir
+ def mkdir(dir, mode=0):
+ pass
+ os.mkdir = mkdir
+ old_warn_exceptions = SCons.Warnings.warningAsException(1)
+ SCons.Warnings.enableWarningClass(SCons.Warnings.CacheWriteErrorWarning)
+
+ try:
+ cd_f7 = self.test.workpath("cd.f7")
+ self.test.write(cd_f7, "cd.f7\n")
+ f7 = self.File(cd_f7, 'f7_bsig')
+
+ warn_caught = 0
+ try:
+ f7.push_to_cache()
+ except SCons.Errors.BuildError, e:
+ assert e.exc_info[0] == SCons.Warnings.CacheWriteErrorWarning
+ warn_caught = 1
+ assert warn_caught
+ finally:
+ shutil.copy2 = save_copy2
+ os.mkdir = save_mkdir
+ SCons.Warnings.warningAsException(old_warn_exceptions)
+ SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning)
+
+ def test_no_strfunction(self):
+ """Test handling no strfunction() for an action."""
+
+ save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent
+
+ f8 = self.File("cd.f8", 'f8_bsig')
+ try:
+ SCons.CacheDir.CacheRetrieveSilent = self.retrieve_succeed
+ self.retrieved = []
+ built_it = None
+
+ r = f8.retrieve_from_cache()
+ assert r == 1, r
+ assert self.retrieved == [f8], self.retrieved
+ assert built_it is None, built_it
+
+ SCons.CacheDir.CacheRetrieveSilent = self.retrieve_fail
+ self.retrieved = []
+ built_it = None
+
+ r = f8.retrieve_from_cache()
+ assert r is False, r
+ assert self.retrieved == [f8], self.retrieved
+ assert built_it is None, built_it
+ finally:
+ SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ CacheDirTestCase,
+ FileTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py
new file mode 100644
index 0000000..e995e77
--- /dev/null
+++ b/src/engine/SCons/Conftest.py
@@ -0,0 +1,794 @@
+"""SCons.Conftest
+
+Autoconf-like configuration support; low level implementation of tests.
+"""
+
+#
+# Copyright (c) 2003 Stichting NLnet Labs
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+#
+# The purpose of this module is to define how a check is to be performed.
+# Use one of the Check...() functions below.
+#
+
+#
+# A context class is used that defines functions for carrying out the tests,
+# logging and messages. The following methods and members must be present:
+#
+# context.Display(msg) Function called to print messages that are normally
+# displayed for the user. Newlines are explicitly used.
+# The text should also be written to the logfile!
+#
+# context.Log(msg) Function called to write to a log file.
+#
+# context.BuildProg(text, ext)
+# Function called to build a program, using "ext" for the
+# file extention. Must return an empty string for
+# success, an error message for failure.
+# For reliable test results building should be done just
+# like an actual program would be build, using the same
+# command and arguments (including configure results so
+# far).
+#
+# context.CompileProg(text, ext)
+# Function called to compile a program, using "ext" for
+# the file extention. Must return an empty string for
+# success, an error message for failure.
+# For reliable test results compiling should be done just
+# like an actual source file would be compiled, using the
+# same command and arguments (including configure results
+# so far).
+#
+# context.AppendLIBS(lib_name_list)
+# Append "lib_name_list" to the value of LIBS.
+# "lib_namelist" is a list of strings.
+# Return the value of LIBS before changing it (any type
+# can be used, it is passed to SetLIBS() later.)
+#
+# context.PrependLIBS(lib_name_list)
+# Prepend "lib_name_list" to the value of LIBS.
+# "lib_namelist" is a list of strings.
+# Return the value of LIBS before changing it (any type
+# can be used, it is passed to SetLIBS() later.)
+#
+# context.SetLIBS(value)
+# Set LIBS to "value". The type of "value" is what
+# AppendLIBS() returned.
+# Return the value of LIBS before changing it (any type
+# can be used, it is passed to SetLIBS() later.)
+#
+# context.headerfilename
+# Name of file to append configure results to, usually
+# "confdefs.h".
+# The file must not exist or be empty when starting.
+# Empty or None to skip this (some tests will not work!).
+#
+# context.config_h (may be missing). If present, must be a string, which
+# will be filled with the contents of a config_h file.
+#
+# context.vardict Dictionary holding variables used for the tests and
+# stores results from the tests, used for the build
+# commands.
+# Normally contains "CC", "LIBS", "CPPFLAGS", etc.
+#
+# context.havedict Dictionary holding results from the tests that are to
+# be used inside a program.
+# Names often start with "HAVE_". These are zero
+# (feature not present) or one (feature present). Other
+# variables may have any value, e.g., "PERLVERSION" can
+# be a number and "SYSTEMNAME" a string.
+#
+
+import re
+import string
+from types import IntType
+
+#
+# PUBLIC VARIABLES
+#
+
+LogInputFiles = 1 # Set that to log the input files in case of a failed test
+LogErrorMessages = 1 # Set that to log Conftest-generated error messages
+
+#
+# PUBLIC FUNCTIONS
+#
+
+# Generic remarks:
+# - When a language is specified which is not supported the test fails. The
+# message is a bit different, because not all the arguments for the normal
+# message are available yet (chicken-egg problem).
+
+
+def CheckBuilder(context, text = None, language = None):
+ """
+ Configure check to see if the compiler works.
+ Note that this uses the current value of compiler and linker flags, make
+ sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+ "language" should be "C" or "C++" and is used to select the compiler.
+ Default is "C".
+ "text" may be used to specify the code to be build.
+ Returns an empty string for success, an error message for failure.
+ """
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("%s\n" % msg)
+ return msg
+
+ if not text:
+ text = """
+int main() {
+ return 0;
+}
+"""
+
+ context.Display("Checking if building a %s file works... " % lang)
+ ret = context.BuildProg(text, suffix)
+ _YesNoResult(context, ret, None, text)
+ return ret
+
+def CheckCC(context):
+ """
+ Configure check for a working C compiler.
+
+ This checks whether the C compiler, as defined in the $CC construction
+ variable, can compile a C source file. It uses the current $CCCOM value
+ too, so that it can test against non working flags.
+
+ """
+ context.Display("Checking whether the C compiler works")
+ text = """
+int main()
+{
+ return 0;
+}
+"""
+ ret = _check_empty_program(context, 'CC', text, 'C')
+ _YesNoResult(context, ret, None, text)
+ return ret
+
+def CheckSHCC(context):
+ """
+ Configure check for a working shared C compiler.
+
+ This checks whether the C compiler, as defined in the $SHCC construction
+ variable, can compile a C source file. It uses the current $SHCCCOM value
+ too, so that it can test against non working flags.
+
+ """
+ context.Display("Checking whether the (shared) C compiler works")
+ text = """
+int foo()
+{
+ return 0;
+}
+"""
+ ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True)
+ _YesNoResult(context, ret, None, text)
+ return ret
+
+def CheckCXX(context):
+ """
+ Configure check for a working CXX compiler.
+
+ This checks whether the CXX compiler, as defined in the $CXX construction
+ variable, can compile a CXX source file. It uses the current $CXXCOM value
+ too, so that it can test against non working flags.
+
+ """
+ context.Display("Checking whether the C++ compiler works")
+ text = """
+int main()
+{
+ return 0;
+}
+"""
+ ret = _check_empty_program(context, 'CXX', text, 'C++')
+ _YesNoResult(context, ret, None, text)
+ return ret
+
+def CheckSHCXX(context):
+ """
+ Configure check for a working shared CXX compiler.
+
+ This checks whether the CXX compiler, as defined in the $SHCXX construction
+ variable, can compile a CXX source file. It uses the current $SHCXXCOM value
+ too, so that it can test against non working flags.
+
+ """
+ context.Display("Checking whether the (shared) C++ compiler works")
+ text = """
+int main()
+{
+ return 0;
+}
+"""
+ ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True)
+ _YesNoResult(context, ret, None, text)
+ return ret
+
+def _check_empty_program(context, comp, text, language, use_shared = False):
+ """Return 0 on success, 1 otherwise."""
+ if not context.env.has_key(comp) or not context.env[comp]:
+ # The compiler construction variable is not set or empty
+ return 1
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ return 1
+
+ if use_shared:
+ return context.CompileSharedObject(text, suffix)
+ else:
+ return context.CompileProg(text, suffix)
+
+
+def CheckFunc(context, function_name, header = None, language = None):
+ """
+ Configure check for a function "function_name".
+ "language" should be "C" or "C++" and is used to select the compiler.
+ Default is "C".
+ Optional "header" can be defined to define a function prototype, include a
+ header file or anything else that comes before main().
+ Sets HAVE_function_name in context.havedict according to the result.
+ Note that this uses the current value of compiler and linker flags, make
+ sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+ Returns an empty string for success, an error message for failure.
+ """
+
+ # Remarks from autoconf:
+ # - Don't include <ctype.h> because on OSF/1 3.0 it includes <sys/types.h>
+ # which includes <sys/select.h> which contains a prototype for select.
+ # Similarly for bzero.
+ # - assert.h is included to define __stub macros and hopefully few
+ # prototypes, which can conflict with char $1(); below.
+ # - Override any gcc2 internal prototype to avoid an error.
+ # - We use char for the function declaration because int might match the
+ # return type of a gcc2 builtin and then its argument prototype would
+ # still apply.
+ # - The GNU C library defines this for functions which it implements to
+ # always fail with ENOSYS. Some functions are actually named something
+ # starting with __ and the normal name is an alias.
+
+ if context.headerfilename:
+ includetext = '#include "%s"' % context.headerfilename
+ else:
+ includetext = ''
+ if not header:
+ header = """
+#ifdef __cplusplus
+extern "C"
+#endif
+char %s();""" % function_name
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for %s(): %s\n" % (function_name, msg))
+ return msg
+
+ text = """
+%(include)s
+#include <assert.h>
+%(hdr)s
+
+int main() {
+#if defined (__stub_%(name)s) || defined (__stub___%(name)s)
+ fail fail fail
+#else
+ %(name)s();
+#endif
+
+ return 0;
+}
+""" % { 'name': function_name,
+ 'include': includetext,
+ 'hdr': header }
+
+ context.Display("Checking for %s function %s()... " % (lang, function_name))
+ ret = context.BuildProg(text, suffix)
+ _YesNoResult(context, ret, "HAVE_" + function_name, text,
+ "Define to 1 if the system has the function `%s'." %\
+ function_name)
+ return ret
+
+
+def CheckHeader(context, header_name, header = None, language = None,
+ include_quotes = None):
+ """
+ Configure check for a C or C++ header file "header_name".
+ Optional "header" can be defined to do something before including the
+ header file (unusual, supported for consistency).
+ "language" should be "C" or "C++" and is used to select the compiler.
+ Default is "C".
+ Sets HAVE_header_name in context.havedict according to the result.
+ Note that this uses the current value of compiler and linker flags, make
+ sure $CFLAGS and $CPPFLAGS are set correctly.
+ Returns an empty string for success, an error message for failure.
+ """
+ # Why compile the program instead of just running the preprocessor?
+ # It is possible that the header file exists, but actually using it may
+ # fail (e.g., because it depends on other header files). Thus this test is
+ # more strict. It may require using the "header" argument.
+ #
+ # Use <> by default, because the check is normally used for system header
+ # files. SCons passes '""' to overrule this.
+
+ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+ if context.headerfilename:
+ includetext = '#include "%s"\n' % context.headerfilename
+ else:
+ includetext = ''
+ if not header:
+ header = ""
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for header file %s: %s\n"
+ % (header_name, msg))
+ return msg
+
+ if not include_quotes:
+ include_quotes = "<>"
+
+ text = "%s%s\n#include %s%s%s\n\n" % (includetext, header,
+ include_quotes[0], header_name, include_quotes[1])
+
+ context.Display("Checking for %s header file %s... " % (lang, header_name))
+ ret = context.CompileProg(text, suffix)
+ _YesNoResult(context, ret, "HAVE_" + header_name, text,
+ "Define to 1 if you have the <%s> header file." % header_name)
+ return ret
+
+
+def CheckType(context, type_name, fallback = None,
+ header = None, language = None):
+ """
+ Configure check for a C or C++ type "type_name".
+ Optional "header" can be defined to include a header file.
+ "language" should be "C" or "C++" and is used to select the compiler.
+ Default is "C".
+ Sets HAVE_type_name in context.havedict according to the result.
+ Note that this uses the current value of compiler and linker flags, make
+ sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+ Returns an empty string for success, an error message for failure.
+ """
+
+ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+ if context.headerfilename:
+ includetext = '#include "%s"' % context.headerfilename
+ else:
+ includetext = ''
+ if not header:
+ header = ""
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for %s type: %s\n" % (type_name, msg))
+ return msg
+
+ # Remarks from autoconf about this test:
+ # - Grepping for the type in include files is not reliable (grep isn't
+ # portable anyway).
+ # - Using "TYPE my_var;" doesn't work for const qualified types in C++.
+ # Adding an initializer is not valid for some C++ classes.
+ # - Using the type as parameter to a function either fails for K&$ C or for
+ # C++.
+ # - Using "TYPE *my_var;" is valid in C for some types that are not
+ # declared (struct something).
+ # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable.
+ # - Using the previous two together works reliably.
+ text = """
+%(include)s
+%(header)s
+
+int main() {
+ if ((%(name)s *) 0)
+ return 0;
+ if (sizeof (%(name)s))
+ return 0;
+}
+""" % { 'include': includetext,
+ 'header': header,
+ 'name': type_name }
+
+ context.Display("Checking for %s type %s... " % (lang, type_name))
+ ret = context.BuildProg(text, suffix)
+ _YesNoResult(context, ret, "HAVE_" + type_name, text,
+ "Define to 1 if the system has the type `%s'." % type_name)
+ if ret and fallback and context.headerfilename:
+ f = open(context.headerfilename, "a")
+ f.write("typedef %s %s;\n" % (fallback, type_name))
+ f.close()
+
+ return ret
+
+def CheckTypeSize(context, type_name, header = None, language = None, expect = None):
+ """This check can be used to get the size of a given type, or to check whether
+ the type is of expected size.
+
+ Arguments:
+ - type : str
+ the type to check
+ - includes : sequence
+ list of headers to include in the test code before testing the type
+ - language : str
+ 'C' or 'C++'
+ - expect : int
+ if given, will test wether the type has the given number of bytes.
+ If not given, will automatically find the size.
+
+ Returns:
+ status : int
+ 0 if the check failed, or the found size of the type if the check succeeded."""
+
+ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+ if context.headerfilename:
+ includetext = '#include "%s"' % context.headerfilename
+ else:
+ includetext = ''
+
+ if not header:
+ header = ""
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for %s type: %s\n" % (type_name, msg))
+ return msg
+
+ src = includetext + header
+ if not expect is None:
+ # Only check if the given size is the right one
+ context.Display('Checking %s is %d bytes... ' % (type_name, expect))
+
+ # test code taken from autoconf: this is a pretty clever hack to find that
+ # a type is of a given size using only compilation. This speeds things up
+ # quite a bit compared to straightforward code using TryRun
+ src = src + r"""
+typedef %s scons_check_type;
+
+int main()
+{
+ static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)];
+ test_array[0] = 0;
+
+ return 0;
+}
+"""
+
+ st = context.CompileProg(src % (type_name, expect), suffix)
+ if not st:
+ context.Display("yes\n")
+ _Have(context, "SIZEOF_%s" % type_name, expect,
+ "The size of `%s', as computed by sizeof." % type_name)
+ return expect
+ else:
+ context.Display("no\n")
+ _LogFailed(context, src, st)
+ return 0
+ else:
+ # Only check if the given size is the right one
+ context.Message('Checking size of %s ... ' % type_name)
+
+ # We have to be careful with the program we wish to test here since
+ # compilation will be attempted using the current environment's flags.
+ # So make sure that the program will compile without any warning. For
+ # example using: 'int main(int argc, char** argv)' will fail with the
+ # '-Wall -Werror' flags since the variables argc and argv would not be
+ # used in the program...
+ #
+ src = src + """
+#include <stdlib.h>
+#include <stdio.h>
+int main() {
+ printf("%d", (int)sizeof(""" + type_name + """));
+ return 0;
+}
+ """
+ st, out = context.RunProg(src, suffix)
+ try:
+ size = int(out)
+ except ValueError:
+ # If cannot convert output of test prog to an integer (the size),
+ # something went wront, so just fail
+ st = 1
+ size = 0
+
+ if not st:
+ context.Display("yes\n")
+ _Have(context, "SIZEOF_%s" % type_name, size,
+ "The size of `%s', as computed by sizeof." % type_name)
+ return size
+ else:
+ context.Display("no\n")
+ _LogFailed(context, src, st)
+ return 0
+
+ return 0
+
+def CheckDeclaration(context, symbol, includes = None, language = None):
+ """Checks whether symbol is declared.
+
+ Use the same test as autoconf, that is test whether the symbol is defined
+ as a macro or can be used as an r-value.
+
+ Arguments:
+ symbol : str
+ the symbol to check
+ includes : str
+ Optional "header" can be defined to include a header file.
+ language : str
+ only C and C++ supported.
+
+ Returns:
+ status : bool
+ True if the check failed, False if succeeded."""
+
+ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+ if context.headerfilename:
+ includetext = '#include "%s"' % context.headerfilename
+ else:
+ includetext = ''
+
+ if not includes:
+ includes = ""
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for declaration %s: %s\n" % (type_name, msg))
+ return msg
+
+ src = includetext + includes
+ context.Display('Checking whether %s is declared... ' % symbol)
+
+ src = src + r"""
+int main()
+{
+#ifndef %s
+ (void) %s;
+#endif
+ ;
+ return 0;
+}
+""" % (symbol, symbol)
+
+ st = context.CompileProg(src, suffix)
+ _YesNoResult(context, st, "HAVE_DECL_" + symbol, src,
+ "Set to 1 if %s is defined." % symbol)
+ return st
+
+def CheckLib(context, libs, func_name = None, header = None,
+ extra_libs = None, call = None, language = None, autoadd = 1,
+ append = True):
+ """
+ Configure check for a C or C++ libraries "libs". Searches through
+ the list of libraries, until one is found where the test succeeds.
+ Tests if "func_name" or "call" exists in the library. Note: if it exists
+ in another library the test succeeds anyway!
+ Optional "header" can be defined to include a header file. If not given a
+ default prototype for "func_name" is added.
+ Optional "extra_libs" is a list of library names to be added after
+ "lib_name" in the build command. To be used for libraries that "lib_name"
+ depends on.
+ Optional "call" replaces the call to "func_name" in the test code. It must
+ consist of complete C statements, including a trailing ";".
+ Both "func_name" and "call" arguments are optional, and in that case, just
+ linking against the libs is tested.
+ "language" should be "C" or "C++" and is used to select the compiler.
+ Default is "C".
+ Note that this uses the current value of compiler and linker flags, make
+ sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+ Returns an empty string for success, an error message for failure.
+ """
+ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+ if context.headerfilename:
+ includetext = '#include "%s"' % context.headerfilename
+ else:
+ includetext = ''
+ if not header:
+ header = ""
+
+ text = """
+%s
+%s""" % (includetext, header)
+
+ # Add a function declaration if needed.
+ if func_name and func_name != "main":
+ if not header:
+ text = text + """
+#ifdef __cplusplus
+extern "C"
+#endif
+char %s();
+""" % func_name
+
+ # The actual test code.
+ if not call:
+ call = "%s();" % func_name
+
+ # if no function to test, leave main() blank
+ text = text + """
+int
+main() {
+ %s
+return 0;
+}
+""" % (call or "")
+
+ if call:
+ i = string.find(call, "\n")
+ if i > 0:
+ calltext = call[:i] + ".."
+ elif call[-1] == ';':
+ calltext = call[:-1]
+ else:
+ calltext = call
+
+ for lib_name in libs:
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for library %s: %s\n" % (lib_name, msg))
+ return msg
+
+ # if a function was specified to run in main(), say it
+ if call:
+ context.Display("Checking for %s in %s library %s... "
+ % (calltext, lang, lib_name))
+ # otherwise, just say the name of library and language
+ else:
+ context.Display("Checking for %s library %s... "
+ % (lang, lib_name))
+
+ if lib_name:
+ l = [ lib_name ]
+ if extra_libs:
+ l.extend(extra_libs)
+ if append:
+ oldLIBS = context.AppendLIBS(l)
+ else:
+ oldLIBS = context.PrependLIBS(l)
+ sym = "HAVE_LIB" + lib_name
+ else:
+ oldLIBS = -1
+ sym = None
+
+ ret = context.BuildProg(text, suffix)
+
+ _YesNoResult(context, ret, sym, text,
+ "Define to 1 if you have the `%s' library." % lib_name)
+ if oldLIBS != -1 and (ret or not autoadd):
+ context.SetLIBS(oldLIBS)
+
+ if not ret:
+ return ret
+
+ return ret
+
+#
+# END OF PUBLIC FUNCTIONS
+#
+
+def _YesNoResult(context, ret, key, text, comment = None):
+ """
+ Handle the result of a test with a "yes" or "no" result.
+ "ret" is the return value: empty if OK, error message when not.
+ "key" is the name of the symbol to be defined (HAVE_foo).
+ "text" is the source code of the program used for testing.
+ "comment" is the C comment to add above the line defining the symbol (the
+ comment is automatically put inside a /* */). If None, no comment is added.
+ """
+ if key:
+ _Have(context, key, not ret, comment)
+ if ret:
+ context.Display("no\n")
+ _LogFailed(context, text, ret)
+ else:
+ context.Display("yes\n")
+
+
+def _Have(context, key, have, comment = None):
+ """
+ Store result of a test in context.havedict and context.headerfilename.
+ "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non-
+ alphanumerics are replaced by an underscore.
+ The value of "have" can be:
+ 1 - Feature is defined, add "#define key".
+ 0 - Feature is not defined, add "/* #undef key */".
+ Adding "undef" is what autoconf does. Not useful for the
+ compiler, but it shows that the test was done.
+ number - Feature is defined to this number "#define key have".
+ Doesn't work for 0 or 1, use a string then.
+ string - Feature is defined to this string "#define key have".
+ Give "have" as is should appear in the header file, include quotes
+ when desired and escape special characters!
+ """
+ key_up = string.upper(key)
+ key_up = re.sub('[^A-Z0-9_]', '_', key_up)
+ context.havedict[key_up] = have
+ if have == 1:
+ line = "#define %s 1\n" % key_up
+ elif have == 0:
+ line = "/* #undef %s */\n" % key_up
+ elif type(have) == IntType:
+ line = "#define %s %d\n" % (key_up, have)
+ else:
+ line = "#define %s %s\n" % (key_up, str(have))
+
+ if comment is not None:
+ lines = "\n/* %s */\n" % comment + line
+ else:
+ lines = "\n" + line
+
+ if context.headerfilename:
+ f = open(context.headerfilename, "a")
+ f.write(lines)
+ f.close()
+ elif hasattr(context,'config_h'):
+ context.config_h = context.config_h + lines
+
+
+def _LogFailed(context, text, msg):
+ """
+ Write to the log about a failed program.
+ Add line numbers, so that error messages can be understood.
+ """
+ if LogInputFiles:
+ context.Log("Failed program was:\n")
+ lines = string.split(text, '\n')
+ if len(lines) and lines[-1] == '':
+ lines = lines[:-1] # remove trailing empty line
+ n = 1
+ for line in lines:
+ context.Log("%d: %s\n" % (n, line))
+ n = n + 1
+ if LogErrorMessages:
+ context.Log("Error message: %s\n" % msg)
+
+
+def _lang2suffix(lang):
+ """
+ Convert a language name to a suffix.
+ When "lang" is empty or None C is assumed.
+ Returns a tuple (lang, suffix, None) when it works.
+ For an unrecognized language returns (None, None, msg).
+ Where:
+ lang = the unified language name
+ suffix = the suffix, including the leading dot
+ msg = an error message
+ """
+ if not lang or lang in ["C", "c"]:
+ return ("C", ".c", None)
+ if lang in ["c++", "C++", "cpp", "CXX", "cxx"]:
+ return ("C++", ".cpp", None)
+
+ return None, None, "Unsupported language: %s" % lang
+
+
+# vim: set sw=4 et sts=4 tw=79 fo+=l:
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Debug.py b/src/engine/SCons/Debug.py
new file mode 100644
index 0000000..5536169
--- /dev/null
+++ b/src/engine/SCons/Debug.py
@@ -0,0 +1,237 @@
+"""SCons.Debug
+
+Code for debugging SCons internal things. Not everything here is
+guaranteed to work all the way back to Python 1.5.2, and shouldn't be
+needed by most users.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Debug.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import string
+import sys
+import time
+
+# Recipe 14.10 from the Python Cookbook.
+try:
+ import weakref
+except ImportError:
+ def logInstanceCreation(instance, name=None):
+ pass
+else:
+ def logInstanceCreation(instance, name=None):
+ if name is None:
+ name = instance.__class__.__name__
+ if not tracked_classes.has_key(name):
+ tracked_classes[name] = []
+ tracked_classes[name].append(weakref.ref(instance))
+
+
+
+tracked_classes = {}
+
+def string_to_classes(s):
+ if s == '*':
+ c = tracked_classes.keys()
+ c.sort()
+ return c
+ else:
+ return string.split(s)
+
+def fetchLoggedInstances(classes="*"):
+ classnames = string_to_classes(classes)
+ return map(lambda cn: (cn, len(tracked_classes[cn])), classnames)
+
+def countLoggedInstances(classes, file=sys.stdout):
+ for classname in string_to_classes(classes):
+ file.write("%s: %d\n" % (classname, len(tracked_classes[classname])))
+
+def listLoggedInstances(classes, file=sys.stdout):
+ for classname in string_to_classes(classes):
+ file.write('\n%s:\n' % classname)
+ for ref in tracked_classes[classname]:
+ obj = ref()
+ if obj is not None:
+ file.write(' %s\n' % repr(obj))
+
+def dumpLoggedInstances(classes, file=sys.stdout):
+ for classname in string_to_classes(classes):
+ file.write('\n%s:\n' % classname)
+ for ref in tracked_classes[classname]:
+ obj = ref()
+ if obj is not None:
+ file.write(' %s:\n' % obj)
+ for key, value in obj.__dict__.items():
+ file.write(' %20s : %s\n' % (key, value))
+
+
+
+if sys.platform[:5] == "linux":
+ # Linux doesn't actually support memory usage stats from getrusage().
+ def memory():
+ mstr = open('/proc/self/stat').read()
+ mstr = string.split(mstr)[22]
+ return int(mstr)
+else:
+ try:
+ import resource
+ except ImportError:
+ try:
+ import win32process
+ import win32api
+ except ImportError:
+ def memory():
+ return 0
+ else:
+ def memory():
+ process_handle = win32api.GetCurrentProcess()
+ memory_info = win32process.GetProcessMemoryInfo( process_handle )
+ return memory_info['PeakWorkingSetSize']
+ else:
+ def memory():
+ res = resource.getrusage(resource.RUSAGE_SELF)
+ return res[4]
+
+# returns caller's stack
+def caller_stack(*backlist):
+ import traceback
+ if not backlist:
+ backlist = [0]
+ result = []
+ for back in backlist:
+ tb = traceback.extract_stack(limit=3+back)
+ key = tb[0][:3]
+ result.append('%s:%d(%s)' % func_shorten(key))
+ return result
+
+caller_bases = {}
+caller_dicts = {}
+
+# trace a caller's stack
+def caller_trace(back=0):
+ import traceback
+ tb = traceback.extract_stack(limit=3+back)
+ tb.reverse()
+ callee = tb[1][:3]
+ caller_bases[callee] = caller_bases.get(callee, 0) + 1
+ for caller in tb[2:]:
+ caller = callee + caller[:3]
+ try:
+ entry = caller_dicts[callee]
+ except KeyError:
+ caller_dicts[callee] = entry = {}
+ entry[caller] = entry.get(caller, 0) + 1
+ callee = caller
+
+# print a single caller and its callers, if any
+def _dump_one_caller(key, file, level=0):
+ l = []
+ for c,v in caller_dicts[key].items():
+ l.append((-v,c))
+ l.sort()
+ leader = ' '*level
+ for v,c in l:
+ file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:])))
+ if caller_dicts.has_key(c):
+ _dump_one_caller(c, file, level+1)
+
+# print each call tree
+def dump_caller_counts(file=sys.stdout):
+ keys = caller_bases.keys()
+ keys.sort()
+ for k in keys:
+ file.write("Callers of %s:%d(%s), %d calls:\n"
+ % (func_shorten(k) + (caller_bases[k],)))
+ _dump_one_caller(k, file)
+
+shorten_list = [
+ ( '/scons/SCons/', 1),
+ ( '/src/engine/SCons/', 1),
+ ( '/usr/lib/python', 0),
+]
+
+if os.sep != '/':
+ def platformize(t):
+ return (string.replace(t[0], '/', os.sep), t[1])
+ shorten_list = map(platformize, shorten_list)
+ del platformize
+
+def func_shorten(func_tuple):
+ f = func_tuple[0]
+ for t in shorten_list:
+ i = string.find(f, t[0])
+ if i >= 0:
+ if t[1]:
+ i = i + len(t[0])
+ return (f[i:],)+func_tuple[1:]
+ return func_tuple
+
+
+TraceFP = {}
+if sys.platform == 'win32':
+ TraceDefault = 'con'
+else:
+ TraceDefault = '/dev/tty'
+
+TimeStampDefault = None
+StartTime = time.time()
+PreviousTime = StartTime
+
+def Trace(msg, file=None, mode='w', tstamp=None):
+ """Write a trace message to a file. Whenever a file is specified,
+ it becomes the default for the next call to Trace()."""
+ global TraceDefault
+ global TimeStamp
+ global PreviousTime
+ if file is None:
+ file = TraceDefault
+ else:
+ TraceDefault = file
+ if tstamp is None:
+ tstamp = TimeStampDefault
+ else:
+ TimeStampDefault = tstamp
+ try:
+ fp = TraceFP[file]
+ except KeyError:
+ try:
+ fp = TraceFP[file] = open(file, mode)
+ except TypeError:
+ # Assume we were passed an open file pointer.
+ fp = file
+ if tstamp:
+ now = time.time()
+ fp.write('%8.4f %8.4f: ' % (now - StartTime, now - PreviousTime))
+ PreviousTime = now
+ fp.write(msg)
+ fp.flush()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
new file mode 100644
index 0000000..0ff0399
--- /dev/null
+++ b/src/engine/SCons/Defaults.py
@@ -0,0 +1,485 @@
+"""SCons.Defaults
+
+Builders and other things for the local site. Here's where we'll
+duplicate the functionality of autoconf until we move it into the
+installation procedure or use something like qmconf.
+
+The code that reads the registry to find MSVC components was borrowed
+from distutils.msvccompiler.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Defaults.py 4577 2009/12/27 19:44:43 scons"
+
+
+
+import os
+import os.path
+import errno
+import shutil
+import stat
+import string
+import time
+import types
+import sys
+
+import SCons.Action
+import SCons.Builder
+import SCons.CacheDir
+import SCons.Environment
+import SCons.PathList
+import SCons.Subst
+import SCons.Tool
+
+# A placeholder for a default Environment (for fetching source files
+# from source code management systems and the like). This must be
+# initialized later, after the top-level directory is set by the calling
+# interface.
+_default_env = None
+
+# Lazily instantiate the default environment so the overhead of creating
+# it doesn't apply when it's not needed.
+def _fetch_DefaultEnvironment(*args, **kw):
+ """
+ Returns the already-created default construction environment.
+ """
+ global _default_env
+ return _default_env
+
+def DefaultEnvironment(*args, **kw):
+ """
+ Initial public entry point for creating the default construction
+ Environment.
+
+ After creating the environment, we overwrite our name
+ (DefaultEnvironment) with the _fetch_DefaultEnvironment() function,
+ which more efficiently returns the initialized default construction
+ environment without checking for its existence.
+
+ (This function still exists with its _default_check because someone
+ else (*cough* Script/__init__.py *cough*) may keep a reference
+ to this function. So we can't use the fully functional idiom of
+ having the name originally be a something that *only* creates the
+ construction environment and then overwrites the name.)
+ """
+ global _default_env
+ if not _default_env:
+ import SCons.Util
+ _default_env = apply(SCons.Environment.Environment, args, kw)
+ if SCons.Util.md5:
+ _default_env.Decider('MD5')
+ else:
+ _default_env.Decider('timestamp-match')
+ global DefaultEnvironment
+ DefaultEnvironment = _fetch_DefaultEnvironment
+ _default_env._CacheDir_path = None
+ return _default_env
+
+# Emitters for setting the shared attribute on object files,
+# and an action for checking that all of the source files
+# going into a shared library are, in fact, shared.
+def StaticObjectEmitter(target, source, env):
+ for tgt in target:
+ tgt.attributes.shared = None
+ return (target, source)
+
+def SharedObjectEmitter(target, source, env):
+ for tgt in target:
+ tgt.attributes.shared = 1
+ return (target, source)
+
+def SharedFlagChecker(source, target, env):
+ same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
+ if same == '0' or same == '' or same == 'False':
+ for src in source:
+ try:
+ shared = src.attributes.shared
+ except AttributeError:
+ shared = None
+ if not shared:
+ raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])
+
+SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
+
+# Some people were using these variable name before we made
+# SourceFileScanner part of the public interface. Don't break their
+# SConscript files until we've given them some fair warning and a
+# transition period.
+CScan = SCons.Tool.CScanner
+DScan = SCons.Tool.DScanner
+LaTeXScan = SCons.Tool.LaTeXScanner
+ObjSourceScan = SCons.Tool.SourceFileScanner
+ProgScan = SCons.Tool.ProgramScanner
+
+# These aren't really tool scanners, so they don't quite belong with
+# the rest of those in Tool/__init__.py, but I'm not sure where else
+# they should go. Leave them here for now.
+import SCons.Scanner.Dir
+DirScanner = SCons.Scanner.Dir.DirScanner()
+DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
+
+# Actions for common languages.
+CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
+ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
+CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
+ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
+
+ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
+ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
+
+LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
+ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
+
+LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
+
+# Common tasks that we allow users to perform in platform-independent
+# ways by creating ActionFactory instances.
+ActionFactory = SCons.Action.ActionFactory
+
+def get_paths_str(dest):
+ # If dest is a list, we need to manually call str() on each element
+ if SCons.Util.is_List(dest):
+ elem_strs = []
+ for element in dest:
+ elem_strs.append('"' + str(element) + '"')
+ return '[' + string.join(elem_strs, ', ') + ']'
+ else:
+ return '"' + str(dest) + '"'
+
+def chmod_func(dest, mode):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for element in dest:
+ os.chmod(str(element), mode)
+
+def chmod_strfunc(dest, mode):
+ return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode)
+
+Chmod = ActionFactory(chmod_func, chmod_strfunc)
+
+def copy_func(dest, src):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if SCons.Util.is_List(src) and os.path.isdir(dest):
+ for file in src:
+ shutil.copy2(file, dest)
+ return 0
+ elif os.path.isfile(src):
+ return shutil.copy2(src, dest)
+ else:
+ return shutil.copytree(src, dest, 1)
+
+Copy = ActionFactory(copy_func,
+ lambda dest, src: 'Copy("%s", "%s")' % (dest, src),
+ convert=str)
+
+def delete_func(dest, must_exist=0):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for entry in dest:
+ entry = str(entry)
+ if not must_exist and not os.path.exists(entry):
+ continue
+ if not os.path.exists(entry) or os.path.isfile(entry):
+ os.unlink(entry)
+ continue
+ else:
+ shutil.rmtree(entry, 1)
+ continue
+
+def delete_strfunc(dest, must_exist=0):
+ return 'Delete(%s)' % get_paths_str(dest)
+
+Delete = ActionFactory(delete_func, delete_strfunc)
+
+def mkdir_func(dest):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for entry in dest:
+ try:
+ os.makedirs(str(entry))
+ except os.error, e:
+ p = str(entry)
+ if (e[0] == errno.EEXIST or (sys.platform=='win32' and e[0]==183)) \
+ and os.path.isdir(str(entry)):
+ pass # not an error if already exists
+ else:
+ raise
+
+Mkdir = ActionFactory(mkdir_func,
+ lambda dir: 'Mkdir(%s)' % get_paths_str(dir))
+
+def move_func(dest, src):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ SCons.Node.FS.invalidate_node_memos(src)
+ shutil.move(src, dest)
+
+Move = ActionFactory(move_func,
+ lambda dest, src: 'Move("%s", "%s")' % (dest, src),
+ convert=str)
+
+def touch_func(dest):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for file in dest:
+ file = str(file)
+ mtime = int(time.time())
+ if os.path.exists(file):
+ atime = os.path.getatime(file)
+ else:
+ open(file, 'w')
+ atime = mtime
+ os.utime(file, (atime, mtime))
+
+Touch = ActionFactory(touch_func,
+ lambda file: 'Touch(%s)' % get_paths_str(file))
+
+# Internal utility functions
+
+def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
+ """
+ Creates a new list from 'list' by first interpolating each element
+ in the list using the 'env' dictionary and then calling f on the
+ list, and finally calling _concat_ixes to concatenate 'prefix' and
+ 'suffix' onto each element of the list.
+ """
+ if not list:
+ return list
+
+ l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
+ if l is not None:
+ list = l
+
+ return _concat_ixes(prefix, list, suffix, env)
+
+def _concat_ixes(prefix, list, suffix, env):
+ """
+ Creates a new list from 'list' by concatenating the 'prefix' and
+ 'suffix' arguments onto each element of the list. A trailing space
+ on 'prefix' or leading space on 'suffix' will cause them to be put
+ into separate list elements rather than being concatenated.
+ """
+
+ result = []
+
+ # ensure that prefix and suffix are strings
+ prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
+ suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
+
+ for x in list:
+ if isinstance(x, SCons.Node.FS.File):
+ result.append(x)
+ continue
+ x = str(x)
+ if x:
+
+ if prefix:
+ if prefix[-1] == ' ':
+ result.append(prefix[:-1])
+ elif x[:len(prefix)] != prefix:
+ x = prefix + x
+
+ result.append(x)
+
+ if suffix:
+ if suffix[0] == ' ':
+ result.append(suffix[1:])
+ elif x[-len(suffix):] != suffix:
+ result[-1] = result[-1]+suffix
+
+ return result
+
+def _stripixes(prefix, list, suffix, stripprefixes, stripsuffixes, env, c=None):
+ """
+ This is a wrapper around _concat()/_concat_ixes() that checks for the
+ existence of prefixes or suffixes on list elements and strips them
+ where it finds them. This is used by tools (like the GNU linker)
+ that need to turn something like 'libfoo.a' into '-lfoo'.
+ """
+
+ if not list:
+ return list
+
+ if not callable(c):
+ env_c = env['_concat']
+ if env_c != _concat and callable(env_c):
+ # There's a custom _concat() method in the construction
+ # environment, and we've allowed people to set that in
+ # the past (see test/custom-concat.py), so preserve the
+ # backwards compatibility.
+ c = env_c
+ else:
+ c = _concat_ixes
+
+ stripprefixes = map(env.subst, SCons.Util.flatten(stripprefixes))
+ stripsuffixes = map(env.subst, SCons.Util.flatten(stripsuffixes))
+
+ stripped = []
+ for l in SCons.PathList.PathList(list).subst_path(env, None, None):
+ if isinstance(l, SCons.Node.FS.File):
+ stripped.append(l)
+ continue
+
+ if not SCons.Util.is_String(l):
+ l = str(l)
+
+ for stripprefix in stripprefixes:
+ lsp = len(stripprefix)
+ if l[:lsp] == stripprefix:
+ l = l[lsp:]
+ # Do not strip more than one prefix
+ break
+
+ for stripsuffix in stripsuffixes:
+ lss = len(stripsuffix)
+ if l[-lss:] == stripsuffix:
+ l = l[:-lss]
+ # Do not strip more than one suffix
+ break
+
+ stripped.append(l)
+
+ return c(prefix, stripped, suffix, env)
+
+def processDefines(defs):
+ """process defines, resolving strings, lists, dictionaries, into a list of
+ strings
+ """
+ if SCons.Util.is_List(defs):
+ l = []
+ for d in defs:
+ if SCons.Util.is_List(d) or type(d) is types.TupleType:
+ l.append(str(d[0]) + '=' + str(d[1]))
+ else:
+ l.append(str(d))
+ elif SCons.Util.is_Dict(defs):
+ # The items in a dictionary are stored in random order, but
+ # if the order of the command-line options changes from
+ # invocation to invocation, then the signature of the command
+ # line will change and we'll get random unnecessary rebuilds.
+ # Consequently, we have to sort the keys to ensure a
+ # consistent order...
+ l = []
+ keys = defs.keys()
+ keys.sort()
+ for k in keys:
+ v = defs[k]
+ if v is None:
+ l.append(str(k))
+ else:
+ l.append(str(k) + '=' + str(v))
+ else:
+ l = [str(defs)]
+ return l
+
+def _defines(prefix, defs, suffix, env, c=_concat_ixes):
+ """A wrapper around _concat_ixes that turns a list or string
+ into a list of C preprocessor command-line definitions.
+ """
+
+ return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
+
+class NullCmdGenerator:
+ """This is a callable class that can be used in place of other
+ command generators if you don't want them to do anything.
+
+ The __call__ method for this class simply returns the thing
+ you instantiated it with.
+
+ Example usage:
+ env["DO_NOTHING"] = NullCmdGenerator
+ env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
+ """
+
+ def __init__(self, cmd):
+ self.cmd = cmd
+
+ def __call__(self, target, source, env, for_signature=None):
+ return self.cmd
+
+class Variable_Method_Caller:
+ """A class for finding a construction variable on the stack and
+ calling one of its methods.
+
+ We use this to support "construction variables" in our string
+ eval()s that actually stand in for methods--specifically, use
+ of "RDirs" in call to _concat that should actually execute the
+ "TARGET.RDirs" method. (We used to support this by creating a little
+ "build dictionary" that mapped RDirs to the method, but this got in
+ the way of Memoizing construction environments, because we had to
+ create new environment objects to hold the variables.)
+ """
+ def __init__(self, variable, method):
+ self.variable = variable
+ self.method = method
+ def __call__(self, *args, **kw):
+ try: 1/0
+ except ZeroDivisionError:
+ # Don't start iterating with the current stack-frame to
+ # prevent creating reference cycles (f_back is safe).
+ frame = sys.exc_info()[2].tb_frame.f_back
+ variable = self.variable
+ while frame:
+ if frame.f_locals.has_key(variable):
+ v = frame.f_locals[variable]
+ if v:
+ method = getattr(v, self.method)
+ return apply(method, args, kw)
+ frame = frame.f_back
+ return None
+
+ConstructionEnvironment = {
+ 'BUILDERS' : {},
+ 'SCANNERS' : [],
+ 'CONFIGUREDIR' : '#/.sconf_temp',
+ 'CONFIGURELOG' : '#/config.log',
+ 'CPPSUFFIXES' : SCons.Tool.CSuffixes,
+ 'DSUFFIXES' : SCons.Tool.DSuffixes,
+ 'ENV' : {},
+ 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes,
+# 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions
+ '_concat' : _concat,
+ '_defines' : _defines,
+ '_stripixes' : _stripixes,
+ '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
+ '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+ '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+ '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
+ 'TEMPFILE' : NullCmdGenerator,
+ 'Dir' : Variable_Method_Caller('TARGET', 'Dir'),
+ 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'),
+ 'File' : Variable_Method_Caller('TARGET', 'File'),
+ 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'),
+}
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Defaults.xml b/src/engine/SCons/Defaults.xml
new file mode 100644
index 0000000..8b617ae
--- /dev/null
+++ b/src/engine/SCons/Defaults.xml
@@ -0,0 +1,472 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<cvar name ="_concat">
+<summary>
+A function used to produce variables like &cv-_CPPINCFLAGS;. It takes
+four or five
+arguments: a prefix to concatenate onto each element, a list of
+elements, a suffix to concatenate onto each element, an environment
+for variable interpolation, and an optional function that will be
+called to transform the list before concatenation.
+
+<example>
+env['_CPPINCFLAGS'] = '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)',
+</example>
+</summary>
+</cvar>
+
+<cvar name="CONFIGUREDIR">
+<summary>
+The name of the directory in which
+Configure context test files are written.
+The default is
+<filename>.sconf_temp</filename>
+in the top-level directory
+containing the
+<filename>SConstruct</filename>
+file.
+</summary>
+</cvar>
+
+<cvar name="CONFIGURELOG">
+<summary>
+The name of the Configure context log file.
+The default is
+<filename>config.log</filename>
+in the top-level directory
+containing the
+<filename>SConstruct</filename>
+file.
+</summary>
+</cvar>
+
+<cvar name="_CPPDEFFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the C preprocessor command-line options
+to define values.
+The value of &cv-_CPPDEFFLAGS; is created
+by appending &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX;
+to the beginning and end
+of each definition in &cv-CPPDEFINES;.
+</summary>
+</cvar>
+
+<cvar name="CPPDEFINES">
+<summary>
+A platform independent specification of C preprocessor definitions.
+The definitions will be added to command lines
+through the automatically-generated
+&cv-_CPPDEFFLAGS; construction variable (see above),
+which is constructed according to
+the type of value of &cv-CPPDEFINES;:
+
+If &cv-CPPDEFINES; is a string,
+the values of the
+&cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX;
+construction variables
+will be added to the beginning and end.
+
+<example>
+# Will add -Dxyz to POSIX compiler command lines,
+# and /Dxyz to Microsoft Visual C++ command lines.
+env = Environment(CPPDEFINES='xyz')
+</example>
+
+If &cv-CPPDEFINES; is a list,
+the values of the
+&cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX;
+construction variables
+will be appended to the beginning and end
+of each element in the list.
+If any element is a list or tuple,
+then the first item is the name being
+defined and the second item is its value:
+
+<example>
+# Will add -DB=2 -DA to POSIX compiler command lines,
+# and /DB=2 /DA to Microsoft Visual C++ command lines.
+env = Environment(CPPDEFINES=[('B', 2), 'A'])
+</example>
+
+If &cv-CPPDEFINES; is a dictionary,
+the values of the
+&cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX;
+construction variables
+will be appended to the beginning and end
+of each item from the dictionary.
+The key of each dictionary item
+is a name being defined
+to the dictionary item's corresponding value;
+if the value is
+<literal>None</literal>,
+then the name is defined without an explicit value.
+Note that the resulting flags are sorted by keyword
+to ensure that the order of the options on the
+command line is consistent each time
+&scons;
+is run.
+
+<example>
+# Will add -DA -DB=2 to POSIX compiler command lines,
+# and /DA /DB=2 to Microsoft Visual C++ command lines.
+env = Environment(CPPDEFINES={'B':2, 'A':None})
+</example>
+</summary>
+</cvar>
+
+<cvar name="CPPDEFPREFIX">
+<summary>
+The prefix used to specify preprocessor definitions
+on the C compiler command line.
+This will be appended to the beginning of each definition
+in the &cv-CPPDEFINES; construction variable
+when the &cv-_CPPDEFFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="CPPDEFSUFFIX">
+<summary>
+The suffix used to specify preprocessor definitions
+on the C compiler command line.
+This will be appended to the end of each definition
+in the &cv-CPPDEFINES; construction variable
+when the &cv-_CPPDEFFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="_CPPINCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the C preprocessor command-line options
+for specifying directories to be searched for include files.
+The value of &cv-_CPPINCFLAGS; is created
+by appending &cv-INCPREFIX; and &cv-INCSUFFIX;
+to the beginning and end
+of each directory in &cv-CPPPATH;.
+</summary>
+</cvar>
+
+<cvar name="CPPPATH">
+<summary>
+The list of directories that the C preprocessor will search for include
+directories. The C/C++ implicit dependency scanner will search these
+directories for include files. Don't explicitly put include directory
+arguments in CCFLAGS or CXXFLAGS because the result will be non-portable
+and the directories will not be searched by the dependency scanner. Note:
+directory names in CPPPATH will be looked-up relative to the SConscript
+directory when they are used in a command. To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+
+<example>
+env = Environment(CPPPATH='#/include')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+include = Dir('include')
+env = Environment(CPPPATH=include)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-_CPPINCFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-INCPREFIX; and &cv-INCSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-CPPPATH;.
+Any command lines you define that need
+the CPPPATH directory list should
+include &cv-_CPPINCFLAGS;:
+
+<example>
+env = Environment(CCCOM="my_compiler $_CPPINCFLAGS -c -o $TARGET $SOURCE")
+</example>
+</summary>
+</cvar>
+
+<cvar name="Dir">
+<summary>
+A function that converts a string
+into a Dir instance relative to the target being built.
+</summary>
+</cvar>
+
+<cvar name="Dirs">
+<summary>
+A function that converts a list of strings
+into a list of Dir instances relative to the target being built.
+</summary>
+</cvar>
+
+<cvar name="DSUFFIXES">
+<summary>
+The list of suffixes of files that will be scanned
+for imported D package files.
+The default list is:
+
+<example>
+['.d']
+</example>
+</summary>
+</cvar>
+
+<cvar name="File">
+<summary>
+A function that converts a string into a File instance relative to the
+target being built.
+</summary>
+</cvar>
+
+<cvar name="IDLSUFFIXES">
+<summary>
+The list of suffixes of files that will be scanned
+for IDL implicit dependencies
+(#include or import lines).
+The default list is:
+
+<example>
+[".idl", ".IDL"]
+</example>
+</summary>
+</cvar>
+
+<cvar name="INCPREFIX">
+<summary>
+The prefix used to specify an include directory on the C compiler command
+line.
+This will be appended to the beginning of each directory
+in the &cv-CPPPATH; and &cv-FORTRANPATH; construction variables
+when the &cv-_CPPINCFLAGS; and &cv-_FORTRANINCFLAGS;
+variables are automatically generated.
+</summary>
+</cvar>
+
+<cvar name="INCSUFFIX">
+<summary>
+The suffix used to specify an include directory on the C compiler command
+line.
+This will be appended to the end of each directory
+in the &cv-CPPPATH; and &cv-FORTRANPATH; construction variables
+when the &cv-_CPPINCFLAGS; and &cv-_FORTRANINCFLAGS;
+variables are automatically generated.
+</summary>
+</cvar>
+
+<cvar name="INSTALL">
+<summary>
+A function to be called to install a file into a
+destination file name.
+The default function copies the file into the destination
+(and sets the destination file's mode and permission bits
+to match the source file's).
+The function takes the following arguments:
+
+<example>
+def install(dest, source, env):
+</example>
+
+<varname>dest</varname>
+is the path name of the destination file.
+<varname>source</varname>
+is the path name of the source file.
+<varname>env</varname>
+is the construction environment
+(a dictionary of construction values)
+in force for this file installation.
+</summary>
+</cvar>
+
+<cvar name="INSTALLSTR">
+<summary>
+The string displayed when a file is
+installed into a destination file name.
+The default is:
+<example>
+Install file: "$SOURCE" as "$TARGET"
+</example>
+</summary>
+</cvar>
+
+<cvar name="LATEXSUFFIXES">
+<summary>
+The list of suffixes of files that will be scanned
+for LaTeX implicit dependencies
+(<literal>\include</literal> or <literal>\import</literal> files).
+The default list is:
+
+<example>
+[".tex", ".ltx", ".latex"]
+</example>
+</summary>
+</cvar>
+
+<cvar name="_LIBDIRFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the linker command-line options
+for specifying directories to be searched for library.
+The value of &cv-_LIBDIRFLAGS; is created
+by appending &cv-LIBDIRPREFIX; and &cv-LIBDIRSUFFIX;
+to the beginning and end
+of each directory in &cv-LIBPATH;.
+</summary>
+</cvar>
+
+<cvar name="LIBDIRPREFIX">
+<summary>
+The prefix used to specify a library directory on the linker command line.
+This will be appended to the beginning of each directory
+in the &cv-LIBPATH; construction variable
+when the &cv-_LIBDIRFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="LIBDIRSUFFIX">
+<summary>
+The suffix used to specify a library directory on the linker command line.
+This will be appended to the end of each directory
+in the &cv-LIBPATH; construction variable
+when the &cv-_LIBDIRFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="_LIBFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the linker command-line options
+for specifying libraries to be linked with the resulting target.
+The value of &cv-_LIBFLAGS; is created
+by appending &cv-LIBLINKPREFIX; and &cv-LIBLINKSUFFIX;
+to the beginning and end
+of each filename in &cv-LIBS;.
+</summary>
+</cvar>
+
+<cvar name="LIBLINKPREFIX">
+<summary>
+The prefix used to specify a library to link on the linker command line.
+This will be appended to the beginning of each library
+in the &cv-LIBS; construction variable
+when the &cv-_LIBFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="LIBLINKSUFFIX">
+<summary>
+The suffix used to specify a library to link on the linker command line.
+This will be appended to the end of each library
+in the &cv-LIBS; construction variable
+when the &cv-_LIBFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="LIBPATH">
+<summary>
+The list of directories that will be searched for libraries.
+The implicit dependency scanner will search these
+directories for include files. Don't explicitly put include directory
+arguments in &cv-LINKFLAGS; or &cv-SHLINKFLAGS;
+because the result will be non-portable
+and the directories will not be searched by the dependency scanner. Note:
+directory names in LIBPATH will be looked-up relative to the SConscript
+directory when they are used in a command. To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+
+<example>
+env = Environment(LIBPATH='#/libs')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+libs = Dir('libs')
+env = Environment(LIBPATH=libs)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-_LIBDIRFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-LIBDIRPREFIX; and &cv-LIBDIRSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-LIBPATH;.
+Any command lines you define that need
+the LIBPATH directory list should
+include &cv-_LIBDIRFLAGS;:
+
+<example>
+env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE")
+</example>
+</summary>
+</cvar>
+
+<cvar name="LIBS">
+<summary>
+A list of one or more libraries
+that will be linked with
+any executable programs
+created by this environment.
+
+The library list will be added to command lines
+through the automatically-generated
+&cv-_LIBFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-LIBLINKPREFIX; and &cv-LIBLINKSUFFIX;
+construction variables
+to the beginning and end
+of each filename in &cv-LIBS;.
+Any command lines you define that need
+the LIBS library list should
+include &cv-_LIBFLAGS;:
+
+<example>
+env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE")
+</example>
+
+If you add a
+File
+object to the
+&cv-LIBS;
+list, the name of that file will be added to
+&cv-_LIBFLAGS;,
+and thus the link line, as is, without
+&cv-LIBLINKPREFIX;
+or
+&cv-LIBLINKSUFFIX;.
+For example:
+
+<example>
+env.Append(LIBS=File('/tmp/mylib.so'))
+</example>
+
+In all cases, scons will add dependencies from the executable program to
+all the libraries in this list.
+</summary>
+</cvar>
+
+<cvar name="RDirs">
+<summary>
+A function that converts a string into a list of Dir instances by
+searching the repositories.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/DefaultsTests.py b/src/engine/SCons/DefaultsTests.py
new file mode 100644
index 0000000..25d828b
--- /dev/null
+++ b/src/engine/SCons/DefaultsTests.py
@@ -0,0 +1,94 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/DefaultsTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+import StringIO
+import sys
+import types
+import unittest
+
+from UserDict import UserDict
+
+import TestCmd
+
+import SCons.Errors
+
+from SCons.Defaults import *
+
+class DefaultsTestCase(unittest.TestCase):
+ def test_mkdir_func0(self):
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub')
+ subdir2 = test.workpath('sub', 'dir1', 'dir2')
+ # Simple smoke test
+ mkdir_func(subdir2)
+ mkdir_func(subdir2) # 2nd time should be OK too
+
+ def test_mkdir_func1(self):
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub')
+ subdir1 = test.workpath('sub', 'dir1')
+ subdir2 = test.workpath('sub', 'dir1', 'dir2')
+ # No error if asked to create existing dir
+ os.makedirs(subdir2)
+ mkdir_func(subdir2)
+ mkdir_func(subdir1)
+
+ def test_mkdir_func2(self):
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub')
+ subdir1 = test.workpath('sub', 'dir1')
+ subdir2 = test.workpath('sub', 'dir1', 'dir2')
+ file = test.workpath('sub', 'dir1', 'dir2', 'file')
+
+ # make sure it does error if asked to create a dir
+ # where there's already a file
+ os.makedirs(subdir2)
+ test.write(file, "test\n")
+ try:
+ mkdir_func(file)
+ except os.error, e:
+ pass
+ else:
+ fail("expected os.error")
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ DefaultsTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
new file mode 100644
index 0000000..1ce8b4a
--- /dev/null
+++ b/src/engine/SCons/Environment.py
@@ -0,0 +1,2327 @@
+"""SCons.Environment
+
+Base class for construction Environments. These are
+the primary objects used to communicate dependency and
+construction information to the build engine.
+
+Keyword arguments supplied when the construction Environment
+is created are construction variables used to initialize the
+Environment
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Environment.py 4577 2009/12/27 19:44:43 scons"
+
+
+import copy
+import os
+import sys
+import re
+import shlex
+import string
+from UserDict import UserDict
+
+import SCons.Action
+import SCons.Builder
+from SCons.Debug import logInstanceCreation
+import SCons.Defaults
+import SCons.Errors
+import SCons.Memoize
+import SCons.Node
+import SCons.Node.Alias
+import SCons.Node.FS
+import SCons.Node.Python
+import SCons.Platform
+import SCons.SConf
+import SCons.SConsign
+import SCons.Subst
+import SCons.Tool
+import SCons.Util
+import SCons.Warnings
+
+class _Null:
+ pass
+
+_null = _Null
+
+_warn_copy_deprecated = True
+_warn_source_signatures_deprecated = True
+_warn_target_signatures_deprecated = True
+
+CleanTargets = {}
+CalculatorArgs = {}
+
+semi_deepcopy = SCons.Util.semi_deepcopy
+
+# Pull UserError into the global name space for the benefit of
+# Environment().SourceSignatures(), which has some import statements
+# which seem to mess up its ability to reference SCons directly.
+UserError = SCons.Errors.UserError
+
+def alias_builder(env, target, source):
+ pass
+
+AliasBuilder = SCons.Builder.Builder(action = alias_builder,
+ target_factory = SCons.Node.Alias.default_ans.Alias,
+ source_factory = SCons.Node.FS.Entry,
+ multi = 1,
+ is_explicit = None,
+ name='AliasBuilder')
+
+def apply_tools(env, tools, toolpath):
+ # Store the toolpath in the Environment.
+ if toolpath is not None:
+ env['toolpath'] = toolpath
+
+ if not tools:
+ return
+ # Filter out null tools from the list.
+ for tool in filter(None, tools):
+ if SCons.Util.is_List(tool) or type(tool)==type(()):
+ toolname = tool[0]
+ toolargs = tool[1] # should be a dict of kw args
+ tool = apply(env.Tool, [toolname], toolargs)
+ else:
+ env.Tool(tool)
+
+# These names are (or will be) controlled by SCons; users should never
+# set or override them. This warning can optionally be turned off,
+# but scons will still ignore the illegal variable names even if it's off.
+reserved_construction_var_names = [
+ 'CHANGED_SOURCES',
+ 'CHANGED_TARGETS',
+ 'SOURCE',
+ 'SOURCES',
+ 'TARGET',
+ 'TARGETS',
+ 'UNCHANGED_SOURCES',
+ 'UNCHANGED_TARGETS',
+]
+
+future_reserved_construction_var_names = [
+ #'HOST_OS',
+ #'HOST_ARCH',
+ #'HOST_CPU',
+ ]
+
+def copy_non_reserved_keywords(dict):
+ result = semi_deepcopy(dict)
+ for k in result.keys():
+ if k in reserved_construction_var_names:
+ msg = "Ignoring attempt to set reserved variable `$%s'"
+ SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
+ del result[k]
+ return result
+
+def _set_reserved(env, key, value):
+ msg = "Ignoring attempt to set reserved variable `$%s'"
+ SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
+
+def _set_future_reserved(env, key, value):
+ env._dict[key] = value
+ msg = "`$%s' will be reserved in a future release and setting it will become ignored"
+ SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
+
+def _set_BUILDERS(env, key, value):
+ try:
+ bd = env._dict[key]
+ for k in bd.keys():
+ del bd[k]
+ except KeyError:
+ bd = BuilderDict(kwbd, env)
+ env._dict[key] = bd
+ bd.update(value)
+
+def _del_SCANNERS(env, key):
+ del env._dict[key]
+ env.scanner_map_delete()
+
+def _set_SCANNERS(env, key, value):
+ env._dict[key] = value
+ env.scanner_map_delete()
+
+def _delete_duplicates(l, keep_last):
+ """Delete duplicates from a sequence, keeping the first or last."""
+ seen={}
+ result=[]
+ if keep_last: # reverse in & out, then keep first
+ l.reverse()
+ for i in l:
+ try:
+ if not seen.has_key(i):
+ result.append(i)
+ seen[i]=1
+ except TypeError:
+ # probably unhashable. Just keep it.
+ result.append(i)
+ if keep_last:
+ result.reverse()
+ return result
+
+
+
+# The following is partly based on code in a comment added by Peter
+# Shannon at the following page (there called the "transplant" class):
+#
+# ASPN : Python Cookbook : Dynamically added methods to a class
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
+#
+# We had independently been using the idiom as BuilderWrapper, but
+# factoring out the common parts into this base class, and making
+# BuilderWrapper a subclass that overrides __call__() to enforce specific
+# Builder calling conventions, simplified some of our higher-layer code.
+
+class MethodWrapper:
+ """
+ A generic Wrapper class that associates a method (which can
+ actually be any callable) with an object. As part of creating this
+ MethodWrapper object an attribute with the specified (by default,
+ the name of the supplied method) is added to the underlying object.
+ When that new "method" is called, our __call__() method adds the
+ object as the first argument, simulating the Python behavior of
+ supplying "self" on method calls.
+
+ We hang on to the name by which the method was added to the underlying
+ base class so that we can provide a method to "clone" ourselves onto
+ a new underlying object being copied (without which we wouldn't need
+ to save that info).
+ """
+ def __init__(self, object, method, name=None):
+ if name is None:
+ name = method.__name__
+ self.object = object
+ self.method = method
+ self.name = name
+ setattr(self.object, name, self)
+
+ def __call__(self, *args, **kwargs):
+ nargs = (self.object,) + args
+ return apply(self.method, nargs, kwargs)
+
+ def clone(self, new_object):
+ """
+ Returns an object that re-binds the underlying "method" to
+ the specified new object.
+ """
+ return self.__class__(new_object, self.method, self.name)
+
+class BuilderWrapper(MethodWrapper):
+ """
+ A MethodWrapper subclass that that associates an environment with
+ a Builder.
+
+ This mainly exists to wrap the __call__() function so that all calls
+ to Builders can have their argument lists massaged in the same way
+ (treat a lone argument as the source, treat two arguments as target
+ then source, make sure both target and source are lists) without
+ having to have cut-and-paste code to do it.
+
+ As a bit of obsessive backwards compatibility, we also intercept
+ attempts to get or set the "env" or "builder" attributes, which were
+ the names we used before we put the common functionality into the
+ MethodWrapper base class. We'll keep this around for a while in case
+ people shipped Tool modules that reached into the wrapper (like the
+ Tool/qt.py module does, or did). There shouldn't be a lot attribute
+ fetching or setting on these, so a little extra work shouldn't hurt.
+ """
+ def __call__(self, target=None, source=_null, *args, **kw):
+ if source is _null:
+ source = target
+ target = None
+ if target is not None and not SCons.Util.is_List(target):
+ target = [target]
+ if source is not None and not SCons.Util.is_List(source):
+ source = [source]
+ return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
+
+ def __repr__(self):
+ return '<BuilderWrapper %s>' % repr(self.name)
+
+ def __str__(self):
+ return self.__repr__()
+
+ def __getattr__(self, name):
+ if name == 'env':
+ return self.object
+ elif name == 'builder':
+ return self.method
+ else:
+ raise AttributeError, name
+
+ def __setattr__(self, name, value):
+ if name == 'env':
+ self.object = value
+ elif name == 'builder':
+ self.method = value
+ else:
+ self.__dict__[name] = value
+
+ # This allows a Builder to be executed directly
+ # through the Environment to which it's attached.
+ # In practice, we shouldn't need this, because
+ # builders actually get executed through a Node.
+ # But we do have a unit test for this, and can't
+ # yet rule out that it would be useful in the
+ # future, so leave it for now.
+ #def execute(self, **kw):
+ # kw['env'] = self.env
+ # apply(self.builder.execute, (), kw)
+
+class BuilderDict(UserDict):
+ """This is a dictionary-like class used by an Environment to hold
+ the Builders. We need to do this because every time someone changes
+ the Builders in the Environment's BUILDERS dictionary, we must
+ update the Environment's attributes."""
+ def __init__(self, dict, env):
+ # Set self.env before calling the superclass initialization,
+ # because it will end up calling our other methods, which will
+ # need to point the values in this dictionary to self.env.
+ self.env = env
+ UserDict.__init__(self, dict)
+
+ def __semi_deepcopy__(self):
+ return self.__class__(self.data, self.env)
+
+ def __setitem__(self, item, val):
+ try:
+ method = getattr(self.env, item).method
+ except AttributeError:
+ pass
+ else:
+ self.env.RemoveMethod(method)
+ UserDict.__setitem__(self, item, val)
+ BuilderWrapper(self.env, val, item)
+
+ def __delitem__(self, item):
+ UserDict.__delitem__(self, item)
+ delattr(self.env, item)
+
+ def update(self, dict):
+ for i, v in dict.items():
+ self.__setitem__(i, v)
+
+
+
+_is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
+
+def is_valid_construction_var(varstr):
+ """Return if the specified string is a legitimate construction
+ variable.
+ """
+ return _is_valid_var.match(varstr)
+
+
+
+class SubstitutionEnvironment:
+ """Base class for different flavors of construction environments.
+
+ This class contains a minimal set of methods that handle contruction
+ variable expansion and conversion of strings to Nodes, which may or
+ may not be actually useful as a stand-alone class. Which methods
+ ended up in this class is pretty arbitrary right now. They're
+ basically the ones which we've empirically determined are common to
+ the different construction environment subclasses, and most of the
+ others that use or touch the underlying dictionary of construction
+ variables.
+
+ Eventually, this class should contain all the methods that we
+ determine are necessary for a "minimal" interface to the build engine.
+ A full "native Python" SCons environment has gotten pretty heavyweight
+ with all of the methods and Tools and construction variables we've
+ jammed in there, so it would be nice to have a lighter weight
+ alternative for interfaces that don't need all of the bells and
+ whistles. (At some point, we'll also probably rename this class
+ "Base," since that more reflects what we want this class to become,
+ but because we've released comments that tell people to subclass
+ Environment.Base to create their own flavors of construction
+ environment, we'll save that for a future refactoring when this
+ class actually becomes useful.)
+ """
+
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ def __init__(self, **kw):
+ """Initialization of an underlying SubstitutionEnvironment class.
+ """
+ if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
+ self.fs = SCons.Node.FS.get_default_fs()
+ self.ans = SCons.Node.Alias.default_ans
+ self.lookup_list = SCons.Node.arg2nodes_lookups
+ self._dict = kw.copy()
+ self._init_special()
+ self.added_methods = []
+ #self._memo = {}
+
+ def _init_special(self):
+ """Initial the dispatch tables for special handling of
+ special construction variables."""
+ self._special_del = {}
+ self._special_del['SCANNERS'] = _del_SCANNERS
+
+ self._special_set = {}
+ for key in reserved_construction_var_names:
+ self._special_set[key] = _set_reserved
+ for key in future_reserved_construction_var_names:
+ self._special_set[key] = _set_future_reserved
+ self._special_set['BUILDERS'] = _set_BUILDERS
+ self._special_set['SCANNERS'] = _set_SCANNERS
+
+ # Freeze the keys of self._special_set in a list for use by
+ # methods that need to check. (Empirically, list scanning has
+ # gotten better than dict.has_key() in Python 2.5.)
+ self._special_set_keys = self._special_set.keys()
+
+ def __cmp__(self, other):
+ return cmp(self._dict, other._dict)
+
+ def __delitem__(self, key):
+ special = self._special_del.get(key)
+ if special:
+ special(self, key)
+ else:
+ del self._dict[key]
+
+ def __getitem__(self, key):
+ return self._dict[key]
+
+ def __setitem__(self, key, value):
+ # This is heavily used. This implementation is the best we have
+ # according to the timings in bench/env.__setitem__.py.
+ #
+ # The "key in self._special_set_keys" test here seems to perform
+ # pretty well for the number of keys we have. A hard-coded
+ # list works a little better in Python 2.5, but that has the
+ # disadvantage of maybe getting out of sync if we ever add more
+ # variable names. Using self._special_set.has_key() works a
+ # little better in Python 2.4, but is worse then this test.
+ # So right now it seems like a good trade-off, but feel free to
+ # revisit this with bench/env.__setitem__.py as needed (and
+ # as newer versions of Python come out).
+ if key in self._special_set_keys:
+ self._special_set[key](self, key, value)
+ else:
+ # If we already have the entry, then it's obviously a valid
+ # key and we don't need to check. If we do check, using a
+ # global, pre-compiled regular expression directly is more
+ # efficient than calling another function or a method.
+ if not self._dict.has_key(key) \
+ and not _is_valid_var.match(key):
+ raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
+ self._dict[key] = value
+
+ def get(self, key, default=None):
+ """Emulates the get() method of dictionaries."""
+ return self._dict.get(key, default)
+
+ def has_key(self, key):
+ return self._dict.has_key(key)
+
+ def __contains__(self, key):
+ return self._dict.__contains__(key)
+
+ def items(self):
+ return self._dict.items()
+
+ def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
+ if node_factory is _null:
+ node_factory = self.fs.File
+ if lookup_list is _null:
+ lookup_list = self.lookup_list
+
+ if not args:
+ return []
+
+ args = SCons.Util.flatten(args)
+
+ nodes = []
+ for v in args:
+ if SCons.Util.is_String(v):
+ n = None
+ for l in lookup_list:
+ n = l(v)
+ if n is not None:
+ break
+ if n is not None:
+ if SCons.Util.is_String(n):
+ # n = self.subst(n, raw=1, **kw)
+ kw['raw'] = 1
+ n = apply(self.subst, (n,), kw)
+ if node_factory:
+ n = node_factory(n)
+ if SCons.Util.is_List(n):
+ nodes.extend(n)
+ else:
+ nodes.append(n)
+ elif node_factory:
+ # v = node_factory(self.subst(v, raw=1, **kw))
+ kw['raw'] = 1
+ v = node_factory(apply(self.subst, (v,), kw))
+ if SCons.Util.is_List(v):
+ nodes.extend(v)
+ else:
+ nodes.append(v)
+ else:
+ nodes.append(v)
+
+ return nodes
+
+ def gvars(self):
+ return self._dict
+
+ def lvars(self):
+ return {}
+
+ def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None):
+ """Recursively interpolates construction variables from the
+ Environment into the specified string, returning the expanded
+ result. Construction variables are specified by a $ prefix
+ in the string and begin with an initial underscore or
+ alphabetic character followed by any number of underscores
+ or alphanumeric characters. The construction variable names
+ may be surrounded by curly braces to separate the name from
+ trailing characters.
+ """
+ gvars = self.gvars()
+ lvars = self.lvars()
+ lvars['__env__'] = self
+ if executor:
+ lvars.update(executor.get_lvars())
+ return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
+
+ def subst_kw(self, kw, raw=0, target=None, source=None):
+ nkw = {}
+ for k, v in kw.items():
+ k = self.subst(k, raw, target, source)
+ if SCons.Util.is_String(v):
+ v = self.subst(v, raw, target, source)
+ nkw[k] = v
+ return nkw
+
+ def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None):
+ """Calls through to SCons.Subst.scons_subst_list(). See
+ the documentation for that function."""
+ gvars = self.gvars()
+ lvars = self.lvars()
+ lvars['__env__'] = self
+ if executor:
+ lvars.update(executor.get_lvars())
+ return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
+
+ def subst_path(self, path, target=None, source=None):
+ """Substitute a path list, turning EntryProxies into Nodes
+ and leaving Nodes (and other objects) as-is."""
+
+ if not SCons.Util.is_List(path):
+ path = [path]
+
+ def s(obj):
+ """This is the "string conversion" routine that we have our
+ substitutions use to return Nodes, not strings. This relies
+ on the fact that an EntryProxy object has a get() method that
+ returns the underlying Node that it wraps, which is a bit of
+ architectural dependence that we might need to break or modify
+ in the future in response to additional requirements."""
+ try:
+ get = obj.get
+ except AttributeError:
+ obj = SCons.Util.to_String_for_subst(obj)
+ else:
+ obj = get()
+ return obj
+
+ r = []
+ for p in path:
+ if SCons.Util.is_String(p):
+ p = self.subst(p, target=target, source=source, conv=s)
+ if SCons.Util.is_List(p):
+ if len(p) == 1:
+ p = p[0]
+ else:
+ # We have an object plus a string, or multiple
+ # objects that we need to smush together. No choice
+ # but to make them into a string.
+ p = string.join(map(SCons.Util.to_String_for_subst, p), '')
+ else:
+ p = s(p)
+ r.append(p)
+ return r
+
+ subst_target_source = subst
+
+ def backtick(self, command):
+ import subprocess
+ # common arguments
+ kw = { 'stdin' : 'devnull',
+ 'stdout' : subprocess.PIPE,
+ 'stderr' : subprocess.PIPE,
+ 'universal_newlines' : True,
+ }
+ # if the command is a list, assume it's been quoted
+ # othewise force a shell
+ if not SCons.Util.is_List(command): kw['shell'] = True
+ # run constructed command
+ #TODO(1.5) p = SCons.Action._subproc(self, command, **kw)
+ p = apply(SCons.Action._subproc, (self, command), kw)
+ out,err = p.communicate()
+ status = p.wait()
+ if err:
+ sys.stderr.write(err)
+ if status:
+ raise OSError("'%s' exited %d" % (command, status))
+ return out
+
+ def AddMethod(self, function, name=None):
+ """
+ Adds the specified function as a method of this construction
+ environment with the specified name. If the name is omitted,
+ the default name is the name of the function itself.
+ """
+ method = MethodWrapper(self, function, name)
+ self.added_methods.append(method)
+
+ def RemoveMethod(self, function):
+ """
+ Removes the specified function's MethodWrapper from the
+ added_methods list, so we don't re-bind it when making a clone.
+ """
+ is_not_func = lambda dm, f=function: not dm.method is f
+ self.added_methods = filter(is_not_func, self.added_methods)
+
+ def Override(self, overrides):
+ """
+ Produce a modified environment whose variables are overriden by
+ the overrides dictionaries. "overrides" is a dictionary that
+ will override the variables of this environment.
+
+ This function is much more efficient than Clone() or creating
+ a new Environment because it doesn't copy the construction
+ environment dictionary, it just wraps the underlying construction
+ environment, and doesn't even create a wrapper object if there
+ are no overrides.
+ """
+ if not overrides: return self
+ o = copy_non_reserved_keywords(overrides)
+ if not o: return self
+ overrides = {}
+ merges = None
+ for key, value in o.items():
+ if key == 'parse_flags':
+ merges = value
+ else:
+ overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
+ env = OverrideEnvironment(self, overrides)
+ if merges: env.MergeFlags(merges)
+ return env
+
+ def ParseFlags(self, *flags):
+ """
+ Parse the set of flags and return a dict with the flags placed
+ in the appropriate entry. The flags are treated as a typical
+ set of command-line flags for a GNU-like toolchain and used to
+ populate the entries in the dict immediately below. If one of
+ the flag strings begins with a bang (exclamation mark), it is
+ assumed to be a command and the rest of the string is executed;
+ the result of that evaluation is then added to the dict.
+ """
+ dict = {
+ 'ASFLAGS' : SCons.Util.CLVar(''),
+ 'CFLAGS' : SCons.Util.CLVar(''),
+ 'CCFLAGS' : SCons.Util.CLVar(''),
+ 'CPPDEFINES' : [],
+ 'CPPFLAGS' : SCons.Util.CLVar(''),
+ 'CPPPATH' : [],
+ 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
+ 'FRAMEWORKS' : SCons.Util.CLVar(''),
+ 'LIBPATH' : [],
+ 'LIBS' : [],
+ 'LINKFLAGS' : SCons.Util.CLVar(''),
+ 'RPATH' : [],
+ }
+
+ # The use of the "me" parameter to provide our own name for
+ # recursion is an egregious hack to support Python 2.1 and before.
+ def do_parse(arg, me, self = self, dict = dict):
+ # if arg is a sequence, recurse with each element
+ if not arg:
+ return
+
+ if not SCons.Util.is_String(arg):
+ for t in arg: me(t, me)
+ return
+
+ # if arg is a command, execute it
+ if arg[0] == '!':
+ arg = self.backtick(arg[1:])
+
+ # utility function to deal with -D option
+ def append_define(name, dict = dict):
+ t = string.split(name, '=')
+ if len(t) == 1:
+ dict['CPPDEFINES'].append(name)
+ else:
+ dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
+
+ # Loop through the flags and add them to the appropriate option.
+ # This tries to strike a balance between checking for all possible
+ # flags and keeping the logic to a finite size, so it doesn't
+ # check for some that don't occur often. It particular, if the
+ # flag is not known to occur in a config script and there's a way
+ # of passing the flag to the right place (by wrapping it in a -W
+ # flag, for example) we don't check for it. Note that most
+ # preprocessor options are not handled, since unhandled options
+ # are placed in CCFLAGS, so unless the preprocessor is invoked
+ # separately, these flags will still get to the preprocessor.
+ # Other options not currently handled:
+ # -iqoutedir (preprocessor search path)
+ # -u symbol (linker undefined symbol)
+ # -s (linker strip files)
+ # -static* (linker static binding)
+ # -shared* (linker dynamic binding)
+ # -symbolic (linker global binding)
+ # -R dir (deprecated linker rpath)
+ # IBM compilers may also accept -qframeworkdir=foo
+
+ params = shlex.split(arg)
+ append_next_arg_to = None # for multi-word args
+ for arg in params:
+ if append_next_arg_to:
+ if append_next_arg_to == 'CPPDEFINES':
+ append_define(arg)
+ elif append_next_arg_to == '-include':
+ t = ('-include', self.fs.File(arg))
+ dict['CCFLAGS'].append(t)
+ elif append_next_arg_to == '-isysroot':
+ t = ('-isysroot', arg)
+ dict['CCFLAGS'].append(t)
+ dict['LINKFLAGS'].append(t)
+ elif append_next_arg_to == '-arch':
+ t = ('-arch', arg)
+ dict['CCFLAGS'].append(t)
+ dict['LINKFLAGS'].append(t)
+ else:
+ dict[append_next_arg_to].append(arg)
+ append_next_arg_to = None
+ elif not arg[0] in ['-', '+']:
+ dict['LIBS'].append(self.fs.File(arg))
+ elif arg[:2] == '-L':
+ if arg[2:]:
+ dict['LIBPATH'].append(arg[2:])
+ else:
+ append_next_arg_to = 'LIBPATH'
+ elif arg[:2] == '-l':
+ if arg[2:]:
+ dict['LIBS'].append(arg[2:])
+ else:
+ append_next_arg_to = 'LIBS'
+ elif arg[:2] == '-I':
+ if arg[2:]:
+ dict['CPPPATH'].append(arg[2:])
+ else:
+ append_next_arg_to = 'CPPPATH'
+ elif arg[:4] == '-Wa,':
+ dict['ASFLAGS'].append(arg[4:])
+ dict['CCFLAGS'].append(arg)
+ elif arg[:4] == '-Wl,':
+ if arg[:11] == '-Wl,-rpath=':
+ dict['RPATH'].append(arg[11:])
+ elif arg[:7] == '-Wl,-R,':
+ dict['RPATH'].append(arg[7:])
+ elif arg[:6] == '-Wl,-R':
+ dict['RPATH'].append(arg[6:])
+ else:
+ dict['LINKFLAGS'].append(arg)
+ elif arg[:4] == '-Wp,':
+ dict['CPPFLAGS'].append(arg)
+ elif arg[:2] == '-D':
+ if arg[2:]:
+ append_define(arg[2:])
+ else:
+ append_next_arg_to = 'CPPDEFINES'
+ elif arg == '-framework':
+ append_next_arg_to = 'FRAMEWORKS'
+ elif arg[:14] == '-frameworkdir=':
+ dict['FRAMEWORKPATH'].append(arg[14:])
+ elif arg[:2] == '-F':
+ if arg[2:]:
+ dict['FRAMEWORKPATH'].append(arg[2:])
+ else:
+ append_next_arg_to = 'FRAMEWORKPATH'
+ elif arg == '-mno-cygwin':
+ dict['CCFLAGS'].append(arg)
+ dict['LINKFLAGS'].append(arg)
+ elif arg == '-mwindows':
+ dict['LINKFLAGS'].append(arg)
+ elif arg == '-pthread':
+ dict['CCFLAGS'].append(arg)
+ dict['LINKFLAGS'].append(arg)
+ elif arg[:5] == '-std=':
+ dict['CFLAGS'].append(arg) # C only
+ elif arg[0] == '+':
+ dict['CCFLAGS'].append(arg)
+ dict['LINKFLAGS'].append(arg)
+ elif arg in ['-include', '-isysroot', '-arch']:
+ append_next_arg_to = arg
+ else:
+ dict['CCFLAGS'].append(arg)
+
+ for arg in flags:
+ do_parse(arg, do_parse)
+ return dict
+
+ def MergeFlags(self, args, unique=1, dict=None):
+ """
+ Merge the dict in args into the construction variables of this
+ env, or the passed-in dict. If args is not a dict, it is
+ converted into a dict using ParseFlags. If unique is not set,
+ the flags are appended rather than merged.
+ """
+
+ if dict is None:
+ dict = self
+ if not SCons.Util.is_Dict(args):
+ args = self.ParseFlags(args)
+ if not unique:
+ apply(self.Append, (), args)
+ return self
+ for key, value in args.items():
+ if not value:
+ continue
+ try:
+ orig = self[key]
+ except KeyError:
+ orig = value
+ else:
+ if not orig:
+ orig = value
+ elif value:
+ # Add orig and value. The logic here was lifted from
+ # part of env.Append() (see there for a lot of comments
+ # about the order in which things are tried) and is
+ # used mainly to handle coercion of strings to CLVar to
+ # "do the right thing" given (e.g.) an original CCFLAGS
+ # string variable like '-pipe -Wall'.
+ try:
+ orig = orig + value
+ except (KeyError, TypeError):
+ try:
+ add_to_orig = orig.append
+ except AttributeError:
+ value.insert(0, orig)
+ orig = value
+ else:
+ add_to_orig(value)
+ t = []
+ if key[-4:] == 'PATH':
+ ### keep left-most occurence
+ for v in orig:
+ if v not in t:
+ t.append(v)
+ else:
+ ### keep right-most occurence
+ orig.reverse()
+ for v in orig:
+ if v not in t:
+ t.insert(0, v)
+ self[key] = t
+ return self
+
+# def MergeShellPaths(self, args, prepend=1):
+# """
+# Merge the dict in args into the shell environment in env['ENV'].
+# Shell path elements are appended or prepended according to prepend.
+
+# Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
+
+# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
+# prepends /usr/local/lib to env['ENV']['LIBPATH'].
+# """
+
+# for pathname, pathval in args.items():
+# if not pathval:
+# continue
+# if prepend:
+# apply(self.PrependENVPath, (pathname, pathval))
+# else:
+# apply(self.AppendENVPath, (pathname, pathval))
+
+
+# Used by the FindSourceFiles() method, below.
+# Stuck here for support of pre-2.2 Python versions.
+def build_source(ss, result):
+ for s in ss:
+ if isinstance(s, SCons.Node.FS.Dir):
+ build_source(s.all_children(), result)
+ elif s.has_builder():
+ build_source(s.sources, result)
+ elif isinstance(s.disambiguate(), SCons.Node.FS.File):
+ result.append(s)
+
+def default_decide_source(dependency, target, prev_ni):
+ f = SCons.Defaults.DefaultEnvironment().decide_source
+ return f(dependency, target, prev_ni)
+
+def default_decide_target(dependency, target, prev_ni):
+ f = SCons.Defaults.DefaultEnvironment().decide_target
+ return f(dependency, target, prev_ni)
+
+def default_copy_from_cache(src, dst):
+ f = SCons.Defaults.DefaultEnvironment().copy_from_cache
+ return f(src, dst)
+
+class Base(SubstitutionEnvironment):
+ """Base class for "real" construction Environments. These are the
+ primary objects used to communicate dependency and construction
+ information to the build engine.
+
+ Keyword arguments supplied when the construction Environment
+ is created are construction variables used to initialize the
+ Environment.
+ """
+
+ memoizer_counters = []
+
+ #######################################################################
+ # This is THE class for interacting with the SCons build engine,
+ # and it contains a lot of stuff, so we're going to try to keep this
+ # a little organized by grouping the methods.
+ #######################################################################
+
+ #######################################################################
+ # Methods that make an Environment act like a dictionary. These have
+ # the expected standard names for Python mapping objects. Note that
+ # we don't actually make an Environment a subclass of UserDict for
+ # performance reasons. Note also that we only supply methods for
+ # dictionary functionality that we actually need and use.
+ #######################################################################
+
+ def __init__(self,
+ platform=None,
+ tools=None,
+ toolpath=None,
+ variables=None,
+ parse_flags = None,
+ **kw):
+ """
+ Initialization of a basic SCons construction environment,
+ including setting up special construction variables like BUILDER,
+ PLATFORM, etc., and searching for and applying available Tools.
+
+ Note that we do *not* call the underlying base class
+ (SubsitutionEnvironment) initialization, because we need to
+ initialize things in a very specific order that doesn't work
+ with the much simpler base class initialization.
+ """
+ if __debug__: logInstanceCreation(self, 'Environment.Base')
+ self._memo = {}
+ self.fs = SCons.Node.FS.get_default_fs()
+ self.ans = SCons.Node.Alias.default_ans
+ self.lookup_list = SCons.Node.arg2nodes_lookups
+ self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
+ self._init_special()
+ self.added_methods = []
+
+ # We don't use AddMethod, or define these as methods in this
+ # class, because we *don't* want these functions to be bound
+ # methods. They need to operate independently so that the
+ # settings will work properly regardless of whether a given
+ # target ends up being built with a Base environment or an
+ # OverrideEnvironment or what have you.
+ self.decide_target = default_decide_target
+ self.decide_source = default_decide_source
+
+ self.copy_from_cache = default_copy_from_cache
+
+ self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
+
+ if platform is None:
+ platform = self._dict.get('PLATFORM', None)
+ if platform is None:
+ platform = SCons.Platform.Platform()
+ if SCons.Util.is_String(platform):
+ platform = SCons.Platform.Platform(platform)
+ self._dict['PLATFORM'] = str(platform)
+ platform(self)
+
+ self._dict['HOST_OS'] = self._dict.get('HOST_OS',None)
+ self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None)
+
+ # Now set defaults for TARGET_{OS|ARCH}
+ self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None)
+ self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None)
+
+
+ # Apply the passed-in and customizable variables to the
+ # environment before calling the tools, because they may use
+ # some of them during initialization.
+ if kw.has_key('options'):
+ # Backwards compatibility: they may stll be using the
+ # old "options" keyword.
+ variables = kw['options']
+ del kw['options']
+ apply(self.Replace, (), kw)
+ keys = kw.keys()
+ if variables:
+ keys = keys + variables.keys()
+ variables.Update(self)
+
+ save = {}
+ for k in keys:
+ try:
+ save[k] = self._dict[k]
+ except KeyError:
+ # No value may have been set if they tried to pass in a
+ # reserved variable name like TARGETS.
+ pass
+
+ SCons.Tool.Initializers(self)
+
+ if tools is None:
+ tools = self._dict.get('TOOLS', None)
+ if tools is None:
+ tools = ['default']
+ apply_tools(self, tools, toolpath)
+
+ # Now restore the passed-in and customized variables
+ # to the environment, since the values the user set explicitly
+ # should override any values set by the tools.
+ for key, val in save.items():
+ self._dict[key] = val
+
+ # Finally, apply any flags to be merged in
+ if parse_flags: self.MergeFlags(parse_flags)
+
+ #######################################################################
+ # Utility methods that are primarily for internal use by SCons.
+ # These begin with lower-case letters.
+ #######################################################################
+
+ def get_builder(self, name):
+ """Fetch the builder with the specified name from the environment.
+ """
+ try:
+ return self._dict['BUILDERS'][name]
+ except KeyError:
+ return None
+
+ def get_CacheDir(self):
+ try:
+ path = self._CacheDir_path
+ except AttributeError:
+ path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
+ try:
+ if path == self._last_CacheDir_path:
+ return self._last_CacheDir
+ except AttributeError:
+ pass
+ cd = SCons.CacheDir.CacheDir(path)
+ self._last_CacheDir_path = path
+ self._last_CacheDir = cd
+ return cd
+
+ def get_factory(self, factory, default='File'):
+ """Return a factory function for creating Nodes for this
+ construction environment.
+ """
+ name = default
+ try:
+ is_node = issubclass(factory, SCons.Node.FS.Base)
+ except TypeError:
+ # The specified factory isn't a Node itself--it's
+ # most likely None, or possibly a callable.
+ pass
+ else:
+ if is_node:
+ # The specified factory is a Node (sub)class. Try to
+ # return the FS method that corresponds to the Node's
+ # name--that is, we return self.fs.Dir if they want a Dir,
+ # self.fs.File for a File, etc.
+ try: name = factory.__name__
+ except AttributeError: pass
+ else: factory = None
+ if not factory:
+ # They passed us None, or we picked up a name from a specified
+ # class, so return the FS method. (Note that we *don't*
+ # use our own self.{Dir,File} methods because that would
+ # cause env.subst() to be called twice on the file name,
+ # interfering with files that have $$ in them.)
+ factory = getattr(self.fs, name)
+ return factory
+
+ memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
+
+ def _gsm(self):
+ try:
+ return self._memo['_gsm']
+ except KeyError:
+ pass
+
+ result = {}
+
+ try:
+ scanners = self._dict['SCANNERS']
+ except KeyError:
+ pass
+ else:
+ # Reverse the scanner list so that, if multiple scanners
+ # claim they can scan the same suffix, earlier scanners
+ # in the list will overwrite later scanners, so that
+ # the result looks like a "first match" to the user.
+ if not SCons.Util.is_List(scanners):
+ scanners = [scanners]
+ else:
+ scanners = scanners[:] # copy so reverse() doesn't mod original
+ scanners.reverse()
+ for scanner in scanners:
+ for k in scanner.get_skeys(self):
+ if k and self['PLATFORM'] == 'win32':
+ k = string.lower(k)
+ result[k] = scanner
+
+ self._memo['_gsm'] = result
+
+ return result
+
+ def get_scanner(self, skey):
+ """Find the appropriate scanner given a key (usually a file suffix).
+ """
+ if skey and self['PLATFORM'] == 'win32':
+ skey = string.lower(skey)
+ return self._gsm().get(skey)
+
+ def scanner_map_delete(self, kw=None):
+ """Delete the cached scanner map (if we need to).
+ """
+ try:
+ del self._memo['_gsm']
+ except KeyError:
+ pass
+
+ def _update(self, dict):
+ """Update an environment's values directly, bypassing the normal
+ checks that occur when users try to set items.
+ """
+ self._dict.update(dict)
+
+ def get_src_sig_type(self):
+ try:
+ return self.src_sig_type
+ except AttributeError:
+ t = SCons.Defaults.DefaultEnvironment().src_sig_type
+ self.src_sig_type = t
+ return t
+
+ def get_tgt_sig_type(self):
+ try:
+ return self.tgt_sig_type
+ except AttributeError:
+ t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
+ self.tgt_sig_type = t
+ return t
+
+ #######################################################################
+ # Public methods for manipulating an Environment. These begin with
+ # upper-case letters. The essential characteristic of methods in
+ # this section is that they do *not* have corresponding same-named
+ # global functions. For example, a stand-alone Append() function
+ # makes no sense, because Append() is all about appending values to
+ # an Environment's construction variables.
+ #######################################################################
+
+ def Append(self, **kw):
+ """Append values to existing construction variables
+ in an Environment.
+ """
+ kw = copy_non_reserved_keywords(kw)
+ for key, val in kw.items():
+ # It would be easier on the eyes to write this using
+ # "continue" statements whenever we finish processing an item,
+ # but Python 1.5.2 apparently doesn't let you use "continue"
+ # within try:-except: blocks, so we have to nest our code.
+ try:
+ orig = self._dict[key]
+ except KeyError:
+ # No existing variable in the environment, so just set
+ # it to the new value.
+ self._dict[key] = val
+ else:
+ try:
+ # Check if the original looks like a dictionary.
+ # If it is, we can't just try adding the value because
+ # dictionaries don't have __add__() methods, and
+ # things like UserList will incorrectly coerce the
+ # original dict to a list (which we don't want).
+ update_dict = orig.update
+ except AttributeError:
+ try:
+ # Most straightforward: just try to add them
+ # together. This will work in most cases, when the
+ # original and new values are of compatible types.
+ self._dict[key] = orig + val
+ except (KeyError, TypeError):
+ try:
+ # Check if the original is a list.
+ add_to_orig = orig.append
+ except AttributeError:
+ # The original isn't a list, but the new
+ # value is (by process of elimination),
+ # so insert the original in the new value
+ # (if there's one to insert) and replace
+ # the variable with it.
+ if orig:
+ val.insert(0, orig)
+ self._dict[key] = val
+ else:
+ # The original is a list, so append the new
+ # value to it (if there's a value to append).
+ if val:
+ add_to_orig(val)
+ else:
+ # The original looks like a dictionary, so update it
+ # based on what we think the value looks like.
+ if SCons.Util.is_List(val):
+ for v in val:
+ orig[v] = None
+ else:
+ try:
+ update_dict(val)
+ except (AttributeError, TypeError, ValueError):
+ if SCons.Util.is_Dict(val):
+ for k, v in val.items():
+ orig[k] = v
+ else:
+ orig[val] = None
+ self.scanner_map_delete(kw)
+
+ # allow Dirs and strings beginning with # for top-relative
+ # Note this uses the current env's fs (in self).
+ def _canonicalize(self, path):
+ if not SCons.Util.is_String(path): # typically a Dir
+ path = str(path)
+ if path and path[0] == '#':
+ path = str(self.fs.Dir(path))
+ return path
+
+ def AppendENVPath(self, name, newpath, envname = 'ENV',
+ sep = os.pathsep, delete_existing=1):
+ """Append path elements to the path 'name' in the 'ENV'
+ dictionary for this environment. Will only add any particular
+ path once, and will normpath and normcase all paths to help
+ assure this. This can also handle the case where the env
+ variable is a list instead of a string.
+
+ If delete_existing is 0, a newpath which is already in the path
+ will not be moved to the end (it will be left where it is).
+ """
+
+ orig = ''
+ if self._dict.has_key(envname) and self._dict[envname].has_key(name):
+ orig = self._dict[envname][name]
+
+ nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing,
+ canonicalize=self._canonicalize)
+
+ if not self._dict.has_key(envname):
+ self._dict[envname] = {}
+
+ self._dict[envname][name] = nv
+
+ def AppendUnique(self, delete_existing=0, **kw):
+ """Append values to existing construction variables
+ in an Environment, if they're not already there.
+ If delete_existing is 1, removes existing values first, so
+ values move to end.
+ """
+ kw = copy_non_reserved_keywords(kw)
+ for key, val in kw.items():
+ if SCons.Util.is_List(val):
+ val = _delete_duplicates(val, delete_existing)
+ if not self._dict.has_key(key) or self._dict[key] in ('', None):
+ self._dict[key] = val
+ elif SCons.Util.is_Dict(self._dict[key]) and \
+ SCons.Util.is_Dict(val):
+ self._dict[key].update(val)
+ elif SCons.Util.is_List(val):
+ dk = self._dict[key]
+ if not SCons.Util.is_List(dk):
+ dk = [dk]
+ if delete_existing:
+ dk = filter(lambda x, val=val: x not in val, dk)
+ else:
+ val = filter(lambda x, dk=dk: x not in dk, val)
+ self._dict[key] = dk + val
+ else:
+ dk = self._dict[key]
+ if SCons.Util.is_List(dk):
+ # By elimination, val is not a list. Since dk is a
+ # list, wrap val in a list first.
+ if delete_existing:
+ dk = filter(lambda x, val=val: x not in val, dk)
+ self._dict[key] = dk + [val]
+ else:
+ if not val in dk:
+ self._dict[key] = dk + [val]
+ else:
+ if delete_existing:
+ dk = filter(lambda x, val=val: x not in val, dk)
+ self._dict[key] = dk + val
+ self.scanner_map_delete(kw)
+
+ def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
+ """Return a copy of a construction Environment. The
+ copy is like a Python "deep copy"--that is, independent
+ copies are made recursively of each objects--except that
+ a reference is copied when an object is not deep-copyable
+ (like a function). There are no references to any mutable
+ objects in the original Environment.
+ """
+ clone = copy.copy(self)
+ clone._dict = semi_deepcopy(self._dict)
+
+ try:
+ cbd = clone._dict['BUILDERS']
+ except KeyError:
+ pass
+ else:
+ clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
+
+ # Check the methods added via AddMethod() and re-bind them to
+ # the cloned environment. Only do this if the attribute hasn't
+ # been overwritten by the user explicitly and still points to
+ # the added method.
+ clone.added_methods = []
+ for mw in self.added_methods:
+ if mw == getattr(self, mw.name):
+ clone.added_methods.append(mw.clone(clone))
+
+ clone._memo = {}
+
+ # Apply passed-in variables before the tools
+ # so the tools can use the new variables
+ kw = copy_non_reserved_keywords(kw)
+ new = {}
+ for key, value in kw.items():
+ new[key] = SCons.Subst.scons_subst_once(value, self, key)
+ apply(clone.Replace, (), new)
+
+ apply_tools(clone, tools, toolpath)
+
+ # apply them again in case the tools overwrote them
+ apply(clone.Replace, (), new)
+
+ # Finally, apply any flags to be merged in
+ if parse_flags: clone.MergeFlags(parse_flags)
+
+ if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
+ return clone
+
+ def Copy(self, *args, **kw):
+ global _warn_copy_deprecated
+ if _warn_copy_deprecated:
+ msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
+ _warn_copy_deprecated = False
+ return apply(self.Clone, args, kw)
+
+ def _changed_build(self, dependency, target, prev_ni):
+ if dependency.changed_state(target, prev_ni):
+ return 1
+ return self.decide_source(dependency, target, prev_ni)
+
+ def _changed_content(self, dependency, target, prev_ni):
+ return dependency.changed_content(target, prev_ni)
+
+ def _changed_source(self, dependency, target, prev_ni):
+ target_env = dependency.get_build_env()
+ type = target_env.get_tgt_sig_type()
+ if type == 'source':
+ return target_env.decide_source(dependency, target, prev_ni)
+ else:
+ return target_env.decide_target(dependency, target, prev_ni)
+
+ def _changed_timestamp_then_content(self, dependency, target, prev_ni):
+ return dependency.changed_timestamp_then_content(target, prev_ni)
+
+ def _changed_timestamp_newer(self, dependency, target, prev_ni):
+ return dependency.changed_timestamp_newer(target, prev_ni)
+
+ def _changed_timestamp_match(self, dependency, target, prev_ni):
+ return dependency.changed_timestamp_match(target, prev_ni)
+
+ def _copy_from_cache(self, src, dst):
+ return self.fs.copy(src, dst)
+
+ def _copy2_from_cache(self, src, dst):
+ return self.fs.copy2(src, dst)
+
+ def Decider(self, function):
+ copy_function = self._copy2_from_cache
+ if function in ('MD5', 'content'):
+ if not SCons.Util.md5:
+ raise UserError, "MD5 signatures are not available in this version of Python."
+ function = self._changed_content
+ elif function == 'MD5-timestamp':
+ function = self._changed_timestamp_then_content
+ elif function in ('timestamp-newer', 'make'):
+ function = self._changed_timestamp_newer
+ copy_function = self._copy_from_cache
+ elif function == 'timestamp-match':
+ function = self._changed_timestamp_match
+ elif not callable(function):
+ raise UserError, "Unknown Decider value %s" % repr(function)
+
+ # We don't use AddMethod because we don't want to turn the
+ # function, which only expects three arguments, into a bound
+ # method, which would add self as an initial, fourth argument.
+ self.decide_target = function
+ self.decide_source = function
+
+ self.copy_from_cache = copy_function
+
+ def Detect(self, progs):
+ """Return the first available program in progs.
+ """
+ if not SCons.Util.is_List(progs):
+ progs = [ progs ]
+ for prog in progs:
+ path = self.WhereIs(prog)
+ if path: return prog
+ return None
+
+ def Dictionary(self, *args):
+ if not args:
+ return self._dict
+ dlist = map(lambda x, s=self: s._dict[x], args)
+ if len(dlist) == 1:
+ dlist = dlist[0]
+ return dlist
+
+ def Dump(self, key = None):
+ """
+ Using the standard Python pretty printer, dump the contents of the
+ scons build environment to stdout.
+
+ If the key passed in is anything other than None, then that will
+ be used as an index into the build environment dictionary and
+ whatever is found there will be fed into the pretty printer. Note
+ that this key is case sensitive.
+ """
+ import pprint
+ pp = pprint.PrettyPrinter(indent=2)
+ if key:
+ dict = self.Dictionary(key)
+ else:
+ dict = self.Dictionary()
+ return pp.pformat(dict)
+
+ def FindIxes(self, paths, prefix, suffix):
+ """
+ Search a list of paths for something that matches the prefix and suffix.
+
+ paths - the list of paths or nodes.
+ prefix - construction variable for the prefix.
+ suffix - construction variable for the suffix.
+ """
+
+ suffix = self.subst('$'+suffix)
+ prefix = self.subst('$'+prefix)
+
+ for path in paths:
+ dir,name = os.path.split(str(path))
+ if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
+ return path
+
+ def ParseConfig(self, command, function=None, unique=1):
+ """
+ Use the specified function to parse the output of the command
+ in order to modify the current environment. The 'command' can
+ be a string or a list of strings representing a command and
+ its arguments. 'Function' is an optional argument that takes
+ the environment, the output of the command, and the unique flag.
+ If no function is specified, MergeFlags, which treats the output
+ as the result of a typical 'X-config' command (i.e. gtk-config),
+ will merge the output into the appropriate variables.
+ """
+ if function is None:
+ def parse_conf(env, cmd, unique=unique):
+ return env.MergeFlags(cmd, unique)
+ function = parse_conf
+ if SCons.Util.is_List(command):
+ command = string.join(command)
+ command = self.subst(command)
+ return function(self, self.backtick(command))
+
+ def ParseDepends(self, filename, must_exist=None, only_one=0):
+ """
+ Parse a mkdep-style file for explicit dependencies. This is
+ completely abusable, and should be unnecessary in the "normal"
+ case of proper SCons configuration, but it may help make
+ the transition from a Make hierarchy easier for some people
+ to swallow. It can also be genuinely useful when using a tool
+ that can write a .d file, but for which writing a scanner would
+ be too complicated.
+ """
+ filename = self.subst(filename)
+ try:
+ fp = open(filename, 'r')
+ except IOError:
+ if must_exist:
+ raise
+ return
+ lines = SCons.Util.LogicalLines(fp).readlines()
+ lines = filter(lambda l: l[0] != '#', lines)
+ tdlist = []
+ for line in lines:
+ try:
+ target, depends = string.split(line, ':', 1)
+ except (AttributeError, TypeError, ValueError):
+ # Python 1.5.2 throws TypeError if line isn't a string,
+ # Python 2.x throws AttributeError because it tries
+ # to call line.split(). Either can throw ValueError
+ # if the line doesn't split into two or more elements.
+ pass
+ else:
+ tdlist.append((string.split(target), string.split(depends)))
+ if only_one:
+ targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
+ if len(targets) > 1:
+ raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
+ for target, depends in tdlist:
+ self.Depends(target, depends)
+
+ def Platform(self, platform):
+ platform = self.subst(platform)
+ return SCons.Platform.Platform(platform)(self)
+
+ def Prepend(self, **kw):
+ """Prepend values to existing construction variables
+ in an Environment.
+ """
+ kw = copy_non_reserved_keywords(kw)
+ for key, val in kw.items():
+ # It would be easier on the eyes to write this using
+ # "continue" statements whenever we finish processing an item,
+ # but Python 1.5.2 apparently doesn't let you use "continue"
+ # within try:-except: blocks, so we have to nest our code.
+ try:
+ orig = self._dict[key]
+ except KeyError:
+ # No existing variable in the environment, so just set
+ # it to the new value.
+ self._dict[key] = val
+ else:
+ try:
+ # Check if the original looks like a dictionary.
+ # If it is, we can't just try adding the value because
+ # dictionaries don't have __add__() methods, and
+ # things like UserList will incorrectly coerce the
+ # original dict to a list (which we don't want).
+ update_dict = orig.update
+ except AttributeError:
+ try:
+ # Most straightforward: just try to add them
+ # together. This will work in most cases, when the
+ # original and new values are of compatible types.
+ self._dict[key] = val + orig
+ except (KeyError, TypeError):
+ try:
+ # Check if the added value is a list.
+ add_to_val = val.append
+ except AttributeError:
+ # The added value isn't a list, but the
+ # original is (by process of elimination),
+ # so insert the the new value in the original
+ # (if there's one to insert).
+ if val:
+ orig.insert(0, val)
+ else:
+ # The added value is a list, so append
+ # the original to it (if there's a value
+ # to append).
+ if orig:
+ add_to_val(orig)
+ self._dict[key] = val
+ else:
+ # The original looks like a dictionary, so update it
+ # based on what we think the value looks like.
+ if SCons.Util.is_List(val):
+ for v in val:
+ orig[v] = None
+ else:
+ try:
+ update_dict(val)
+ except (AttributeError, TypeError, ValueError):
+ if SCons.Util.is_Dict(val):
+ for k, v in val.items():
+ orig[k] = v
+ else:
+ orig[val] = None
+ self.scanner_map_delete(kw)
+
+ def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
+ delete_existing=1):
+ """Prepend path elements to the path 'name' in the 'ENV'
+ dictionary for this environment. Will only add any particular
+ path once, and will normpath and normcase all paths to help
+ assure this. This can also handle the case where the env
+ variable is a list instead of a string.
+
+ If delete_existing is 0, a newpath which is already in the path
+ will not be moved to the front (it will be left where it is).
+ """
+
+ orig = ''
+ if self._dict.has_key(envname) and self._dict[envname].has_key(name):
+ orig = self._dict[envname][name]
+
+ nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing,
+ canonicalize=self._canonicalize)
+
+ if not self._dict.has_key(envname):
+ self._dict[envname] = {}
+
+ self._dict[envname][name] = nv
+
+ def PrependUnique(self, delete_existing=0, **kw):
+ """Prepend values to existing construction variables
+ in an Environment, if they're not already there.
+ If delete_existing is 1, removes existing values first, so
+ values move to front.
+ """
+ kw = copy_non_reserved_keywords(kw)
+ for key, val in kw.items():
+ if SCons.Util.is_List(val):
+ val = _delete_duplicates(val, not delete_existing)
+ if not self._dict.has_key(key) or self._dict[key] in ('', None):
+ self._dict[key] = val
+ elif SCons.Util.is_Dict(self._dict[key]) and \
+ SCons.Util.is_Dict(val):
+ self._dict[key].update(val)
+ elif SCons.Util.is_List(val):
+ dk = self._dict[key]
+ if not SCons.Util.is_List(dk):
+ dk = [dk]
+ if delete_existing:
+ dk = filter(lambda x, val=val: x not in val, dk)
+ else:
+ val = filter(lambda x, dk=dk: x not in dk, val)
+ self._dict[key] = val + dk
+ else:
+ dk = self._dict[key]
+ if SCons.Util.is_List(dk):
+ # By elimination, val is not a list. Since dk is a
+ # list, wrap val in a list first.
+ if delete_existing:
+ dk = filter(lambda x, val=val: x not in val, dk)
+ self._dict[key] = [val] + dk
+ else:
+ if not val in dk:
+ self._dict[key] = [val] + dk
+ else:
+ if delete_existing:
+ dk = filter(lambda x, val=val: x not in val, dk)
+ self._dict[key] = val + dk
+ self.scanner_map_delete(kw)
+
+ def Replace(self, **kw):
+ """Replace existing construction variables in an Environment
+ with new construction variables and/or values.
+ """
+ try:
+ kwbd = kw['BUILDERS']
+ except KeyError:
+ pass
+ else:
+ kwbd = semi_deepcopy(kwbd)
+ del kw['BUILDERS']
+ self.__setitem__('BUILDERS', kwbd)
+ kw = copy_non_reserved_keywords(kw)
+ self._update(semi_deepcopy(kw))
+ self.scanner_map_delete(kw)
+
+ def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
+ """
+ Replace old_prefix with new_prefix and old_suffix with new_suffix.
+
+ env - Environment used to interpolate variables.
+ path - the path that will be modified.
+ old_prefix - construction variable for the old prefix.
+ old_suffix - construction variable for the old suffix.
+ new_prefix - construction variable for the new prefix.
+ new_suffix - construction variable for the new suffix.
+ """
+ old_prefix = self.subst('$'+old_prefix)
+ old_suffix = self.subst('$'+old_suffix)
+
+ new_prefix = self.subst('$'+new_prefix)
+ new_suffix = self.subst('$'+new_suffix)
+
+ dir,name = os.path.split(str(path))
+ if name[:len(old_prefix)] == old_prefix:
+ name = name[len(old_prefix):]
+ if name[-len(old_suffix):] == old_suffix:
+ name = name[:-len(old_suffix)]
+ return os.path.join(dir, new_prefix+name+new_suffix)
+
+ def SetDefault(self, **kw):
+ for k in kw.keys():
+ if self._dict.has_key(k):
+ del kw[k]
+ apply(self.Replace, (), kw)
+
+ def _find_toolpath_dir(self, tp):
+ return self.fs.Dir(self.subst(tp)).srcnode().abspath
+
+ def Tool(self, tool, toolpath=None, **kw):
+ if SCons.Util.is_String(tool):
+ tool = self.subst(tool)
+ if toolpath is None:
+ toolpath = self.get('toolpath', [])
+ toolpath = map(self._find_toolpath_dir, toolpath)
+ tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
+ tool(self)
+
+ def WhereIs(self, prog, path=None, pathext=None, reject=[]):
+ """Find prog in the path.
+ """
+ if path is None:
+ try:
+ path = self['ENV']['PATH']
+ except KeyError:
+ pass
+ elif SCons.Util.is_String(path):
+ path = self.subst(path)
+ if pathext is None:
+ try:
+ pathext = self['ENV']['PATHEXT']
+ except KeyError:
+ pass
+ elif SCons.Util.is_String(pathext):
+ pathext = self.subst(pathext)
+ prog = self.subst(prog)
+ path = SCons.Util.WhereIs(prog, path, pathext, reject)
+ if path: return path
+ return None
+
+ #######################################################################
+ # Public methods for doing real "SCons stuff" (manipulating
+ # dependencies, setting attributes on targets, etc.). These begin
+ # with upper-case letters. The essential characteristic of methods
+ # in this section is that they all *should* have corresponding
+ # same-named global functions.
+ #######################################################################
+
+ def Action(self, *args, **kw):
+ def subst_string(a, self=self):
+ if SCons.Util.is_String(a):
+ a = self.subst(a)
+ return a
+ nargs = map(subst_string, args)
+ nkw = self.subst_kw(kw)
+ return apply(SCons.Action.Action, nargs, nkw)
+
+ def AddPreAction(self, files, action):
+ nodes = self.arg2nodes(files, self.fs.Entry)
+ action = SCons.Action.Action(action)
+ uniq = {}
+ for executor in map(lambda n: n.get_executor(), nodes):
+ uniq[executor] = 1
+ for executor in uniq.keys():
+ executor.add_pre_action(action)
+ return nodes
+
+ def AddPostAction(self, files, action):
+ nodes = self.arg2nodes(files, self.fs.Entry)
+ action = SCons.Action.Action(action)
+ uniq = {}
+ for executor in map(lambda n: n.get_executor(), nodes):
+ uniq[executor] = 1
+ for executor in uniq.keys():
+ executor.add_post_action(action)
+ return nodes
+
+ def Alias(self, target, source=[], action=None, **kw):
+ tlist = self.arg2nodes(target, self.ans.Alias)
+ if not SCons.Util.is_List(source):
+ source = [source]
+ source = filter(None, source)
+
+ if not action:
+ if not source:
+ # There are no source files and no action, so just
+ # return a target list of classic Alias Nodes, without
+ # any builder. The externally visible effect is that
+ # this will make the wrapping Script.BuildTask class
+ # say that there's "Nothing to be done" for this Alias,
+ # instead of that it's "up to date."
+ return tlist
+
+ # No action, but there are sources. Re-call all the target
+ # builders to add the sources to each target.
+ result = []
+ for t in tlist:
+ bld = t.get_builder(AliasBuilder)
+ result.extend(bld(self, t, source))
+ return result
+
+ nkw = self.subst_kw(kw)
+ nkw.update({
+ 'action' : SCons.Action.Action(action),
+ 'source_factory' : self.fs.Entry,
+ 'multi' : 1,
+ 'is_explicit' : None,
+ })
+ bld = apply(SCons.Builder.Builder, (), nkw)
+
+ # Apply the Builder separately to each target so that the Aliases
+ # stay separate. If we did one "normal" Builder call with the
+ # whole target list, then all of the target Aliases would be
+ # associated under a single Executor.
+ result = []
+ for t in tlist:
+ # Calling the convert() method will cause a new Executor to be
+ # created from scratch, so we have to explicitly initialize
+ # it with the target's existing sources, plus our new ones,
+ # so nothing gets lost.
+ b = t.get_builder()
+ if b is None or b is AliasBuilder:
+ b = bld
+ else:
+ nkw['action'] = b.action + action
+ b = apply(SCons.Builder.Builder, (), nkw)
+ t.convert()
+ result.extend(b(self, t, t.sources + source))
+ return result
+
+ def AlwaysBuild(self, *targets):
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_always_build()
+ return tlist
+
+ def BuildDir(self, *args, **kw):
+ if kw.has_key('build_dir'):
+ kw['variant_dir'] = kw['build_dir']
+ del kw['build_dir']
+ return apply(self.VariantDir, args, kw)
+
+ def Builder(self, **kw):
+ nkw = self.subst_kw(kw)
+ return apply(SCons.Builder.Builder, [], nkw)
+
+ def CacheDir(self, path):
+ import SCons.CacheDir
+ if path is not None:
+ path = self.subst(path)
+ self._CacheDir_path = path
+
+ def Clean(self, targets, files):
+ global CleanTargets
+ tlist = self.arg2nodes(targets, self.fs.Entry)
+ flist = self.arg2nodes(files, self.fs.Entry)
+ for t in tlist:
+ try:
+ CleanTargets[t].extend(flist)
+ except KeyError:
+ CleanTargets[t] = flist
+
+ def Configure(self, *args, **kw):
+ nargs = [self]
+ if args:
+ nargs = nargs + self.subst_list(args)[0]
+ nkw = self.subst_kw(kw)
+ nkw['_depth'] = kw.get('_depth', 0) + 1
+ try:
+ nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
+ except KeyError:
+ pass
+ return apply(SCons.SConf.SConf, nargs, nkw)
+
+ def Command(self, target, source, action, **kw):
+ """Builds the supplied target files from the supplied
+ source files using the supplied action. Action may
+ be any type that the Builder constructor will accept
+ for an action."""
+ bkw = {
+ 'action' : action,
+ 'target_factory' : self.fs.Entry,
+ 'source_factory' : self.fs.Entry,
+ }
+ try: bkw['source_scanner'] = kw['source_scanner']
+ except KeyError: pass
+ else: del kw['source_scanner']
+ bld = apply(SCons.Builder.Builder, (), bkw)
+ return apply(bld, (self, target, source), kw)
+
+ def Depends(self, target, dependency):
+ """Explicity specify that 'target's depend on 'dependency'."""
+ tlist = self.arg2nodes(target, self.fs.Entry)
+ dlist = self.arg2nodes(dependency, self.fs.Entry)
+ for t in tlist:
+ t.add_dependency(dlist)
+ return tlist
+
+ def Dir(self, name, *args, **kw):
+ """
+ """
+ s = self.subst(name)
+ if SCons.Util.is_Sequence(s):
+ result=[]
+ for e in s:
+ result.append(apply(self.fs.Dir, (e,) + args, kw))
+ return result
+ return apply(self.fs.Dir, (s,) + args, kw)
+
+ def NoClean(self, *targets):
+ """Tags a target so that it will not be cleaned by -c"""
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_noclean()
+ return tlist
+
+ def NoCache(self, *targets):
+ """Tags a target so that it will not be cached"""
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_nocache()
+ return tlist
+
+ def Entry(self, name, *args, **kw):
+ """
+ """
+ s = self.subst(name)
+ if SCons.Util.is_Sequence(s):
+ result=[]
+ for e in s:
+ result.append(apply(self.fs.Entry, (e,) + args, kw))
+ return result
+ return apply(self.fs.Entry, (s,) + args, kw)
+
+ def Environment(self, **kw):
+ return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
+
+ def Execute(self, action, *args, **kw):
+ """Directly execute an action through an Environment
+ """
+ action = apply(self.Action, (action,) + args, kw)
+ result = action([], [], self)
+ if isinstance(result, SCons.Errors.BuildError):
+ errstr = result.errstr
+ if result.filename:
+ errstr = result.filename + ': ' + errstr
+ sys.stderr.write("scons: *** %s\n" % errstr)
+ return result.status
+ else:
+ return result
+
+ def File(self, name, *args, **kw):
+ """
+ """
+ s = self.subst(name)
+ if SCons.Util.is_Sequence(s):
+ result=[]
+ for e in s:
+ result.append(apply(self.fs.File, (e,) + args, kw))
+ return result
+ return apply(self.fs.File, (s,) + args, kw)
+
+ def FindFile(self, file, dirs):
+ file = self.subst(file)
+ nodes = self.arg2nodes(dirs, self.fs.Dir)
+ return SCons.Node.FS.find_file(file, tuple(nodes))
+
+ def Flatten(self, sequence):
+ return SCons.Util.flatten(sequence)
+
+ def GetBuildPath(self, files):
+ result = map(str, self.arg2nodes(files, self.fs.Entry))
+ if SCons.Util.is_List(files):
+ return result
+ else:
+ return result[0]
+
+ def Glob(self, pattern, ondisk=True, source=False, strings=False):
+ return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
+
+ def Ignore(self, target, dependency):
+ """Ignore a dependency."""
+ tlist = self.arg2nodes(target, self.fs.Entry)
+ dlist = self.arg2nodes(dependency, self.fs.Entry)
+ for t in tlist:
+ t.add_ignore(dlist)
+ return tlist
+
+ def Literal(self, string):
+ return SCons.Subst.Literal(string)
+
+ def Local(self, *targets):
+ ret = []
+ for targ in targets:
+ if isinstance(targ, SCons.Node.Node):
+ targ.set_local()
+ ret.append(targ)
+ else:
+ for t in self.arg2nodes(targ, self.fs.Entry):
+ t.set_local()
+ ret.append(t)
+ return ret
+
+ def Precious(self, *targets):
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_precious()
+ return tlist
+
+ def Repository(self, *dirs, **kw):
+ dirs = self.arg2nodes(list(dirs), self.fs.Dir)
+ apply(self.fs.Repository, dirs, kw)
+
+ def Requires(self, target, prerequisite):
+ """Specify that 'prerequisite' must be built before 'target',
+ (but 'target' does not actually depend on 'prerequisite'
+ and need not be rebuilt if it changes)."""
+ tlist = self.arg2nodes(target, self.fs.Entry)
+ plist = self.arg2nodes(prerequisite, self.fs.Entry)
+ for t in tlist:
+ t.add_prerequisite(plist)
+ return tlist
+
+ def Scanner(self, *args, **kw):
+ nargs = []
+ for arg in args:
+ if SCons.Util.is_String(arg):
+ arg = self.subst(arg)
+ nargs.append(arg)
+ nkw = self.subst_kw(kw)
+ return apply(SCons.Scanner.Base, nargs, nkw)
+
+ def SConsignFile(self, name=".sconsign", dbm_module=None):
+ if name is not None:
+ name = self.subst(name)
+ if not os.path.isabs(name):
+ name = os.path.join(str(self.fs.SConstruct_dir), name)
+ if name:
+ name = os.path.normpath(name)
+ sconsign_dir = os.path.dirname(name)
+ if sconsign_dir and not os.path.exists(sconsign_dir):
+ self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
+ SCons.SConsign.File(name, dbm_module)
+
+ def SideEffect(self, side_effect, target):
+ """Tell scons that side_effects are built as side
+ effects of building targets."""
+ side_effects = self.arg2nodes(side_effect, self.fs.Entry)
+ targets = self.arg2nodes(target, self.fs.Entry)
+
+ for side_effect in side_effects:
+ if side_effect.multiple_side_effect_has_builder():
+ raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
+ side_effect.add_source(targets)
+ side_effect.side_effect = 1
+ self.Precious(side_effect)
+ for target in targets:
+ target.side_effects.append(side_effect)
+ return side_effects
+
+ def SourceCode(self, entry, builder):
+ """Arrange for a source code builder for (part of) a tree."""
+ entries = self.arg2nodes(entry, self.fs.Entry)
+ for entry in entries:
+ entry.set_src_builder(builder)
+ return entries
+
+ def SourceSignatures(self, type):
+ global _warn_source_signatures_deprecated
+ if _warn_source_signatures_deprecated:
+ msg = "The env.SourceSignatures() method is deprecated;\n" + \
+ "\tconvert your build to use the env.Decider() method instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
+ _warn_source_signatures_deprecated = False
+ type = self.subst(type)
+ self.src_sig_type = type
+ if type == 'MD5':
+ if not SCons.Util.md5:
+ raise UserError, "MD5 signatures are not available in this version of Python."
+ self.decide_source = self._changed_content
+ elif type == 'timestamp':
+ self.decide_source = self._changed_timestamp_match
+ else:
+ raise UserError, "Unknown source signature type '%s'" % type
+
+ def Split(self, arg):
+ """This function converts a string or list into a list of strings
+ or Nodes. This makes things easier for users by allowing files to
+ be specified as a white-space separated list to be split.
+ The input rules are:
+ - A single string containing names separated by spaces. These will be
+ split apart at the spaces.
+ - A single Node instance
+ - A list containing either strings or Node instances. Any strings
+ in the list are not split at spaces.
+ In all cases, the function returns a list of Nodes and strings."""
+ if SCons.Util.is_List(arg):
+ return map(self.subst, arg)
+ elif SCons.Util.is_String(arg):
+ return string.split(self.subst(arg))
+ else:
+ return [self.subst(arg)]
+
+ def TargetSignatures(self, type):
+ global _warn_target_signatures_deprecated
+ if _warn_target_signatures_deprecated:
+ msg = "The env.TargetSignatures() method is deprecated;\n" + \
+ "\tconvert your build to use the env.Decider() method instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
+ _warn_target_signatures_deprecated = False
+ type = self.subst(type)
+ self.tgt_sig_type = type
+ if type in ('MD5', 'content'):
+ if not SCons.Util.md5:
+ raise UserError, "MD5 signatures are not available in this version of Python."
+ self.decide_target = self._changed_content
+ elif type == 'timestamp':
+ self.decide_target = self._changed_timestamp_match
+ elif type == 'build':
+ self.decide_target = self._changed_build
+ elif type == 'source':
+ self.decide_target = self._changed_source
+ else:
+ raise UserError, "Unknown target signature type '%s'"%type
+
+ def Value(self, value, built_value=None):
+ """
+ """
+ return SCons.Node.Python.Value(value, built_value)
+
+ def VariantDir(self, variant_dir, src_dir, duplicate=1):
+ variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
+ src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
+ self.fs.VariantDir(variant_dir, src_dir, duplicate)
+
+ def FindSourceFiles(self, node='.'):
+ """ returns a list of all source files.
+ """
+ node = self.arg2nodes(node, self.fs.Entry)[0]
+
+ sources = []
+ # Uncomment this and get rid of the global definition when we
+ # drop support for pre-2.2 Python versions.
+ #def build_source(ss, result):
+ # for s in ss:
+ # if isinstance(s, SCons.Node.FS.Dir):
+ # build_source(s.all_children(), result)
+ # elif s.has_builder():
+ # build_source(s.sources, result)
+ # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
+ # result.append(s)
+ build_source(node.all_children(), sources)
+
+ # THIS CODE APPEARS TO HAVE NO EFFECT
+ # # get the final srcnode for all nodes, this means stripping any
+ # # attached build node by calling the srcnode function
+ # for file in sources:
+ # srcnode = file.srcnode()
+ # while srcnode != file.srcnode():
+ # srcnode = file.srcnode()
+
+ # remove duplicates
+ return list(set(sources))
+
+ def FindInstalledFiles(self):
+ """ returns the list of all targets of the Install and InstallAs Builder.
+ """
+ from SCons.Tool import install
+ if install._UNIQUE_INSTALLED_FILES is None:
+ install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
+ return install._UNIQUE_INSTALLED_FILES
+
+class OverrideEnvironment(Base):
+ """A proxy that overrides variables in a wrapped construction
+ environment by returning values from an overrides dictionary in
+ preference to values from the underlying subject environment.
+
+ This is a lightweight (I hope) proxy that passes through most use of
+ attributes to the underlying Environment.Base class, but has just
+ enough additional methods defined to act like a real construction
+ environment with overridden values. It can wrap either a Base
+ construction environment, or another OverrideEnvironment, which
+ can in turn nest arbitrary OverrideEnvironments...
+
+ Note that we do *not* call the underlying base class
+ (SubsitutionEnvironment) initialization, because we get most of those
+ from proxying the attributes of the subject construction environment.
+ But because we subclass SubstitutionEnvironment, this class also
+ has inherited arg2nodes() and subst*() methods; those methods can't
+ be proxied because they need *this* object's methods to fetch the
+ values from the overrides dictionary.
+ """
+
+ def __init__(self, subject, overrides={}):
+ if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
+ self.__dict__['__subject'] = subject
+ self.__dict__['overrides'] = overrides
+
+ # Methods that make this class act like a proxy.
+ def __getattr__(self, name):
+ return getattr(self.__dict__['__subject'], name)
+ def __setattr__(self, name, value):
+ setattr(self.__dict__['__subject'], name, value)
+
+ # Methods that make this class act like a dictionary.
+ def __getitem__(self, key):
+ try:
+ return self.__dict__['overrides'][key]
+ except KeyError:
+ return self.__dict__['__subject'].__getitem__(key)
+ def __setitem__(self, key, value):
+ if not is_valid_construction_var(key):
+ raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
+ self.__dict__['overrides'][key] = value
+ def __delitem__(self, key):
+ try:
+ del self.__dict__['overrides'][key]
+ except KeyError:
+ deleted = 0
+ else:
+ deleted = 1
+ try:
+ result = self.__dict__['__subject'].__delitem__(key)
+ except KeyError:
+ if not deleted:
+ raise
+ result = None
+ return result
+ def get(self, key, default=None):
+ """Emulates the get() method of dictionaries."""
+ try:
+ return self.__dict__['overrides'][key]
+ except KeyError:
+ return self.__dict__['__subject'].get(key, default)
+ def has_key(self, key):
+ try:
+ self.__dict__['overrides'][key]
+ return 1
+ except KeyError:
+ return self.__dict__['__subject'].has_key(key)
+ def __contains__(self, key):
+ if self.__dict__['overrides'].__contains__(key):
+ return 1
+ return self.__dict__['__subject'].__contains__(key)
+ def Dictionary(self):
+ """Emulates the items() method of dictionaries."""
+ d = self.__dict__['__subject'].Dictionary().copy()
+ d.update(self.__dict__['overrides'])
+ return d
+ def items(self):
+ """Emulates the items() method of dictionaries."""
+ return self.Dictionary().items()
+
+ # Overridden private construction environment methods.
+ def _update(self, dict):
+ """Update an environment's values directly, bypassing the normal
+ checks that occur when users try to set items.
+ """
+ self.__dict__['overrides'].update(dict)
+
+ def gvars(self):
+ return self.__dict__['__subject'].gvars()
+
+ def lvars(self):
+ lvars = self.__dict__['__subject'].lvars()
+ lvars.update(self.__dict__['overrides'])
+ return lvars
+
+ # Overridden public construction environment methods.
+ def Replace(self, **kw):
+ kw = copy_non_reserved_keywords(kw)
+ self.__dict__['overrides'].update(semi_deepcopy(kw))
+
+# The entry point that will be used by the external world
+# to refer to a construction environment. This allows the wrapper
+# interface to extend a construction environment for its own purposes
+# by subclassing SCons.Environment.Base and then assigning the
+# class to SCons.Environment.Environment.
+
+Environment = Base
+
+# An entry point for returning a proxy subclass instance that overrides
+# the subst*() methods so they don't actually perform construction
+# variable substitution. This is specifically intended to be the shim
+# layer in between global function calls (which don't want construction
+# variable substitution) and the DefaultEnvironment() (which would
+# substitute variables if left to its own devices)."""
+#
+# We have to wrap this in a function that allows us to delay definition of
+# the class until it's necessary, so that when it subclasses Environment
+# it will pick up whatever Environment subclass the wrapper interface
+# might have assigned to SCons.Environment.Environment.
+
+def NoSubstitutionProxy(subject):
+ class _NoSubstitutionProxy(Environment):
+ def __init__(self, subject):
+ self.__dict__['__subject'] = subject
+ def __getattr__(self, name):
+ return getattr(self.__dict__['__subject'], name)
+ def __setattr__(self, name, value):
+ return setattr(self.__dict__['__subject'], name, value)
+ def raw_to_mode(self, dict):
+ try:
+ raw = dict['raw']
+ except KeyError:
+ pass
+ else:
+ del dict['raw']
+ dict['mode'] = raw
+ def subst(self, string, *args, **kwargs):
+ return string
+ def subst_kw(self, kw, *args, **kwargs):
+ return kw
+ def subst_list(self, string, *args, **kwargs):
+ nargs = (string, self,) + args
+ nkw = kwargs.copy()
+ nkw['gvars'] = {}
+ self.raw_to_mode(nkw)
+ return apply(SCons.Subst.scons_subst_list, nargs, nkw)
+ def subst_target_source(self, string, *args, **kwargs):
+ nargs = (string, self,) + args
+ nkw = kwargs.copy()
+ nkw['gvars'] = {}
+ self.raw_to_mode(nkw)
+ return apply(SCons.Subst.scons_subst, nargs, nkw)
+ return _NoSubstitutionProxy(subject)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml
new file mode 100644
index 0000000..6a245a5
--- /dev/null
+++ b/src/engine/SCons/Environment.xml
@@ -0,0 +1,187 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<cvar name="BUILDERS">
+<summary>
+A dictionary mapping the names of the builders
+available through this environment
+to underlying Builder objects.
+Builders named
+Alias, CFile, CXXFile, DVI, Library, Object, PDF, PostScript, and Program
+are available by default.
+If you initialize this variable when an
+Environment is created:
+
+<example>
+env = Environment(BUILDERS = {'NewBuilder' : foo})
+</example>
+
+the default Builders will no longer be available.
+To use a new Builder object in addition to the default Builders,
+add your new Builder object like this:
+
+<example>
+env = Environment()
+env.Append(BUILDERS = {'NewBuilder' : foo})
+</example>
+
+or this:
+
+<example>
+env = Environment()
+env['BUILDERS]['NewBuilder'] = foo
+</example>
+</summary>
+</cvar>
+
+<cvar name="Dir">
+<summary>
+A function that converts a string
+into a Dir instance relative to the target being built.
+</summary>
+</cvar>
+
+<cvar name="ENV">
+<summary>
+A dictionary of environment variables
+to use when invoking commands. When
+&cv-ENV; is used in a command all list
+values will be joined using the path separator and any other non-string
+values will simply be coerced to a string.
+Note that, by default,
+&scons;
+does
+<emphasis>not</emphasis>
+propagate the environment in force when you
+execute
+&scons;
+to the commands used to build target files.
+This is so that builds will be guaranteed
+repeatable regardless of the environment
+variables set at the time
+&scons;
+is invoked.
+
+If you want to propagate your
+environment variables
+to the commands executed
+to build target files,
+you must do so explicitly:
+
+<example>
+import os
+env = Environment(ENV = os.environ)
+</example>
+
+Note that you can choose only to propagate
+certain environment variables.
+A common example is
+the system
+<envar>PATH</envar>
+environment variable,
+so that
+&scons;
+uses the same utilities
+as the invoking shell (or other process):
+
+<example>
+import os
+env = Environment(ENV = {'PATH' : os.environ['PATH']})
+</example>
+</summary>
+</cvar>
+
+<cvar name="File">
+<summary>
+A function that converts a string into a File instance relative to the
+target being built.
+</summary>
+</cvar>
+
+<cvar name="SCANNERS">
+<summary>
+A list of the available implicit dependency scanners.
+New file scanners may be added by
+appending to this list,
+although the more flexible approach
+is to associate scanners
+with a specific Builder.
+See the sections "Builder Objects"
+and "Scanner Objects,"
+below, for more information.
+</summary>
+</cvar>
+
+<cvar name="CHANGED_SOURCES">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="CHANGED_TARGETS">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="SOURCE">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="SOURCES">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="TARGET">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="TARGETS">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="UNCHANGED_SOURCES">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="UNCHANGED_TARGETS">
+<summary>
+A reserved variable name
+that may not be set or used in a construction environment.
+(See "Variable Substitution," below.)
+</summary>
+</cvar>
+
+<cvar name="TOOLS">
+<summary>
+A list of the names of the Tool specifications
+that are part of this construction environment.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
new file mode 100644
index 0000000..73ebf3c
--- /dev/null
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -0,0 +1,3987 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/EnvironmentTests.py 4577 2009/12/27 19:44:43 scons"
+
+import copy
+import os
+import string
+import StringIO
+import sys
+import TestCmd
+import unittest
+import UserList
+
+from SCons.Environment import *
+import SCons.Warnings
+
+def diff_env(env1, env2):
+ s1 = "env1 = {\n"
+ s2 = "env2 = {\n"
+ d = {}
+ for k in env1._dict.keys() + env2._dict.keys():
+ d[k] = None
+ keys = d.keys()
+ keys.sort()
+ for k in keys:
+ if env1.has_key(k):
+ if env2.has_key(k):
+ if env1[k] != env2[k]:
+ s1 = s1 + " " + repr(k) + " : " + repr(env1[k]) + "\n"
+ s2 = s2 + " " + repr(k) + " : " + repr(env2[k]) + "\n"
+ else:
+ s1 = s1 + " " + repr(k) + " : " + repr(env1[k]) + "\n"
+ elif env2.has_key(k):
+ s2 = s2 + " " + repr(k) + " : " + repr(env2[k]) + "\n"
+ s1 = s1 + "}\n"
+ s2 = s2 + "}\n"
+ return s1 + s2
+
+def diff_dict(d1, d2):
+ s1 = "d1 = {\n"
+ s2 = "d2 = {\n"
+ d = {}
+ for k in d1.keys() + d2.keys():
+ d[k] = None
+ keys = d.keys()
+ keys.sort()
+ for k in keys:
+ if d1.has_key(k):
+ if d2.has_key(k):
+ if d1[k] != d2[k]:
+ s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n"
+ s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n"
+ else:
+ s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n"
+ elif env2.has_key(k):
+ s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n"
+ s1 = s1 + "}\n"
+ s2 = s2 + "}\n"
+ return s1 + s2
+
+called_it = {}
+built_it = {}
+
+class Builder:
+ """A dummy Builder class for testing purposes. "Building"
+ a target is simply setting a value in the dictionary.
+ """
+ def __init__(self, name = None):
+ self.name = name
+
+ def __call__(self, env, target=None, source=None, **kw):
+ global called_it
+ called_it['target'] = target
+ called_it['source'] = source
+ called_it.update(kw)
+
+ def execute(self, target = None, **kw):
+ global built_it
+ built_it[target] = 1
+
+
+
+scanned_it = {}
+
+class Scanner:
+ """A dummy Scanner class for testing purposes. "Scanning"
+ a target is simply setting a value in the dictionary.
+ """
+ def __init__(self, name, skeys=[]):
+ self.name = name
+ self.skeys = skeys
+
+ def __call__(self, filename):
+ global scanned_it
+ scanned_it[filename] = 1
+
+ def __cmp__(self, other):
+ try:
+ return cmp(self.__dict__, other.__dict__)
+ except AttributeError:
+ return 1
+
+ def get_skeys(self, env):
+ return self.skeys
+
+ def __str__(self):
+ return self.name
+
+
+
+class CLVar(UserList.UserList):
+ def __init__(self, seq):
+ if type(seq) == type(''):
+ seq = string.split(seq)
+ UserList.UserList.__init__(self, seq)
+ def __add__(self, other):
+ return UserList.UserList.__add__(self, CLVar(other))
+ def __radd__(self, other):
+ return UserList.UserList.__radd__(self, CLVar(other))
+ def __coerce__(self, other):
+ return (self, CLVar(other))
+
+
+
+class DummyNode:
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return self.name
+ def rfile(self):
+ return self
+ def get_subst_proxy(self):
+ return self
+
+def test_tool( env ):
+ env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+
+class TestEnvironmentFixture:
+ def TestEnvironment(self, *args, **kw):
+ if not kw or not kw.has_key('tools'):
+ kw['tools'] = [test_tool]
+ default_keys = { 'CC' : 'cc',
+ 'CCFLAGS' : '-DNDEBUG',
+ 'ENV' : { 'TMP' : '/tmp' } }
+ for key, value in default_keys.items():
+ if not kw.has_key(key):
+ kw[key] = value
+ if not kw.has_key('BUILDERS'):
+ static_obj = SCons.Builder.Builder(action = {},
+ emitter = {},
+ suffix = '.o',
+ single_source = 1)
+ kw['BUILDERS'] = {'Object' : static_obj}
+ static_obj.add_action('.cpp', 'fake action')
+
+ env = apply(Environment, args, kw)
+ return env
+
+class SubstitutionTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test initializing a SubstitutionEnvironment
+ """
+ env = SubstitutionEnvironment()
+ assert not env.has_key('__env__')
+
+ def test___cmp__(self):
+ """Test comparing SubstitutionEnvironments
+ """
+
+ env1 = SubstitutionEnvironment(XXX = 'x')
+ env2 = SubstitutionEnvironment(XXX = 'x')
+ env3 = SubstitutionEnvironment(XXX = 'xxx')
+ env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x')
+
+ assert env1 == env2
+ assert env1 != env3
+ assert env1 != env4
+
+ def test___delitem__(self):
+ """Test deleting a variable from a SubstitutionEnvironment
+ """
+ env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+ env2 = SubstitutionEnvironment(XXX = 'x')
+ del env1['YYY']
+ assert env1 == env2
+
+ def test___getitem__(self):
+ """Test fetching a variable from a SubstitutionEnvironment
+ """
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert env['XXX'] == 'x', env['XXX']
+
+ def test___setitem__(self):
+ """Test setting a variable in a SubstitutionEnvironment
+ """
+ env1 = SubstitutionEnvironment(XXX = 'x')
+ env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+ env1['YYY'] = 'y'
+ assert env1 == env2
+
+ def test_get(self):
+ """Test the SubstitutionEnvironment get() method
+ """
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert env.get('XXX') == 'x', env.get('XXX')
+ assert env.get('YYY') is None, env.get('YYY')
+
+ def test_has_key(self):
+ """Test the SubstitutionEnvironment has_key() method
+ """
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert env.has_key('XXX')
+ assert not env.has_key('YYY')
+
+ def test_contains(self):
+ """Test the SubstitutionEnvironment __contains__() method
+ """
+ try:
+ 'x' in {'x':1}
+ except TypeError:
+ # TODO(1.5)
+ # An early version of Python that doesn't support "in"
+ # on dictionaries. Just pass the test.
+ pass
+ else:
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert 'XXX' in env
+ assert not 'YYY' in env
+
+ def test_items(self):
+ """Test the SubstitutionEnvironment items() method
+ """
+ env = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+ items = env.items()
+ assert items == [('XXX','x'), ('YYY','y')], items
+
+ def test_arg2nodes(self):
+ """Test the arg2nodes method
+ """
+ env = SubstitutionEnvironment()
+ dict = {}
+ class X(SCons.Node.Node):
+ pass
+ def Factory(name, directory = None, create = 1, dict=dict, X=X):
+ if not dict.has_key(name):
+ dict[name] = X()
+ dict[name].name = name
+ return dict[name]
+
+ nodes = env.arg2nodes("Util.py UtilTests.py", Factory)
+ assert len(nodes) == 1, nodes
+ assert isinstance(nodes[0], X)
+ assert nodes[0].name == "Util.py UtilTests.py"
+
+ import types
+ if hasattr(types, 'UnicodeType'):
+ code = """if 1:
+ nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory)
+ assert len(nodes) == 1, nodes
+ assert isinstance(nodes[0], X)
+ assert nodes[0].name == u"Util.py UtilTests.py"
+ \n"""
+ exec code in globals(), locals()
+
+ nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory)
+ assert len(nodes) == 2, nodes
+ assert isinstance(nodes[0], X)
+ assert isinstance(nodes[1], X)
+ assert nodes[0].name == "Util.py"
+ assert nodes[1].name == "UtilTests.py"
+
+ n1 = Factory("Util.py")
+ nodes = env.arg2nodes([n1, "UtilTests.py"], Factory)
+ assert len(nodes) == 2, nodes
+ assert isinstance(nodes[0], X)
+ assert isinstance(nodes[1], X)
+ assert nodes[0].name == "Util.py"
+ assert nodes[1].name == "UtilTests.py"
+
+ class SConsNode(SCons.Node.Node):
+ pass
+ nodes = env.arg2nodes(SConsNode())
+ assert len(nodes) == 1, nodes
+ assert isinstance(nodes[0], SConsNode), node
+
+ class OtherNode:
+ pass
+ nodes = env.arg2nodes(OtherNode())
+ assert len(nodes) == 1, nodes
+ assert isinstance(nodes[0], OtherNode), node
+
+ def lookup_a(str, F=Factory):
+ if str[0] == 'a':
+ n = F(str)
+ n.a = 1
+ return n
+ else:
+ return None
+
+ def lookup_b(str, F=Factory):
+ if str[0] == 'b':
+ n = F(str)
+ n.b = 1
+ return n
+ else:
+ return None
+
+ env_ll = SubstitutionEnvironment()
+ env_ll.lookup_list = [lookup_a, lookup_b]
+
+ nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
+ assert len(nodes) == 3, nodes
+
+ assert nodes[0].name == 'aaa', nodes[0]
+ assert nodes[0].a == 1, nodes[0]
+ assert not hasattr(nodes[0], 'b'), nodes[0]
+
+ assert nodes[1].name == 'bbb'
+ assert not hasattr(nodes[1], 'a'), nodes[1]
+ assert nodes[1].b == 1, nodes[1]
+
+ assert nodes[2].name == 'ccc'
+ assert not hasattr(nodes[2], 'a'), nodes[1]
+ assert not hasattr(nodes[2], 'b'), nodes[1]
+
+ def lookup_bbbb(str, F=Factory):
+ if str == 'bbbb':
+ n = F(str)
+ n.bbbb = 1
+ return n
+ else:
+ return None
+
+ def lookup_c(str, F=Factory):
+ if str[0] == 'c':
+ n = F(str)
+ n.c = 1
+ return n
+ else:
+ return None
+
+ nodes = env.arg2nodes(['bbbb', 'ccc'], Factory,
+ [lookup_c, lookup_bbbb, lookup_b])
+ assert len(nodes) == 2, nodes
+
+ assert nodes[0].name == 'bbbb'
+ assert not hasattr(nodes[0], 'a'), nodes[1]
+ assert not hasattr(nodes[0], 'b'), nodes[1]
+ assert nodes[0].bbbb == 1, nodes[1]
+ assert not hasattr(nodes[0], 'c'), nodes[0]
+
+ assert nodes[1].name == 'ccc'
+ assert not hasattr(nodes[1], 'a'), nodes[1]
+ assert not hasattr(nodes[1], 'b'), nodes[1]
+ assert not hasattr(nodes[1], 'bbbb'), nodes[0]
+ assert nodes[1].c == 1, nodes[1]
+
+ def test_arg2nodes_target_source(self):
+ """Test the arg2nodes method with target= and source= keywords
+ """
+ targets = [DummyNode('t1'), DummyNode('t2')]
+ sources = [DummyNode('s1'), DummyNode('s2')]
+ env = SubstitutionEnvironment()
+ nodes = env.arg2nodes(['${TARGET}-a',
+ '${SOURCE}-b',
+ '${TARGETS[1]}-c',
+ '${SOURCES[1]}-d'],
+ DummyNode,
+ target=targets,
+ source=sources)
+ names = map(lambda n: n.name, nodes)
+ assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names
+
+ def test_gvars(self):
+ """Test the base class gvars() method"""
+ env = SubstitutionEnvironment()
+ gvars = env.gvars()
+ assert gvars == {}, gvars
+
+ def test_lvars(self):
+ """Test the base class lvars() method"""
+ env = SubstitutionEnvironment()
+ lvars = env.lvars()
+ assert lvars == {}, lvars
+
+ def test_subst(self):
+ """Test substituting construction variables within strings
+
+ Check various combinations, including recursive expansion
+ of variables into other variables.
+ """
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
+ mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
+ assert mystr == "a aA b", mystr
+
+ # Changed the tests below to reflect a bug fix in
+ # subst()
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+ mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
+ assert mystr == "b bA bB b", mystr
+
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+ mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
+ assert mystr == "c cA cB c", mystr
+
+ # Lists:
+ env = SubstitutionEnvironment(AAA = ['a', 'aa', 'aaa'])
+ mystr = env.subst("$AAA")
+ assert mystr == "a aa aaa", mystr
+
+ # Tuples:
+ env = SubstitutionEnvironment(AAA = ('a', 'aa', 'aaa'))
+ mystr = env.subst("$AAA")
+ assert mystr == "a aa aaa", mystr
+
+ t1 = DummyNode('t1')
+ t2 = DummyNode('t2')
+ s1 = DummyNode('s1')
+ s2 = DummyNode('s2')
+
+ env = SubstitutionEnvironment(AAA = 'aaa')
+ s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
+ assert s == "aaa t1 s1 s2", s
+ s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
+ assert s == "aaa t1 t2 s1", s
+
+ # Test callables in the SubstitutionEnvironment
+ def foo(target, source, env, for_signature):
+ assert str(target) == 't', target
+ assert str(source) == 's', source
+ return env["FOO"]
+
+ env = SubstitutionEnvironment(BAR=foo, FOO='baz')
+ t = DummyNode('t')
+ s = DummyNode('s')
+
+ subst = env.subst('test $BAR', target=t, source=s)
+ assert subst == 'test baz', subst
+
+ # Test not calling callables in the SubstitutionEnvironment
+ if 0:
+ # This will take some serious surgery to subst() and
+ # subst_list(), so just leave these tests out until we can
+ # do that.
+ def bar(arg):
+ pass
+
+ env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
+
+ subst = env.subst('$BAR', call=None)
+ assert subst is bar, subst
+
+ subst = env.subst('$FOO', call=None)
+ assert subst is bar, subst
+
+ def test_subst_kw(self):
+ """Test substituting construction variables within dictionaries"""
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
+ kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
+ assert len(kw) == 2, kw
+ assert kw['a'] == 'aaa', kw['a']
+ assert kw['bbb'] == 'b', kw['bbb']
+
+ def test_subst_list(self):
+ """Test substituting construction variables in command lists
+ """
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
+ l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
+ assert l == [["a", "aA", "b"]], l
+
+ # Changed the tests below to reflect a bug fix in
+ # subst()
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+ l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
+ assert l == [["b", "bA", "bB", "b"]], l
+
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+ l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
+ assert l == [["c", "cA", "cB", "c"]], mystr
+
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
+ lst = env.subst_list([ "$AAA", "B $CCC" ])
+ assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
+
+ t1 = DummyNode('t1')
+ t2 = DummyNode('t2')
+ s1 = DummyNode('s1')
+ s2 = DummyNode('s2')
+
+ env = SubstitutionEnvironment(AAA = 'aaa')
+ s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
+ assert s == [["aaa", "t1", "s1", "s2"]], s
+ s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
+ assert s == [["aaa", "t1", "t2", "s1"]], s
+
+ # Test callables in the SubstitutionEnvironment
+ def foo(target, source, env, for_signature):
+ assert str(target) == 't', target
+ assert str(source) == 's', source
+ return env["FOO"]
+
+ env = SubstitutionEnvironment(BAR=foo, FOO='baz')
+ t = DummyNode('t')
+ s = DummyNode('s')
+
+ lst = env.subst_list('test $BAR', target=t, source=s)
+ assert lst == [['test', 'baz']], lst
+
+ # Test not calling callables in the SubstitutionEnvironment
+ if 0:
+ # This will take some serious surgery to subst() and
+ # subst_list(), so just leave these tests out until we can
+ # do that.
+ def bar(arg):
+ pass
+
+ env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
+
+ subst = env.subst_list('$BAR', call=None)
+ assert subst is bar, subst
+
+ subst = env.subst_list('$FOO', call=None)
+ assert subst is bar, subst
+
+ def test_subst_path(self):
+ """Test substituting a path list
+ """
+ class MyProxy:
+ def __init__(self, val):
+ self.val = val
+ def get(self):
+ return self.val + '-proxy'
+
+ class MyNode:
+ def __init__(self, val):
+ self.val = val
+ def get_subst_proxy(self):
+ return self
+ def __str__(self):
+ return self.val
+
+ class MyObj:
+ def get(self):
+ return self
+
+ env = SubstitutionEnvironment(FOO='foo',
+ BAR='bar',
+ LIST=['one', 'two'],
+ PROXY=MyProxy('my1'))
+
+ r = env.subst_path('$FOO')
+ assert r == ['foo'], r
+
+ r = env.subst_path(['$FOO', 'xxx', '$BAR'])
+ assert r == ['foo', 'xxx', 'bar'], r
+
+ r = env.subst_path(['$FOO', '$LIST', '$BAR'])
+ assert map(str, r) == ['foo', 'one two', 'bar'], r
+
+ r = env.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR'])
+ assert r == ['foo', '', '', 'bar'], r
+
+ r = env.subst_path(['$FOO', '$TARGET', '$BAR'], target=MyNode('ttt'))
+ assert map(str, r) == ['foo', 'ttt', 'bar'], r
+
+ r = env.subst_path(['$FOO', '$SOURCE', '$BAR'], source=MyNode('sss'))
+ assert map(str, r) == ['foo', 'sss', 'bar'], r
+
+ n = MyObj()
+
+ r = env.subst_path(['$PROXY', MyProxy('my2'), n])
+ assert r == ['my1-proxy', 'my2-proxy', n], r
+
+ class StringableObj:
+ def __init__(self, s):
+ self.s = s
+ def __str__(self):
+ return self.s
+
+ env = SubstitutionEnvironment(FOO=StringableObj("foo"),
+ BAR=StringableObj("bar"))
+
+ r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
+ assert r == [ "foo/bar", "bar/baz" ], r
+
+ r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
+ assert r == [ "bar/foo", "baz/bar" ], r
+
+ r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
+ assert r == [ "bar/foo/bar", "baz/bar/baz" ], r
+
+ def test_subst_target_source(self):
+ """Test the base environment subst_target_source() method"""
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
+ mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
+ assert mystr == "a aA b", mystr
+
+ def test_backtick(self):
+ """Test the backtick() method for capturing command output"""
+ env = SubstitutionEnvironment()
+
+ test = TestCmd.TestCmd(workdir = '')
+ test.write('stdout.py', """\
+import sys
+sys.stdout.write('this came from stdout.py\\n')
+sys.exit(0)
+""")
+ test.write('stderr.py', """\
+import sys
+sys.stderr.write('this came from stderr.py\\n')
+sys.exit(0)
+""")
+ test.write('fail.py', """\
+import sys
+sys.exit(1)
+""")
+ test.write('echo.py', """\
+import os, sys
+sys.stdout.write(os.environ['ECHO'] + '\\n')
+sys.exit(0)
+""")
+
+ save_stderr = sys.stderr
+
+ python = '"' + sys.executable + '"'
+
+ try:
+ sys.stderr = StringIO.StringIO()
+ cmd = '%s %s' % (python, test.workpath('stdout.py'))
+ output = env.backtick(cmd)
+ errout = sys.stderr.getvalue()
+ assert output == 'this came from stdout.py\n', output
+ assert errout == '', errout
+
+ sys.stderr = StringIO.StringIO()
+ cmd = '%s %s' % (python, test.workpath('stderr.py'))
+ output = env.backtick(cmd)
+ errout = sys.stderr.getvalue()
+ assert output == '', output
+ assert errout == 'this came from stderr.py\n', errout
+
+ sys.stderr = StringIO.StringIO()
+ cmd = '%s %s' % (python, test.workpath('fail.py'))
+ try:
+ env.backtick(cmd)
+ except OSError, e:
+ assert str(e) == "'%s' exited 1" % cmd, str(e)
+ else:
+ self.fail("did not catch expected OSError")
+
+ sys.stderr = StringIO.StringIO()
+ cmd = '%s %s' % (python, test.workpath('echo.py'))
+ env['ENV'] = os.environ.copy()
+ env['ENV']['ECHO'] = 'this came from ECHO'
+ output = env.backtick(cmd)
+ errout = sys.stderr.getvalue()
+ assert output == 'this came from ECHO\n', output
+ assert errout == '', errout
+
+ finally:
+ sys.stderr = save_stderr
+
+ def test_AddMethod(self):
+ """Test the AddMethod() method"""
+ env = SubstitutionEnvironment(FOO = 'foo')
+
+ def func(self):
+ return 'func-' + self['FOO']
+
+ assert not hasattr(env, 'func')
+ env.AddMethod(func)
+ r = env.func()
+ assert r == 'func-foo', r
+
+ assert not hasattr(env, 'bar')
+ env.AddMethod(func, 'bar')
+ r = env.bar()
+ assert r == 'func-foo', r
+
+ def func2(self, arg=''):
+ return 'func2-' + self['FOO'] + arg
+
+ env.AddMethod(func2)
+ r = env.func2()
+ assert r == 'func2-foo', r
+ r = env.func2('-xxx')
+ assert r == 'func2-foo-xxx', r
+
+ env.AddMethod(func2, 'func')
+ r = env.func()
+ assert r == 'func2-foo', r
+ r = env.func('-yyy')
+ assert r == 'func2-foo-yyy', r
+
+ # Test that clones of clones correctly re-bind added methods.
+ env1 = Environment(FOO = '1')
+ env1.AddMethod(func2)
+ env2 = env1.Clone(FOO = '2')
+ env3 = env2.Clone(FOO = '3')
+ env4 = env3.Clone(FOO = '4')
+ r = env1.func2()
+ assert r == 'func2-1', r
+ r = env2.func2()
+ assert r == 'func2-2', r
+ r = env3.func2()
+ assert r == 'func2-3', r
+ r = env4.func2()
+ assert r == 'func2-4', r
+
+ # Test that clones don't re-bind an attribute that the user
+ env1 = Environment(FOO = '1')
+ env1.AddMethod(func2)
+ def replace_func2():
+ return 'replace_func2'
+ env1.func2 = replace_func2
+ env2 = env1.Clone(FOO = '2')
+ r = env2.func2()
+ assert r == 'replace_func2', r
+
+ def test_Override(self):
+ "Test overriding construction variables"
+ env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4)
+ assert env['ONE'] == 1, env['ONE']
+ assert env['TWO'] == 2, env['TWO']
+ assert env['THREE'] == 3, env['THREE']
+ assert env['FOUR'] == 4, env['FOUR']
+
+ env2 = env.Override({'TWO' : '10',
+ 'THREE' :'x $THREE y',
+ 'FOUR' : ['x', '$FOUR', 'y']})
+ assert env2['ONE'] == 1, env2['ONE']
+ assert env2['TWO'] == '10', env2['TWO']
+ assert env2['THREE'] == 'x 3 y', env2['THREE']
+ assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR']
+
+ assert env['ONE'] == 1, env['ONE']
+ assert env['TWO'] == 2, env['TWO']
+ assert env['THREE'] == 3, env['THREE']
+ assert env['FOUR'] == 4, env['FOUR']
+
+ env2.Replace(ONE = "won")
+ assert env2['ONE'] == "won", env2['ONE']
+ assert env['ONE'] == 1, env['ONE']
+
+ def test_ParseFlags(self):
+ """Test the ParseFlags() method
+ """
+ env = SubstitutionEnvironment()
+
+ empty = {
+ 'ASFLAGS' : [],
+ 'CFLAGS' : [],
+ 'CCFLAGS' : [],
+ 'CPPDEFINES' : [],
+ 'CPPFLAGS' : [],
+ 'CPPPATH' : [],
+ 'FRAMEWORKPATH' : [],
+ 'FRAMEWORKS' : [],
+ 'LIBPATH' : [],
+ 'LIBS' : [],
+ 'LINKFLAGS' : [],
+ 'RPATH' : [],
+ }
+
+ d = env.ParseFlags(None)
+ assert d == empty, d
+
+ d = env.ParseFlags('')
+ assert d == empty, d
+
+ d = env.ParseFlags([])
+ assert d == empty, d
+
+ s = "-I/usr/include/fum -I bar -X\n" + \
+ '-I"C:\\Program Files\\ASCEND\\include" ' + \
+ "-L/usr/fax -L foo -lxxx -l yyy " + \
+ '-L"C:\\Program Files\\ASCEND" -lascend ' + \
+ "-Wa,-as -Wl,-link " + \
+ "-Wl,-rpath=rpath1 " + \
+ "-Wl,-R,rpath2 " + \
+ "-Wl,-Rrpath3 " + \
+ "-Wp,-cpp " + \
+ "-std=c99 " + \
+ "-framework Carbon " + \
+ "-frameworkdir=fwd1 " + \
+ "-Ffwd2 " + \
+ "-F fwd3 " + \
+ "-pthread " + \
+ "-mno-cygwin -mwindows " + \
+ "-arch i386 -isysroot /tmp +DD64 " + \
+ "-DFOO -DBAR=value -D BAZ "
+
+ d = env.ParseFlags(s)
+
+ assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
+ assert d['CFLAGS'] == ['-std=c99']
+ assert d['CCFLAGS'] == ['-X', '-Wa,-as',
+ '-pthread', '-mno-cygwin',
+ ('-arch', 'i386'), ('-isysroot', '/tmp'),
+ '+DD64'], d['CCFLAGS']
+ assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
+ assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
+ assert d['CPPPATH'] == ['/usr/include/fum',
+ 'bar',
+ 'C:\\Program Files\\ASCEND\\include'], d['CPPPATH']
+ assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
+ assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS']
+ assert d['LIBPATH'] == ['/usr/fax',
+ 'foo',
+ 'C:\\Program Files\\ASCEND'], d['LIBPATH']
+ LIBS = map(str, d['LIBS'])
+ assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS)
+ assert d['LINKFLAGS'] == ['-Wl,-link', '-pthread',
+ '-mno-cygwin', '-mwindows',
+ ('-arch', 'i386'),
+ ('-isysroot', '/tmp'),
+ '+DD64'], d['LINKFLAGS']
+ assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH']
+
+
+ def test_MergeFlags(self):
+ """Test the MergeFlags() method
+ """
+ env = SubstitutionEnvironment()
+ env.MergeFlags('')
+ assert not env.has_key('CCFLAGS'), env['CCFLAGS']
+ env.MergeFlags('-X')
+ assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+ env.MergeFlags('-X')
+ assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+
+ env = SubstitutionEnvironment(CCFLAGS=None)
+ env.MergeFlags('-Y')
+ assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
+
+ env = SubstitutionEnvironment()
+ env.MergeFlags({'A':['aaa'], 'B':['bbb']})
+ assert env['A'] == ['aaa'], env['A']
+ assert env['B'] == ['bbb'], env['B']
+
+# def test_MergeShellPaths(self):
+# """Test the MergeShellPaths() method
+# """
+# env = Environment()
+# env.MergeShellPaths({})
+# assert not env['ENV'].has_key('INCLUDE'), env['INCLUDE']
+# env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
+# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
+# env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
+# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
+# env.MergeShellPaths({'INCLUDE': r'xyz'})
+# assert env['ENV']['INCLUDE'] == r'xyz%sc:\Program Files\Stuff'%os.pathsep, env['ENV']['INCLUDE']
+
+# env = Environment()
+# env['ENV']['INCLUDE'] = 'xyz'
+# env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']} )
+# assert env['ENV']['INCLUDE'] == r'c:/inc1%sc:/inc2%sxyz'%(os.pathsep, os.pathsep), env['ENV']['INCLUDE']
+
+# # test prepend=0
+# env = Environment()
+# env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}, prepend=0)
+# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
+# env.MergeShellPaths({'INCLUDE': r'xyz'}, prepend=0)
+# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff%sxyz'%os.pathsep, env['ENV']['INCLUDE']
+
+
+class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
+
+ reserved_variables = [
+ 'CHANGED_SOURCES',
+ 'CHANGED_TARGETS',
+ 'SOURCE',
+ 'SOURCES',
+ 'TARGET',
+ 'TARGETS',
+ 'UNCHANGED_SOURCES',
+ 'UNCHANGED_TARGETS',
+ ]
+
+ def test___init__(self):
+ """Test construction Environment creation
+
+ Create two with identical arguments and check that
+ they compare the same.
+ """
+ env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ env2 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ assert env1 == env2, diff_env(env1, env2)
+
+ assert not env1.has_key('__env__')
+ assert not env2.has_key('__env__')
+
+ def test_variables(self):
+ """Test that variables only get applied once."""
+ class FakeOptions:
+ def __init__(self, key, val):
+ self.calls = 0
+ self.key = key
+ self.val = val
+ def keys(self):
+ return [self.key]
+ def Update(self, env):
+ env[self.key] = self.val
+ self.calls = self.calls + 1
+
+ o = FakeOptions('AAA', 'fake_opt')
+ env = Environment(variables=o, AAA='keyword_arg')
+ assert o.calls == 1, o.calls
+ assert env['AAA'] == 'fake_opt', env['AAA']
+
+ def test_get(self):
+ """Test the get() method."""
+ env = self.TestEnvironment(aaa = 'AAA')
+
+ x = env.get('aaa')
+ assert x == 'AAA', x
+ x = env.get('aaa', 'XXX')
+ assert x == 'AAA', x
+ x = env.get('bbb')
+ assert x is None, x
+ x = env.get('bbb', 'XXX')
+ assert x == 'XXX', x
+
+ def test_Builder_calls(self):
+ """Test Builder calls through different environments
+ """
+ global called_it
+
+ b1 = Builder()
+ b2 = Builder()
+
+ env = Environment()
+ env.Replace(BUILDERS = { 'builder1' : b1,
+ 'builder2' : b2 })
+ called_it = {}
+ env.builder1('in1')
+ assert called_it['target'] is None, called_it
+ assert called_it['source'] == ['in1'], called_it
+
+ called_it = {}
+ env.builder2(source = 'in2', xyzzy = 1)
+ assert called_it['target'] is None, called_it
+ assert called_it['source'] == ['in2'], called_it
+ assert called_it['xyzzy'] == 1, called_it
+
+ called_it = {}
+ env.builder1(foo = 'bar')
+ assert called_it['foo'] == 'bar', called_it
+ assert called_it['target'] is None, called_it
+ assert called_it['source'] is None, called_it
+
+ def test_BuilderWrapper_attributes(self):
+ """Test getting and setting of BuilderWrapper attributes
+ """
+ b1 = Builder()
+ b2 = Builder()
+ e1 = Environment()
+ e2 = Environment()
+
+ e1.Replace(BUILDERS = {'b' : b1})
+ bw = e1.b
+
+ assert bw.env is e1
+ bw.env = e2
+ assert bw.env is e2
+
+ assert bw.builder is b1
+ bw.builder = b2
+ assert bw.builder is b2
+
+ self.assertRaises(AttributeError, getattr, bw, 'foobar')
+ bw.foobar = 42
+ assert bw.foobar is 42
+
+ # This unit test is currently disabled because we don't think the
+ # underlying method it tests (Environment.BuilderWrapper.execute())
+ # is necessary, but we're leaving the code here for now in case
+ # that's mistaken.
+ def _DO_NOT_test_Builder_execs(self):
+ """Test Builder execution through different environments
+
+ One environment is initialized with a single
+ Builder object, one with a list of a single Builder
+ object, and one with a list of two Builder objects.
+ """
+ global built_it
+
+ b1 = Builder()
+ b2 = Builder()
+
+ built_it = {}
+ env3 = Environment()
+ env3.Replace(BUILDERS = { 'builder1' : b1,
+ 'builder2' : b2 })
+ env3.builder1.execute(target = 'out1')
+ env3.builder2.execute(target = 'out2')
+ env3.builder1.execute(target = 'out3')
+ assert built_it['out1']
+ assert built_it['out2']
+ assert built_it['out3']
+
+ env4 = env3.Clone()
+ assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (
+env4.builder1.env, env3)
+ assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (
+env4.builder1.env, env3)
+
+ # Now test BUILDERS as a dictionary.
+ built_it = {}
+ env5 = self.TestEnvironment(BUILDERS={ 'foo' : b1 })
+ env5['BUILDERS']['bar'] = b2
+ env5.foo.execute(target='out1')
+ env5.bar.execute(target='out2')
+ assert built_it['out1']
+ assert built_it['out2']
+
+ built_it = {}
+ env6 = Environment()
+ env6['BUILDERS'] = { 'foo' : b1,
+ 'bar' : b2 }
+ env6.foo.execute(target='out1')
+ env6.bar.execute(target='out2')
+ assert built_it['out1']
+ assert built_it['out2']
+
+
+
+ def test_Scanners(self):
+ """Test setting SCANNERS in various ways
+
+ One environment is initialized with a single
+ Scanner object, one with a list of a single Scanner
+ object, and one with a list of two Scanner objects.
+ """
+ global scanned_it
+
+ s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
+ s2 = Scanner(name = 'scanner2', skeys = [".m4"])
+ s3 = Scanner(name = 'scanner3', skeys = [".m4", ".m5"])
+ s4 = Scanner(name = 'scanner4', skeys = [None])
+
+# XXX Tests for scanner execution through different environments,
+# XXX if we ever want to do that some day
+# scanned_it = {}
+# env1 = self.TestEnvironment(SCANNERS = s1)
+# env1.scanner1(filename = 'out1')
+# assert scanned_it['out1']
+#
+# scanned_it = {}
+# env2 = self.TestEnvironment(SCANNERS = [s1])
+# env1.scanner1(filename = 'out1')
+# assert scanned_it['out1']
+#
+# scanned_it = {}
+# env3 = Environment()
+# env3.Replace(SCANNERS = [s1])
+# env3.scanner1(filename = 'out1')
+# env3.scanner2(filename = 'out2')
+# env3.scanner1(filename = 'out3')
+# assert scanned_it['out1']
+# assert scanned_it['out2']
+# assert scanned_it['out3']
+
+ suffixes = [".c", ".cc", ".cxx", ".m4", ".m5"]
+
+ env = Environment()
+ try: del env['SCANNERS']
+ except KeyError: pass
+ s = map(env.get_scanner, suffixes)
+ assert s == [None, None, None, None, None], s
+
+ env = self.TestEnvironment(SCANNERS = [])
+ s = map(env.get_scanner, suffixes)
+ assert s == [None, None, None, None, None], s
+
+ env.Replace(SCANNERS = [s1])
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, None, None], s
+
+ env.Append(SCANNERS = [s2])
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, s2, None], s
+
+ env.AppendUnique(SCANNERS = [s3])
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, s2, s3], s
+
+ env = env.Clone(SCANNERS = [s2])
+ s = map(env.get_scanner, suffixes)
+ assert s == [None, None, None, s2, None], s
+
+ env['SCANNERS'] = [s1]
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, None, None], s
+
+ env.PrependUnique(SCANNERS = [s2, s1])
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, s2, None], s
+
+ env.Prepend(SCANNERS = [s3])
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, s3, s3], s
+
+ # Verify behavior of case-insensitive suffix matches on Windows.
+ uc_suffixes = map(string.upper, suffixes)
+
+ env = Environment(SCANNERS = [s1, s2, s3],
+ PLATFORM = 'linux')
+
+ s = map(env.get_scanner, suffixes)
+ assert s == [s1, s1, None, s2, s3], s
+
+ s = map(env.get_scanner, uc_suffixes)
+ assert s == [None, None, None, None, None], s
+
+ env['PLATFORM'] = 'win32'
+
+ s = map(env.get_scanner, uc_suffixes)
+ assert s == [s1, s1, None, s2, s3], s
+
+ # Verify behavior for a scanner returning None (on Windows
+ # where we might try to perform case manipulation on None).
+ env.Replace(SCANNERS = [s4])
+ s = map(env.get_scanner, suffixes)
+ assert s == [None, None, None, None, None], s
+
+ def test_ENV(self):
+ """Test setting the external ENV in Environments
+ """
+ env = Environment()
+ assert env.Dictionary().has_key('ENV')
+
+ env = self.TestEnvironment(ENV = { 'PATH' : '/foo:/bar' })
+ assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
+
+ def test_ReservedVariables(self):
+ """Test warning generation when reserved variable names are set"""
+
+ reserved_variables = [
+ 'CHANGED_SOURCES',
+ 'CHANGED_TARGETS',
+ 'SOURCE',
+ 'SOURCES',
+ 'TARGET',
+ 'TARGETS',
+ 'UNCHANGED_SOURCES',
+ 'UNCHANGED_TARGETS',
+ ]
+
+ warning = SCons.Warnings.ReservedVariableWarning
+ SCons.Warnings.enableWarningClass(warning)
+ old = SCons.Warnings.warningAsException(1)
+
+ try:
+ env4 = Environment()
+ for kw in self.reserved_variables:
+ exc_caught = None
+ try:
+ env4[kw] = 'xyzzy'
+ except warning:
+ exc_caught = 1
+ assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
+ assert not env4.has_key(kw), "`%s' variable was incorrectly set" % kw
+ finally:
+ SCons.Warnings.warningAsException(old)
+
+ def test_FutureReservedVariables(self):
+ """Test warning generation when future reserved variable names are set"""
+
+ future_reserved_variables = []
+
+ warning = SCons.Warnings.FutureReservedVariableWarning
+ SCons.Warnings.enableWarningClass(warning)
+ old = SCons.Warnings.warningAsException(1)
+
+ try:
+ env4 = Environment()
+ for kw in future_reserved_variables:
+ exc_caught = None
+ try:
+ env4[kw] = 'xyzzy'
+ except warning:
+ exc_caught = 1
+ assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw
+ assert env4.has_key(kw), "`%s' variable was not set" % kw
+ finally:
+ SCons.Warnings.warningAsException(old)
+
+ def test_IllegalVariables(self):
+ """Test that use of illegal variables raises an exception"""
+ env = Environment()
+ def test_it(var, env=env):
+ exc_caught = None
+ try:
+ env[var] = 1
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch UserError for '%s'" % var
+ env['aaa'] = 1
+ assert env['aaa'] == 1, env['aaa']
+ test_it('foo/bar')
+ test_it('foo.bar')
+ test_it('foo-bar')
+
+ def test_autogenerate(dict):
+ """Test autogenerating variables in a dictionary."""
+
+ drive, p = os.path.splitdrive(os.getcwd())
+ def normalize_path(path, drive=drive):
+ if path[0] in '\\/':
+ path = drive + path
+ path = os.path.normpath(path)
+ drive, path = os.path.splitdrive(path)
+ return string.lower(drive) + path
+
+ env = dict.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ],
+ LIBLINKPREFIX = 'foo',
+ LIBLINKSUFFIX = 'bar')
+
+ def RDirs(pathlist, fs=env.fs):
+ return fs.Dir('xx').Rfindalldirs(pathlist)
+
+ env['RDirs'] = RDirs
+ flags = env.subst_list('$_LIBFLAGS', 1)[0]
+ assert flags == ['foobar', 'foobar', 'foobazbar'], flags
+
+ blat = env.fs.Dir('blat')
+
+ env.Replace(CPPPATH = [ 'foo', '$FOO/bar', blat ],
+ INCPREFIX = 'foo ',
+ INCSUFFIX = 'bar',
+ FOO = 'baz')
+ flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
+ expect = [ '$(',
+ normalize_path('foo'),
+ normalize_path('xx/foobar'),
+ normalize_path('foo'),
+ normalize_path('xx/baz/bar'),
+ normalize_path('foo'),
+ normalize_path('blatbar'),
+ '$)',
+ ]
+ assert flags == expect, flags
+
+ env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ],
+ INCPREFIX = 'foo ',
+ INCSUFFIX = 'bar',
+ FOO = 'baz')
+ flags = env.subst_list('$_F77INCFLAGS', 1)[0]
+ expect = [ '$(',
+ normalize_path('foo'),
+ normalize_path('xx/foobar'),
+ normalize_path('foo'),
+ normalize_path('xx/baz/bar'),
+ normalize_path('foo'),
+ normalize_path('blatbar'),
+ '$)',
+ ]
+ assert flags == expect, flags
+
+ env.Replace(CPPPATH = '', F77PATH = '', LIBPATH = '')
+ l = env.subst_list('$_CPPINCFLAGS')
+ assert l == [[]], l
+ l = env.subst_list('$_F77INCFLAGS')
+ assert l == [[]], l
+ l = env.subst_list('$_LIBDIRFLAGS')
+ assert l == [[]], l
+
+ env.fs.Repository('/rep1')
+ env.fs.Repository('/rep2')
+ env.Replace(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
+ INCPREFIX = '-I ',
+ INCSUFFIX = 'XXX',
+ FOO = 'baz')
+ flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
+ expect = [ '$(',
+ '-I', normalize_path('xx/fooXXX'),
+ '-I', normalize_path('/rep1/xx/fooXXX'),
+ '-I', normalize_path('/rep2/xx/fooXXX'),
+ '-I', normalize_path('/a/bXXX'),
+ '-I', normalize_path('xx/baz/barXXX'),
+ '-I', normalize_path('/rep1/xx/baz/barXXX'),
+ '-I', normalize_path('/rep2/xx/baz/barXXX'),
+ '-I', normalize_path('blatXXX'),
+ '$)'
+ ]
+ def normalize_if_path(arg, np=normalize_path):
+ if arg not in ('$(','$)','-I'):
+ return np(str(arg))
+ return arg
+ flags = map(normalize_if_path, flags)
+ assert flags == expect, flags
+
+ def test_platform(self):
+ """Test specifying a platform callable when instantiating."""
+ class platform:
+ def __str__(self): return "TestPlatform"
+ def __call__(self, env): env['XYZZY'] = 777
+
+ def tool(env):
+ env['SET_TOOL'] = 'initialized'
+ assert env['PLATFORM'] == "TestPlatform"
+
+ env = self.TestEnvironment(platform = platform(), tools = [tool])
+ assert env['XYZZY'] == 777, env
+ assert env['PLATFORM'] == "TestPlatform"
+ assert env['SET_TOOL'] == "initialized"
+
+ def test_Default_PLATFORM(self):
+ """Test overriding the default PLATFORM variable"""
+ class platform:
+ def __str__(self): return "DefaultTestPlatform"
+ def __call__(self, env): env['XYZZY'] = 888
+
+ def tool(env):
+ env['SET_TOOL'] = 'abcde'
+ assert env['PLATFORM'] == "DefaultTestPlatform"
+
+ import SCons.Defaults
+ save = SCons.Defaults.ConstructionEnvironment.copy()
+ try:
+ import SCons.Defaults
+ SCons.Defaults.ConstructionEnvironment.update({
+ 'PLATFORM' : platform(),
+ })
+ env = self.TestEnvironment(tools = [tool])
+ assert env['XYZZY'] == 888, env
+ assert env['PLATFORM'] == "DefaultTestPlatform"
+ assert env['SET_TOOL'] == "abcde"
+ finally:
+ SCons.Defaults.ConstructionEnvironment = save
+
+ def test_tools(self):
+ """Test specifying a tool callable when instantiating."""
+ def t1(env):
+ env['TOOL1'] = 111
+ def t2(env):
+ env['TOOL2'] = 222
+ def t3(env):
+ env['AAA'] = env['XYZ']
+ def t4(env):
+ env['TOOL4'] = 444
+ env = self.TestEnvironment(tools = [t1, t2, t3], XYZ = 'aaa')
+ assert env['TOOL1'] == 111, env['TOOL1']
+ assert env['TOOL2'] == 222, env
+ assert env['AAA'] == 'aaa', env
+ t4(env)
+ assert env['TOOL4'] == 444, env
+
+ test = TestCmd.TestCmd(workdir = '')
+ test.write('faketool.py', """\
+def generate(env, **kw):
+ for k, v in kw.items():
+ env[k] = v
+
+def exists(env):
+ return 1
+""")
+
+ env = self.TestEnvironment(tools = [('faketool', {'a':1, 'b':2, 'c':3})],
+ toolpath = [test.workpath('')])
+ assert env['a'] == 1, env['a']
+ assert env['b'] == 2, env['b']
+ assert env['c'] == 3, env['c']
+
+ def test_Default_TOOLS(self):
+ """Test overriding the default TOOLS variable"""
+ def t5(env):
+ env['TOOL5'] = 555
+ def t6(env):
+ env['TOOL6'] = 666
+ def t7(env):
+ env['BBB'] = env['XYZ']
+ def t8(env):
+ env['TOOL8'] = 888
+
+ import SCons.Defaults
+ save = SCons.Defaults.ConstructionEnvironment.copy()
+ try:
+ SCons.Defaults.ConstructionEnvironment.update({
+ 'TOOLS' : [t5, t6, t7],
+ })
+ env = Environment(XYZ = 'bbb')
+ assert env['TOOL5'] == 555, env['TOOL5']
+ assert env['TOOL6'] == 666, env
+ assert env['BBB'] == 'bbb', env
+ t8(env)
+ assert env['TOOL8'] == 888, env
+ finally:
+ SCons.Defaults.ConstructionEnvironment = save
+
+ def test_null_tools(self):
+ """Test specifying a tool of None is OK."""
+ def t1(env):
+ env['TOOL1'] = 111
+ def t2(env):
+ env['TOOL2'] = 222
+ env = self.TestEnvironment(tools = [t1, None, t2], XYZ = 'aaa')
+ assert env['TOOL1'] == 111, env['TOOL1']
+ assert env['TOOL2'] == 222, env
+ assert env['XYZ'] == 'aaa', env
+ env = self.TestEnvironment(tools = [None], XYZ = 'xyz')
+ assert env['XYZ'] == 'xyz', env
+ env = self.TestEnvironment(tools = [t1, '', t2], XYZ = 'ddd')
+ assert env['TOOL1'] == 111, env['TOOL1']
+ assert env['TOOL2'] == 222, env
+ assert env['XYZ'] == 'ddd', env
+
+ def test_concat(self):
+ "Test _concat()"
+ e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
+ s = e1.subst
+ x = s("${_concat('', '', '', __env__)}")
+ assert x == '', x
+ x = s("${_concat('', [], '', __env__)}")
+ assert x == '', x
+ x = s("${_concat(PRE, '', SUF, __env__)}")
+ assert x == '', x
+ x = s("${_concat(PRE, STR, SUF, __env__)}")
+ assert x == 'prea bsuf', x
+ x = s("${_concat(PRE, LIST, SUF, __env__)}")
+ assert x == 'preasuf prebsuf', x
+
+ def test_gvars(self):
+ """Test the Environment gvars() method"""
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
+ gvars = env.gvars()
+ assert gvars['XXX'] == 'x', gvars['XXX']
+ assert gvars['YYY'] == 'y', gvars['YYY']
+ assert gvars['ZZZ'] == 'z', gvars['ZZZ']
+
+ def test__update(self):
+ """Test the _update() method"""
+ env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z')
+ assert env['X'] == 'x', env['X']
+ assert env['Y'] == 'y', env['Y']
+ assert env['Z'] == 'z', env['Z']
+ env._update({'X' : 'xxx',
+ 'TARGET' : 't',
+ 'TARGETS' : 'ttt',
+ 'SOURCE' : 's',
+ 'SOURCES' : 'sss',
+ 'Z' : 'zzz'})
+ assert env['X'] == 'xxx', env['X']
+ assert env['Y'] == 'y', env['Y']
+ assert env['Z'] == 'zzz', env['Z']
+ assert env['TARGET'] == 't', env['TARGET']
+ assert env['TARGETS'] == 'ttt', env['TARGETS']
+ assert env['SOURCE'] == 's', env['SOURCE']
+ assert env['SOURCES'] == 'sss', env['SOURCES']
+
+
+
+ def test_Append(self):
+ """Test appending to construction variables in an Environment
+ """
+
+ b1 = Environment()['BUILDERS']
+ b2 = Environment()['BUILDERS']
+ assert b1 == b2, diff_dict(b1, b2)
+
+ import UserDict
+ UD = UserDict.UserDict
+ import UserList
+ UL = UserList.UserList
+
+ cases = [
+ 'a1', 'A1', 'a1A1',
+ 'a2', ['A2'], ['a2', 'A2'],
+ 'a3', UL(['A3']), UL(['a', '3', 'A3']),
+ 'a4', '', 'a4',
+ 'a5', [], ['a5'],
+ 'a6', UL([]), UL(['a', '6']),
+ 'a7', [''], ['a7', ''],
+ 'a8', UL(['']), UL(['a', '8', '']),
+
+ ['e1'], 'E1', ['e1', 'E1'],
+ ['e2'], ['E2'], ['e2', 'E2'],
+ ['e3'], UL(['E3']), UL(['e3', 'E3']),
+ ['e4'], '', ['e4'],
+ ['e5'], [], ['e5'],
+ ['e6'], UL([]), UL(['e6']),
+ ['e7'], [''], ['e7', ''],
+ ['e8'], UL(['']), UL(['e8', '']),
+
+ UL(['i1']), 'I1', UL(['i1', 'I', '1']),
+ UL(['i2']), ['I2'], UL(['i2', 'I2']),
+ UL(['i3']), UL(['I3']), UL(['i3', 'I3']),
+ UL(['i4']), '', UL(['i4']),
+ UL(['i5']), [], UL(['i5']),
+ UL(['i6']), UL([]), UL(['i6']),
+ UL(['i7']), [''], UL(['i7', '']),
+ UL(['i8']), UL(['']), UL(['i8', '']),
+
+ {'d1':1}, 'D1', {'d1':1, 'D1':None},
+ {'d2':1}, ['D2'], {'d2':1, 'D2':None},
+ {'d3':1}, UL(['D3']), {'d3':1, 'D3':None},
+ {'d4':1}, {'D4':1}, {'d4':1, 'D4':1},
+ {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}),
+
+ UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}),
+ UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}),
+ UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}),
+ UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}),
+ UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
+
+ '', 'M1', 'M1',
+ '', ['M2'], ['M2'],
+ '', UL(['M3']), UL(['M3']),
+ '', '', '',
+ '', [], [],
+ '', UL([]), UL([]),
+ '', [''], [''],
+ '', UL(['']), UL(['']),
+
+ [], 'N1', ['N1'],
+ [], ['N2'], ['N2'],
+ [], UL(['N3']), UL(['N3']),
+ [], '', [],
+ [], [], [],
+ [], UL([]), UL([]),
+ [], [''], [''],
+ [], UL(['']), UL(['']),
+
+ UL([]), 'O1', ['O', '1'],
+ UL([]), ['O2'], ['O2'],
+ UL([]), UL(['O3']), UL(['O3']),
+ UL([]), '', UL([]),
+ UL([]), [], UL([]),
+ UL([]), UL([]), UL([]),
+ UL([]), [''], UL(['']),
+ UL([]), UL(['']), UL(['']),
+
+ [''], 'P1', ['', 'P1'],
+ [''], ['P2'], ['', 'P2'],
+ [''], UL(['P3']), UL(['', 'P3']),
+ [''], '', [''],
+ [''], [], [''],
+ [''], UL([]), UL(['']),
+ [''], [''], ['', ''],
+ [''], UL(['']), UL(['', '']),
+
+ UL(['']), 'Q1', ['', 'Q', '1'],
+ UL(['']), ['Q2'], ['', 'Q2'],
+ UL(['']), UL(['Q3']), UL(['', 'Q3']),
+ UL(['']), '', UL(['']),
+ UL(['']), [], UL(['']),
+ UL(['']), UL([]), UL(['']),
+ UL(['']), [''], UL(['', '']),
+ UL(['']), UL(['']), UL(['', '']),
+ ]
+
+ env = Environment()
+ failed = 0
+ while cases:
+ input, append, expect = cases[:3]
+ env['XXX'] = copy.copy(input)
+ try:
+ env.Append(XXX = append)
+ except Exception, e:
+ if failed == 0: print
+ print " %s Append %s exception: %s" % \
+ (repr(input), repr(append), e)
+ failed = failed + 1
+ else:
+ result = env['XXX']
+ if result != expect:
+ if failed == 0: print
+ print " %s Append %s => %s did not match %s" % \
+ (repr(input), repr(append), repr(result), repr(expect))
+ failed = failed + 1
+ del cases[:3]
+ assert failed == 0, "%d Append() cases failed" % failed
+
+ env['UL'] = UL(['foo'])
+ env.Append(UL = 'bar')
+ result = env['UL']
+ assert isinstance(result, UL), repr(result)
+ assert result == ['foo', 'b', 'a', 'r'], result
+
+ env['CLVar'] = CLVar(['foo'])
+ env.Append(CLVar = 'bar')
+ result = env['CLVar']
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['foo', 'bar'], result
+
+ class C:
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return self.name
+ def __cmp__(self, other):
+ raise "should not compare"
+
+ ccc = C('ccc')
+
+ env2 = self.TestEnvironment(CCC1 = ['c1'], CCC2 = ccc)
+ env2.Append(CCC1 = ccc, CCC2 = ['c2'])
+ assert env2['CCC1'][0] == 'c1', env2['CCC1']
+ assert env2['CCC1'][1] is ccc, env2['CCC1']
+ assert env2['CCC2'][0] is ccc, env2['CCC2']
+ assert env2['CCC2'][1] == 'c2', env2['CCC2']
+
+ env3 = self.TestEnvironment(X = {'x1' : 7})
+ env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
+ assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
+ assert env3['Y'] == {'y1': 10}, env3['Y']
+
+ env4 = self.TestEnvironment(BUILDERS = {'z1' : 11})
+ env4.Append(BUILDERS = {'z2' : 12})
+ assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+ assert hasattr(env4, 'z1')
+ assert hasattr(env4, 'z2')
+
+ def test_AppendENVPath(self):
+ """Test appending to an ENV path."""
+ env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+ MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+ # have to include the pathsep here so that the test will work on UNIX too.
+ env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
+ env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
+ env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
+ env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
+ # this should do nothing since delete_existing is 0
+ env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
+ assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+ assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
+
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub1', 'sub2')
+ p=env1['ENV']['PATH']
+ env1.AppendENVPath('PATH','#sub1', sep = ';')
+ env1.AppendENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
+ assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH']
+
+ def test_AppendUnique(self):
+ """Test appending to unique values to construction variables
+
+ This strips values that are already present when lists are
+ involved."""
+ env = self.TestEnvironment(AAA1 = 'a1',
+ AAA2 = 'a2',
+ AAA3 = 'a3',
+ AAA4 = 'a4',
+ AAA5 = 'a5',
+ BBB1 = ['b1'],
+ BBB2 = ['b2'],
+ BBB3 = ['b3'],
+ BBB4 = ['b4'],
+ BBB5 = ['b5'],
+ CCC1 = '',
+ CCC2 = '',
+ DDD1 = ['a', 'b', 'c'])
+ env.AppendUnique(AAA1 = 'a1',
+ AAA2 = ['a2'],
+ AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
+ AAA4 = 'a4.new',
+ AAA5 = ['a5.new'],
+ BBB1 = 'b1',
+ BBB2 = ['b2'],
+ BBB3 = ['b3', 'c', 'd', 'c', 'b3'],
+ BBB4 = 'b4.new',
+ BBB5 = ['b5.new'],
+ CCC1 = 'c1',
+ CCC2 = ['c2'],
+ DDD1 = 'b')
+
+ assert env['AAA1'] == 'a1a1', env['AAA1']
+ assert env['AAA2'] == ['a2'], env['AAA2']
+ assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3']
+ assert env['AAA4'] == 'a4a4.new', env['AAA4']
+ assert env['AAA5'] == ['a5', 'a5.new'], env['AAA5']
+ assert env['BBB1'] == ['b1'], env['BBB1']
+ assert env['BBB2'] == ['b2'], env['BBB2']
+ assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3']
+ assert env['BBB4'] == ['b4', 'b4.new'], env['BBB4']
+ assert env['BBB5'] == ['b5', 'b5.new'], env['BBB5']
+ assert env['CCC1'] == 'c1', env['CCC1']
+ assert env['CCC2'] == ['c2'], env['CCC2']
+ assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
+
+ env.AppendUnique(DDD1 = 'b', delete_existing=1)
+ assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end
+ env.AppendUnique(DDD1 = ['a','b'], delete_existing=1)
+ assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
+ env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
+ assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
+
+ env['CLVar'] = CLVar([])
+ env.AppendUnique(CLVar = 'bar')
+ result = env['CLVar']
+ if sys.version[0] == '1' or sys.version[:3] == '2.0':
+ # Python 2.0 and before have a quirky behavior where CLVar([])
+ # actually matches '' and [] due to different __coerce__()
+ # semantics in the UserList implementation. It isn't worth a
+ # lot of effort to get this corner case to work identically
+ # (support for Python 1.5 support will die soon anyway),
+ # so just treat it separately for now.
+ assert result == 'bar', result
+ else:
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['bar'], result
+
+ env['CLVar'] = CLVar(['abc'])
+ env.AppendUnique(CLVar = 'bar')
+ result = env['CLVar']
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['abc', 'bar'], result
+
+ env['CLVar'] = CLVar(['bar'])
+ env.AppendUnique(CLVar = 'bar')
+ result = env['CLVar']
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['bar'], result
+
+ def test_Clone(self):
+ """Test construction environment copying
+
+ Update the copy independently afterwards and check that
+ the original remains intact (that is, no dangling
+ references point to objects in the copied environment).
+ Clone the original with some construction variable
+ updates and check that the original remains intact
+ and the copy has the updated values.
+ """
+ env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ env2 = env1.Clone()
+ env1copy = env1.Clone()
+ assert env1copy == env1copy
+ assert env2 == env2
+ env2.Replace(YYY = 'yyy')
+ assert env2 == env2
+ assert env1 != env2
+ assert env1 == env1copy
+
+ env3 = env1.Clone(XXX = 'x3', ZZZ = 'z3')
+ assert env3 == env3
+ assert env3.Dictionary('XXX') == 'x3'
+ assert env3.Dictionary('YYY') == 'y'
+ assert env3.Dictionary('ZZZ') == 'z3'
+ assert env1 == env1copy
+
+ # Ensure that lists and dictionaries are
+ # deep copied, but not instances.
+ class TestA:
+ pass
+ env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ],
+ ZZZ = { 1:2, 3:4 })
+ env2=env1.Clone()
+ env2.Dictionary('YYY').append(4)
+ env2.Dictionary('ZZZ')[5] = 6
+ assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
+ assert 4 in env2.Dictionary('YYY')
+ assert not 4 in env1.Dictionary('YYY')
+ assert env2.Dictionary('ZZZ').has_key(5)
+ assert not env1.Dictionary('ZZZ').has_key(5)
+
+ #
+ env1 = self.TestEnvironment(BUILDERS = {'b1' : 1})
+ assert hasattr(env1, 'b1'), "env1.b1 was not set"
+ assert env1.b1.object == env1, "b1.object doesn't point to env1"
+ env2 = env1.Clone(BUILDERS = {'b2' : 2})
+ assert env2 is env2
+ assert env2 == env2
+ assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
+ assert env1.b1.object == env1, "b1.object was changed"
+ assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
+ assert hasattr(env2, 'b2'), "env2.b2 was not set"
+ assert env2.b2.object == env2, "b2.object doesn't point to env2"
+
+ # Ensure that specifying new tools in a copied environment
+ # works.
+ def foo(env): env['FOO'] = 1
+ def bar(env): env['BAR'] = 2
+ def baz(env): env['BAZ'] = 3
+ env1 = self.TestEnvironment(tools=[foo])
+ env2 = env1.Clone()
+ env3 = env1.Clone(tools=[bar, baz])
+
+ assert env1.get('FOO') is 1
+ assert env1.get('BAR') is None
+ assert env1.get('BAZ') is None
+ assert env2.get('FOO') is 1
+ assert env2.get('BAR') is None
+ assert env2.get('BAZ') is None
+ assert env3.get('FOO') is 1
+ assert env3.get('BAR') is 2
+ assert env3.get('BAZ') is 3
+
+ # Ensure that recursive variable substitution when copying
+ # environments works properly.
+ env1 = self.TestEnvironment(CCFLAGS = '-DFOO', XYZ = '-DXYZ')
+ env2 = env1.Clone(CCFLAGS = '$CCFLAGS -DBAR',
+ XYZ = ['-DABC', 'x $XYZ y', '-DDEF'])
+ x = env2.get('CCFLAGS')
+ assert x == '-DFOO -DBAR', x
+ x = env2.get('XYZ')
+ assert x == ['-DABC', 'x -DXYZ y', '-DDEF'], x
+
+ # Ensure that special properties of a class don't get
+ # lost on copying.
+ env1 = self.TestEnvironment(FLAGS = CLVar('flag1 flag2'))
+ x = env1.get('FLAGS')
+ assert x == ['flag1', 'flag2'], x
+ env2 = env1.Clone()
+ env2.Append(FLAGS = 'flag3 flag4')
+ x = env2.get('FLAGS')
+ assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
+
+ # Test that the environment stores the toolpath and
+ # re-uses it for copies.
+ test = TestCmd.TestCmd(workdir = '')
+
+ test.write('xxx.py', """\
+def exists(env):
+ 1
+def generate(env):
+ env['XXX'] = 'one'
+""")
+
+ test.write('yyy.py', """\
+def exists(env):
+ 1
+def generate(env):
+ env['YYY'] = 'two'
+""")
+
+ env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
+ assert env['XXX'] == 'one', env['XXX']
+ env = env.Clone(tools=['yyy'])
+ assert env['YYY'] == 'two', env['YYY']
+
+
+ # Test that
+ real_value = [4]
+
+ def my_tool(env, rv=real_value):
+ assert env['KEY_THAT_I_WANT'] == rv[0]
+ env['KEY_THAT_I_WANT'] = rv[0] + 1
+
+ env = self.TestEnvironment()
+
+ real_value[0] = 5
+ env = env.Clone(KEY_THAT_I_WANT=5, tools=[my_tool])
+ assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
+
+ real_value[0] = 6
+ env = env.Clone(KEY_THAT_I_WANT=6, tools=[my_tool])
+ assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
+
+
+ def test_Copy(self):
+ """Test copying using the old env.Copy() method"""
+ env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ env2 = env1.Copy()
+ env1copy = env1.Copy()
+ assert env1copy == env1copy
+ assert env2 == env2
+ env2.Replace(YYY = 'yyy')
+ assert env2 == env2
+ assert env1 != env2
+ assert env1 == env1copy
+
+ def test_Detect(self):
+ """Test Detect()ing tools"""
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub1', 'sub2')
+ sub1 = test.workpath('sub1')
+ sub2 = test.workpath('sub2')
+
+ if sys.platform == 'win32':
+ test.write(['sub1', 'xxx'], "sub1/xxx\n")
+ test.write(['sub2', 'xxx'], "sub2/xxx\n")
+
+ env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
+
+ x = env.Detect('xxx.exe')
+ assert x is None, x
+
+ test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
+
+ env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
+
+ x = env.Detect('xxx.exe')
+ assert x == 'xxx.exe', x
+
+ test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
+
+ x = env.Detect('xxx.exe')
+ assert x == 'xxx.exe', x
+
+ else:
+ test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
+ test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
+
+ env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
+
+ x = env.Detect('xxx.exe')
+ assert x is None, x
+
+ sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
+ os.chmod(sub2_xxx_exe, 0755)
+
+ env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
+
+ x = env.Detect('xxx.exe')
+ assert x == 'xxx.exe', x
+
+ sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
+ os.chmod(sub1_xxx_exe, 0755)
+
+ x = env.Detect('xxx.exe')
+ assert x == 'xxx.exe', x
+
+ env = self.TestEnvironment(ENV = { 'PATH' : [] })
+ x = env.Detect('xxx.exe')
+ assert x is None, x
+
+ def test_Dictionary(self):
+ """Test retrieval of known construction variables
+
+ Fetch them from the Dictionary and check for well-known
+ defaults that get inserted.
+ """
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
+ assert env.Dictionary('XXX') == 'x'
+ assert env.Dictionary('YYY') == 'y'
+ assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
+ xxx, zzz = env.Dictionary('XXX', 'ZZZ')
+ assert xxx == 'x'
+ assert zzz == 'z'
+ assert env.Dictionary().has_key('BUILDERS')
+ assert env.Dictionary().has_key('CC')
+ assert env.Dictionary().has_key('CCFLAGS')
+ assert env.Dictionary().has_key('ENV')
+
+ assert env['XXX'] == 'x'
+ env['XXX'] = 'foo'
+ assert env.Dictionary('XXX') == 'foo'
+ del env['XXX']
+ assert not env.Dictionary().has_key('XXX')
+
+ def test_FindIxes(self):
+ "Test FindIxes()"
+ env = self.TestEnvironment(LIBPREFIX='lib',
+ LIBSUFFIX='.a',
+ SHLIBPREFIX='lib',
+ SHLIBSUFFIX='.so',
+ PREFIX='pre',
+ SUFFIX='post')
+
+ paths = [os.path.join('dir', 'libfoo.a'),
+ os.path.join('dir', 'libfoo.so')]
+
+ assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
+ assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ assert None is env.FindIxes(paths, 'PREFIX', 'POST')
+
+ paths = ['libfoo.a', 'prefoopost']
+
+ assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
+ assert None is env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
+
+ def test_ParseConfig(self):
+ """Test the ParseConfig() method"""
+ env = self.TestEnvironment(COMMAND='command',
+ ASFLAGS='assembler',
+ CCFLAGS=[''],
+ CPPDEFINES=[],
+ CPPFLAGS=[''],
+ CPPPATH='string',
+ FRAMEWORKPATH=[],
+ FRAMEWORKS=[],
+ LIBPATH=['list'],
+ LIBS='',
+ LINKFLAGS=[''],
+ RPATH=[])
+
+ orig_backtick = env.backtick
+ class my_backtick:
+ def __init__(self, save_command, output):
+ self.save_command = save_command
+ self.output = output
+ def __call__(self, command):
+ self.save_command.append(command)
+ return self.output
+
+ try:
+ save_command = []
+ env.backtick = my_backtick(save_command,
+ "-I/usr/include/fum -I bar -X\n" + \
+ "-L/usr/fax -L foo -lxxx -l yyy " + \
+ "-Wa,-as -Wl,-link " + \
+ "-Wl,-rpath=rpath1 " + \
+ "-Wl,-R,rpath2 " + \
+ "-Wl,-Rrpath3 " + \
+ "-Wp,-cpp abc " + \
+ "-framework Carbon " + \
+ "-frameworkdir=fwd1 " + \
+ "-Ffwd2 " + \
+ "-F fwd3 " + \
+ "-pthread " + \
+ "-mno-cygwin -mwindows " + \
+ "-arch i386 -isysroot /tmp +DD64 " + \
+ "-DFOO -DBAR=value")
+ env.ParseConfig("fake $COMMAND")
+ assert save_command == ['fake command'], save_command
+ assert env['ASFLAGS'] == ['assembler', '-as'], env['ASFLAGS']
+ assert env['CCFLAGS'] == ['', '-X', '-Wa,-as',
+ '-pthread', '-mno-cygwin',
+ ('-arch', 'i386'), ('-isysroot', '/tmp'),
+ '+DD64'], env['CCFLAGS']
+ assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES']
+ assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS']
+ assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
+ assert env['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env['FRAMEWORKPATH']
+ assert env['FRAMEWORKS'] == ['Carbon'], env['FRAMEWORKS']
+ assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH']
+ assert env['LIBS'] == ['xxx', 'yyy', env.File('abc')], env['LIBS']
+ assert env['LINKFLAGS'] == ['', '-Wl,-link', '-pthread',
+ '-mno-cygwin', '-mwindows',
+ ('-arch', 'i386'),
+ ('-isysroot', '/tmp'),
+ '+DD64'], env['LINKFLAGS']
+ assert env['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env['RPATH']
+
+ env.backtick = my_backtick([], "-Ibar")
+ env.ParseConfig("fake2")
+ assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
+ env.ParseConfig("fake2", unique=0)
+ assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env['CPPPATH']
+ finally:
+ env.backtick = orig_backtick
+
+ def test_ParseDepends(self):
+ """Test the ParseDepends() method"""
+ test = TestCmd.TestCmd(workdir = '')
+
+ test.write('single', """
+#file: dependency
+
+f0: \
+ d1 \
+ d2 \
+ d3 \
+
+""")
+
+ test.write('multiple', """
+f1: foo
+f2 f3: bar
+f4: abc def
+#file: dependency
+f5: \
+ ghi \
+ jkl \
+ mno \
+""")
+
+ env = self.TestEnvironment(SINGLE = test.workpath('single'))
+
+ tlist = []
+ dlist = []
+ def my_depends(target, dependency, tlist=tlist, dlist=dlist):
+ tlist.extend(target)
+ dlist.extend(dependency)
+
+ env.Depends = my_depends
+
+ env.ParseDepends(test.workpath('does_not_exist'))
+
+ exc_caught = None
+ try:
+ env.ParseDepends(test.workpath('does_not_exist'), must_exist=1)
+ except IOError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected IOError"
+
+ del tlist[:]
+ del dlist[:]
+
+ env.ParseDepends('$SINGLE', only_one=1)
+ t = map(str, tlist)
+ d = map(str, dlist)
+ assert t == ['f0'], t
+ assert d == ['d1', 'd2', 'd3'], d
+
+ del tlist[:]
+ del dlist[:]
+
+ env.ParseDepends(test.workpath('multiple'))
+ t = map(str, tlist)
+ d = map(str, dlist)
+ assert t == ['f1', 'f2', 'f3', 'f4', 'f5'], t
+ assert d == ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d
+
+ exc_caught = None
+ try:
+ env.ParseDepends(test.workpath('multiple'), only_one=1)
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected UserError"
+
+ def test_Platform(self):
+ """Test the Platform() method"""
+ env = self.TestEnvironment(WIN32='win32', NONE='no-such-platform')
+
+ exc_caught = None
+ try:
+ env.Platform('does_not_exist')
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected UserError"
+
+ exc_caught = None
+ try:
+ env.Platform('$NONE')
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected UserError"
+
+ env.Platform('posix')
+ assert env['OBJSUFFIX'] == '.o', env['OBJSUFFIX']
+
+ env.Platform('$WIN32')
+ assert env['OBJSUFFIX'] == '.obj', env['OBJSUFFIX']
+
+ def test_Prepend(self):
+ """Test prepending to construction variables in an Environment
+ """
+ import UserDict
+ UD = UserDict.UserDict
+ import UserList
+ UL = UserList.UserList
+
+ cases = [
+ 'a1', 'A1', 'A1a1',
+ 'a2', ['A2'], ['A2', 'a2'],
+ 'a3', UL(['A3']), UL(['A3', 'a', '3']),
+ 'a4', '', 'a4',
+ 'a5', [], ['a5'],
+ 'a6', UL([]), UL(['a', '6']),
+ 'a7', [''], ['', 'a7'],
+ 'a8', UL(['']), UL(['', 'a', '8']),
+
+ ['e1'], 'E1', ['E1', 'e1'],
+ ['e2'], ['E2'], ['E2', 'e2'],
+ ['e3'], UL(['E3']), UL(['E3', 'e3']),
+ ['e4'], '', ['e4'],
+ ['e5'], [], ['e5'],
+ ['e6'], UL([]), UL(['e6']),
+ ['e7'], [''], ['', 'e7'],
+ ['e8'], UL(['']), UL(['', 'e8']),
+
+ UL(['i1']), 'I1', UL(['I', '1', 'i1']),
+ UL(['i2']), ['I2'], UL(['I2', 'i2']),
+ UL(['i3']), UL(['I3']), UL(['I3', 'i3']),
+ UL(['i4']), '', UL(['i4']),
+ UL(['i5']), [], UL(['i5']),
+ UL(['i6']), UL([]), UL(['i6']),
+ UL(['i7']), [''], UL(['', 'i7']),
+ UL(['i8']), UL(['']), UL(['', 'i8']),
+
+ {'d1':1}, 'D1', {'d1':1, 'D1':None},
+ {'d2':1}, ['D2'], {'d2':1, 'D2':None},
+ {'d3':1}, UL(['D3']), {'d3':1, 'D3':None},
+ {'d4':1}, {'D4':1}, {'d4':1, 'D4':1},
+ {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}),
+
+ UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}),
+ UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}),
+ UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}),
+ UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}),
+ UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
+
+ '', 'M1', 'M1',
+ '', ['M2'], ['M2'],
+ '', UL(['M3']), UL(['M3']),
+ '', '', '',
+ '', [], [],
+ '', UL([]), UL([]),
+ '', [''], [''],
+ '', UL(['']), UL(['']),
+
+ [], 'N1', ['N1'],
+ [], ['N2'], ['N2'],
+ [], UL(['N3']), UL(['N3']),
+ [], '', [],
+ [], [], [],
+ [], UL([]), UL([]),
+ [], [''], [''],
+ [], UL(['']), UL(['']),
+
+ UL([]), 'O1', UL(['O', '1']),
+ UL([]), ['O2'], UL(['O2']),
+ UL([]), UL(['O3']), UL(['O3']),
+ UL([]), '', UL([]),
+ UL([]), [], UL([]),
+ UL([]), UL([]), UL([]),
+ UL([]), [''], UL(['']),
+ UL([]), UL(['']), UL(['']),
+
+ [''], 'P1', ['P1', ''],
+ [''], ['P2'], ['P2', ''],
+ [''], UL(['P3']), UL(['P3', '']),
+ [''], '', [''],
+ [''], [], [''],
+ [''], UL([]), UL(['']),
+ [''], [''], ['', ''],
+ [''], UL(['']), UL(['', '']),
+
+ UL(['']), 'Q1', UL(['Q', '1', '']),
+ UL(['']), ['Q2'], UL(['Q2', '']),
+ UL(['']), UL(['Q3']), UL(['Q3', '']),
+ UL(['']), '', UL(['']),
+ UL(['']), [], UL(['']),
+ UL(['']), UL([]), UL(['']),
+ UL(['']), [''], UL(['', '']),
+ UL(['']), UL(['']), UL(['', '']),
+ ]
+
+ env = Environment()
+ failed = 0
+ while cases:
+ input, prepend, expect = cases[:3]
+ env['XXX'] = copy.copy(input)
+ try:
+ env.Prepend(XXX = prepend)
+ except Exception, e:
+ if failed == 0: print
+ print " %s Prepend %s exception: %s" % \
+ (repr(input), repr(prepend), e)
+ failed = failed + 1
+ else:
+ result = env['XXX']
+ if result != expect:
+ if failed == 0: print
+ print " %s Prepend %s => %s did not match %s" % \
+ (repr(input), repr(prepend), repr(result), repr(expect))
+ failed = failed + 1
+ del cases[:3]
+ assert failed == 0, "%d Prepend() cases failed" % failed
+
+ env['UL'] = UL(['foo'])
+ env.Prepend(UL = 'bar')
+ result = env['UL']
+ assert isinstance(result, UL), repr(result)
+ assert result == ['b', 'a', 'r', 'foo'], result
+
+ env['CLVar'] = CLVar(['foo'])
+ env.Prepend(CLVar = 'bar')
+ result = env['CLVar']
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['bar', 'foo'], result
+
+ env3 = self.TestEnvironment(X = {'x1' : 7})
+ env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
+ assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
+ assert env3['Y'] == {'y1': 10}, env3['Y']
+
+ env4 = self.TestEnvironment(BUILDERS = {'z1' : 11})
+ env4.Prepend(BUILDERS = {'z2' : 12})
+ assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+ assert hasattr(env4, 'z1')
+ assert hasattr(env4, 'z2')
+
+ def test_PrependENVPath(self):
+ """Test prepending to an ENV path."""
+ env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+ MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+ # have to include the pathsep here so that the test will work on UNIX too.
+ env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';')
+ env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
+ env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
+ env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
+ # this should do nothing since delete_existing is 0
+ env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
+ assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+ assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+
+ test = TestCmd.TestCmd(workdir = '')
+ test.subdir('sub1', 'sub2')
+ p=env1['ENV']['PATH']
+ env1.PrependENVPath('PATH','#sub1', sep = ';')
+ env1.PrependENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
+ assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH']
+
+ def test_PrependUnique(self):
+ """Test prepending unique values to construction variables
+
+ This strips values that are already present when lists are
+ involved."""
+ env = self.TestEnvironment(AAA1 = 'a1',
+ AAA2 = 'a2',
+ AAA3 = 'a3',
+ AAA4 = 'a4',
+ AAA5 = 'a5',
+ BBB1 = ['b1'],
+ BBB2 = ['b2'],
+ BBB3 = ['b3'],
+ BBB4 = ['b4'],
+ BBB5 = ['b5'],
+ CCC1 = '',
+ CCC2 = '',
+ DDD1 = ['a', 'b', 'c'])
+ env.PrependUnique(AAA1 = 'a1',
+ AAA2 = ['a2'],
+ AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
+ AAA4 = 'a4.new',
+ AAA5 = ['a5.new'],
+ BBB1 = 'b1',
+ BBB2 = ['b2'],
+ BBB3 = ['b3', 'b', 'c', 'b3'],
+ BBB4 = 'b4.new',
+ BBB5 = ['b5.new'],
+ CCC1 = 'c1',
+ CCC2 = ['c2'],
+ DDD1 = 'b')
+ assert env['AAA1'] == 'a1a1', env['AAA1']
+ assert env['AAA2'] == ['a2'], env['AAA2']
+ assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3']
+ assert env['AAA4'] == 'a4.newa4', env['AAA4']
+ assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5']
+ assert env['BBB1'] == ['b1'], env['BBB1']
+ assert env['BBB2'] == ['b2'], env['BBB2']
+ assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3']
+ assert env['BBB4'] == ['b4.new', 'b4'], env['BBB4']
+ assert env['BBB5'] == ['b5.new', 'b5'], env['BBB5']
+ assert env['CCC1'] == 'c1', env['CCC1']
+ assert env['CCC2'] == ['c2'], env['CCC2']
+ assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
+
+ env.PrependUnique(DDD1 = 'b', delete_existing=1)
+ assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front
+ env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
+ assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
+ env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
+ assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1']
+
+
+ env['CLVar'] = CLVar([])
+ env.PrependUnique(CLVar = 'bar')
+ result = env['CLVar']
+ if sys.version[0] == '1' or sys.version[:3] == '2.0':
+ # Python 2.0 and before have a quirky behavior where CLVar([])
+ # actually matches '' and [] due to different __coerce__()
+ # semantics in the UserList implementation. It isn't worth a
+ # lot of effort to get this corner case to work identically
+ # (support for Python 1.5 support will die soon anyway),
+ # so just treat it separately for now.
+ assert result == 'bar', result
+ else:
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['bar'], result
+
+ env['CLVar'] = CLVar(['abc'])
+ env.PrependUnique(CLVar = 'bar')
+ result = env['CLVar']
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['bar', 'abc'], result
+
+ env['CLVar'] = CLVar(['bar'])
+ env.PrependUnique(CLVar = 'bar')
+ result = env['CLVar']
+ assert isinstance(result, CLVar), repr(result)
+ assert result == ['bar'], result
+
+ def test_Replace(self):
+ """Test replacing construction variables in an Environment
+
+ After creation of the Environment, of course.
+ """
+ env1 = self.TestEnvironment(AAA = 'a', BBB = 'b')
+ env1.Replace(BBB = 'bbb', CCC = 'ccc')
+
+ env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
+ assert env1 == env2, diff_env(env1, env2)
+
+ env3 = self.TestEnvironment(BUILDERS = {'b1' : 1})
+ assert hasattr(env3, 'b1'), "b1 was not set"
+ env3.Replace(BUILDERS = {'b2' : 2})
+ assert not hasattr(env3, 'b1'), "b1 was not cleared"
+ assert hasattr(env3, 'b2'), "b2 was not set"
+
+ def test_ReplaceIxes(self):
+ "Test ReplaceIxes()"
+ env = self.TestEnvironment(LIBPREFIX='lib',
+ LIBSUFFIX='.a',
+ SHLIBPREFIX='lib',
+ SHLIBSUFFIX='.so',
+ PREFIX='pre',
+ SUFFIX='post')
+
+ assert 'libfoo.a' == env.ReplaceIxes('libfoo.so',
+ 'SHLIBPREFIX', 'SHLIBSUFFIX',
+ 'LIBPREFIX', 'LIBSUFFIX')
+
+ assert os.path.join('dir', 'libfoo.a') == env.ReplaceIxes(os.path.join('dir', 'libfoo.so'),
+ 'SHLIBPREFIX', 'SHLIBSUFFIX',
+ 'LIBPREFIX', 'LIBSUFFIX')
+
+ assert 'libfoo.a' == env.ReplaceIxes('prefoopost',
+ 'PREFIX', 'SUFFIX',
+ 'LIBPREFIX', 'LIBSUFFIX')
+
+ def test_SetDefault(self):
+ """Test the SetDefault method"""
+ env = self.TestEnvironment(tools = [])
+ env.SetDefault(V1 = 1)
+ env.SetDefault(V1 = 2)
+ assert env['V1'] == 1
+ env['V2'] = 2
+ env.SetDefault(V2 = 1)
+ assert env['V2'] == 2
+
+ def test_Tool(self):
+ """Test the Tool() method"""
+ env = self.TestEnvironment(LINK='link', NONE='no-such-tool')
+
+ exc_caught = None
+ try:
+ env.Tool('does_not_exist')
+ except SCons.Errors.EnvironmentError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected EnvironmentError"
+
+ exc_caught = None
+ try:
+ env.Tool('$NONE')
+ except SCons.Errors.EnvironmentError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected EnvironmentError"
+
+ # Use a non-existent toolpath directory just to make sure we
+ # can call Tool() with the keyword argument.
+ env.Tool('cc', toolpath=['/no/such/directory'])
+ assert env['CC'] == 'cc', env['CC']
+
+ env.Tool('$LINK')
+ assert env['LINK'] == '$SMARTLINK', env['LINK']
+
+ # Test that the environment stores the toolpath and
+ # re-uses it for later calls.
+ test = TestCmd.TestCmd(workdir = '')
+
+ test.write('xxx.py', """\
+def exists(env):
+ 1
+def generate(env):
+ env['XXX'] = 'one'
+""")
+
+ test.write('yyy.py', """\
+def exists(env):
+ 1
+def generate(env):
+ env['YYY'] = 'two'
+""")
+
+ env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
+ assert env['XXX'] == 'one', env['XXX']
+ env.Tool('yyy')
+ assert env['YYY'] == 'two', env['YYY']
+
+ def test_WhereIs(self):
+ """Test the WhereIs() method"""
+ test = TestCmd.TestCmd(workdir = '')
+
+ sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
+ sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
+ sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
+ sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
+
+ test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
+
+ if sys.platform != 'win32':
+ test.write(sub1_xxx_exe, "\n")
+
+ os.mkdir(sub2_xxx_exe)
+
+ test.write(sub3_xxx_exe, "\n")
+ os.chmod(sub3_xxx_exe, 0777)
+
+ test.write(sub4_xxx_exe, "\n")
+ os.chmod(sub4_xxx_exe, 0777)
+
+ env_path = os.environ['PATH']
+
+ pathdirs_1234 = [ test.workpath('sub1'),
+ test.workpath('sub2'),
+ test.workpath('sub3'),
+ test.workpath('sub4'),
+ ] + string.split(env_path, os.pathsep)
+
+ pathdirs_1243 = [ test.workpath('sub1'),
+ test.workpath('sub2'),
+ test.workpath('sub4'),
+ test.workpath('sub3'),
+ ] + string.split(env_path, os.pathsep)
+
+ path = string.join(pathdirs_1234, os.pathsep)
+ env = self.TestEnvironment(ENV = {'PATH' : path})
+ wi = env.WhereIs('xxx.exe')
+ assert wi == test.workpath(sub3_xxx_exe), wi
+ wi = env.WhereIs('xxx.exe', pathdirs_1243)
+ assert wi == test.workpath(sub4_xxx_exe), wi
+ wi = env.WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
+ assert wi == test.workpath(sub4_xxx_exe), wi
+
+ wi = env.WhereIs('xxx.exe', reject = sub3_xxx_exe)
+ assert wi == test.workpath(sub4_xxx_exe), wi
+ wi = env.WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
+ assert wi == test.workpath(sub4_xxx_exe), wi
+
+ path = string.join(pathdirs_1243, os.pathsep)
+ env = self.TestEnvironment(ENV = {'PATH' : path})
+ wi = env.WhereIs('xxx.exe')
+ assert wi == test.workpath(sub4_xxx_exe), wi
+ wi = env.WhereIs('xxx.exe', pathdirs_1234)
+ assert wi == test.workpath(sub3_xxx_exe), wi
+ wi = env.WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
+ assert wi == test.workpath(sub3_xxx_exe), wi
+
+ if sys.platform == 'win32':
+ wi = env.WhereIs('xxx', pathext = '')
+ assert wi is None, wi
+
+ wi = env.WhereIs('xxx', pathext = '.exe')
+ assert wi == test.workpath(sub4_xxx_exe), wi
+
+ wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
+ assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+
+ # Test that we return a normalized path even when
+ # the path contains forward slashes.
+ forward_slash = test.workpath('') + '/sub3'
+ wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE')
+ assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+
+
+
+ def test_Action(self):
+ """Test the Action() method"""
+ import SCons.Action
+
+ env = self.TestEnvironment(FOO = 'xyzzy')
+
+ a = env.Action('foo')
+ assert a, a
+ assert a.__class__ is SCons.Action.CommandAction, a.__class__
+
+ a = env.Action('$FOO')
+ assert a, a
+ assert a.__class__ is SCons.Action.CommandAction, a.__class__
+
+ a = env.Action('$$FOO')
+ assert a, a
+ assert a.__class__ is SCons.Action.LazyAction, a.__class__
+
+ a = env.Action(['$FOO', 'foo'])
+ assert a, a
+ assert a.__class__ is SCons.Action.ListAction, a.__class__
+
+ def func(arg):
+ pass
+ a = env.Action(func)
+ assert a, a
+ assert a.__class__ is SCons.Action.FunctionAction, a.__class__
+
+ def test_AddPostAction(self):
+ """Test the AddPostAction() method"""
+ env = self.TestEnvironment(FOO='fff', BAR='bbb')
+
+ n = env.AddPostAction('$FOO', lambda x: x)
+ assert str(n[0]) == 'fff', n[0]
+
+ n = env.AddPostAction(['ggg', '$BAR'], lambda x: x)
+ assert str(n[0]) == 'ggg', n[0]
+ assert str(n[1]) == 'bbb', n[1]
+
+ def test_AddPreAction(self):
+ """Test the AddPreAction() method"""
+ env = self.TestEnvironment(FOO='fff', BAR='bbb')
+
+ n = env.AddPreAction('$FOO', lambda x: x)
+ assert str(n[0]) == 'fff', n[0]
+
+ n = env.AddPreAction(['ggg', '$BAR'], lambda x: x)
+ assert str(n[0]) == 'ggg', n[0]
+ assert str(n[1]) == 'bbb', n[1]
+
+ def test_Alias(self):
+ """Test the Alias() method"""
+ env = self.TestEnvironment(FOO='kkk', BAR='lll', EA='export_alias')
+
+ tgt = env.Alias('new_alias')[0]
+ assert str(tgt) == 'new_alias', tgt
+ assert tgt.sources == [], tgt.sources
+ assert not hasattr(tgt, 'builder'), tgt.builder
+
+ tgt = env.Alias('None_alias', None)[0]
+ assert str(tgt) == 'None_alias', tgt
+ assert tgt.sources == [], tgt.sources
+
+ tgt = env.Alias('empty_list', [])[0]
+ assert str(tgt) == 'empty_list', tgt
+ assert tgt.sources == [], tgt.sources
+
+ tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
+ assert str(tgt) == 'export_alias', tgt
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'asrc1', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'kkk', map(str, tgt.sources)
+
+ n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0]
+ assert n is tgt, n
+ assert len(tgt.sources) == 4, map(str, tgt.sources)
+ assert str(tgt.sources[2]) == 'lll', map(str, tgt.sources)
+ assert str(tgt.sources[3]) == 'asrc4', map(str, tgt.sources)
+
+ n = env.Alias('$EA', 'asrc5')[0]
+ assert n is tgt, n
+ assert len(tgt.sources) == 5, map(str, tgt.sources)
+ assert str(tgt.sources[4]) == 'asrc5', map(str, tgt.sources)
+
+ t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
+ assert str(t1) == 't1', t1
+ assert str(t2) == 't2', t2
+ assert len(t1.sources) == 2, map(str, t1.sources)
+ assert str(t1.sources[0]) == 'asrc6', map(str, t1.sources)
+ assert str(t1.sources[1]) == 'asrc7', map(str, t1.sources)
+ assert len(t2.sources) == 2, map(str, t2.sources)
+ assert str(t2.sources[0]) == 'asrc6', map(str, t2.sources)
+ assert str(t2.sources[1]) == 'asrc7', map(str, t2.sources)
+
+ tgt = env.Alias('add', 's1')
+ tgt = env.Alias('add', 's2')[0]
+ s = map(str, tgt.sources)
+ assert s == ['s1', 's2'], s
+ tgt = env.Alias(tgt, 's3')[0]
+ s = map(str, tgt.sources)
+ assert s == ['s1', 's2', 's3'], s
+
+ tgt = env.Alias('act', None, "action1")[0]
+ s = str(tgt.builder.action)
+ assert s == "action1", s
+ tgt = env.Alias('act', None, "action2")[0]
+ s = str(tgt.builder.action)
+ assert s == "action1\naction2", s
+ tgt = env.Alias(tgt, None, "action3")[0]
+ s = str(tgt.builder.action)
+ assert s == "action1\naction2\naction3", s
+
+ def test_AlwaysBuild(self):
+ """Test the AlwaysBuild() method"""
+ env = self.TestEnvironment(FOO='fff', BAR='bbb')
+ t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
+ env.fs.Dir('dir'), env.fs.File('file'))
+ assert t[0].__class__.__name__ == 'Entry'
+ assert t[0].path == 'a'
+ assert t[0].always_build
+ assert t[1].__class__.__name__ == 'Entry'
+ assert t[1].path == 'bfff'
+ assert t[1].always_build
+ assert t[2].__class__.__name__ == 'Entry'
+ assert t[2].path == 'c'
+ assert t[2].always_build
+ assert t[3].__class__.__name__ == 'Entry'
+ assert t[3].path == 'd'
+ assert t[3].always_build
+ assert t[4].__class__.__name__ == 'Entry'
+ assert t[4].path == 'bbb'
+ assert t[4].always_build
+ assert t[5].__class__.__name__ == 'Dir'
+ assert t[5].path == 'dir'
+ assert t[5].always_build
+ assert t[6].__class__.__name__ == 'File'
+ assert t[6].path == 'file'
+ assert t[6].always_build
+
+ def test_VariantDir(self):
+ """Test the VariantDir() method"""
+ class MyFS:
+ def Dir(self, name):
+ return name
+ def VariantDir(self, variant_dir, src_dir, duplicate):
+ self.variant_dir = variant_dir
+ self.src_dir = src_dir
+ self.duplicate = duplicate
+
+ env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
+ env.fs = MyFS()
+
+ env.VariantDir('build', 'src')
+ assert env.fs.variant_dir == 'build', env.fs.variant_dir
+ assert env.fs.src_dir == 'src', env.fs.src_dir
+ assert env.fs.duplicate == 1, env.fs.duplicate
+
+ env.VariantDir('build${FOO}', '${BAR}src', 0)
+ assert env.fs.variant_dir == 'buildfff', env.fs.variant_dir
+ assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
+ assert env.fs.duplicate == 0, env.fs.duplicate
+
+ def test_Builder(self):
+ """Test the Builder() method"""
+ env = self.TestEnvironment(FOO = 'xyzzy')
+
+ b = env.Builder(action = 'foo')
+ assert b is not None, b
+
+ b = env.Builder(action = '$FOO')
+ assert b is not None, b
+
+ b = env.Builder(action = ['$FOO', 'foo'])
+ assert b is not None, b
+
+ def func(arg):
+ pass
+ b = env.Builder(action = func)
+ assert b is not None, b
+ b = env.Builder(generator = func)
+ assert b is not None, b
+
+ def test_CacheDir(self):
+ """Test the CacheDir() method"""
+ env = self.TestEnvironment(CD = 'CacheDir')
+
+ env.CacheDir('foo')
+ assert env._CacheDir_path == 'foo', env._CacheDir_path
+
+ env.CacheDir('$CD')
+ assert env._CacheDir_path == 'CacheDir', env._CacheDir_path
+
+ def test_Clean(self):
+ """Test the Clean() method"""
+ env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
+
+ CT = SCons.Environment.CleanTargets
+
+ foo = env.arg2nodes('foo')[0]
+ fff = env.arg2nodes('fff')[0]
+
+ t = env.Clean('foo', 'aaa')
+ l = map(str, CT[foo])
+ assert l == ['aaa'], l
+
+ t = env.Clean(foo, ['$BAR', 'ccc'])
+ l = map(str, CT[foo])
+ assert l == ['aaa', 'bbb', 'ccc'], l
+
+ eee = env.arg2nodes('eee')[0]
+
+ t = env.Clean('$FOO', 'ddd')
+ l = map(str, CT[fff])
+ assert l == ['ddd'], l
+ t = env.Clean(fff, [eee, 'fff'])
+ l = map(str, CT[fff])
+ assert l == ['ddd', 'eee', 'fff'], l
+
+ def test_Command(self):
+ """Test the Command() method."""
+ env = Environment()
+ t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
+ action='buildfoo $target $source')[0]
+ assert t.builder is not None
+ assert t.builder.action.__class__.__name__ == 'CommandAction'
+ assert t.builder.action.cmd_list == 'buildfoo $target $source'
+ assert 'foo1.in' in map(lambda x: x.path, t.sources)
+ assert 'foo2.in' in map(lambda x: x.path, t.sources)
+
+ sub = env.fs.Dir('sub')
+ t = env.Command(target='bar.out', source='sub',
+ action='buildbar $target $source')[0]
+ assert 'sub' in map(lambda x: x.path, t.sources)
+
+ def testFunc(env, target, source):
+ assert str(target[0]) == 'foo.out'
+ assert 'foo1.in' in map(str, source) and 'foo2.in' in map(str, source), map(str, source)
+ return 0
+ t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
+ action=testFunc)[0]
+ assert t.builder is not None
+ assert t.builder.action.__class__.__name__ == 'FunctionAction'
+ t.build()
+ assert 'foo1.in' in map(lambda x: x.path, t.sources)
+ assert 'foo2.in' in map(lambda x: x.path, t.sources)
+
+ x = []
+ def test2(baz, x=x):
+ x.append(baz)
+ env = self.TestEnvironment(TEST2 = test2)
+ t = env.Command(target='baz.out', source='baz.in',
+ action='${TEST2(XYZ)}',
+ XYZ='magic word')[0]
+ assert t.builder is not None
+ t.build()
+ assert x[0] == 'magic word', x
+
+ t = env.Command(target='${X}.out', source='${X}.in',
+ action = 'foo',
+ X = 'xxx')[0]
+ assert str(t) == 'xxx.out', str(t)
+ assert 'xxx.in' in map(lambda x: x.path, t.sources)
+
+ env = self.TestEnvironment(source_scanner = 'should_not_find_this')
+ t = env.Command(target='file.out', source='file.in',
+ action = 'foo',
+ source_scanner = 'fake')[0]
+ assert t.builder.source_scanner == 'fake', t.builder.source_scanner
+
+ def test_Configure(self):
+ """Test the Configure() method"""
+ # Configure() will write to a local temporary file.
+ test = TestCmd.TestCmd(workdir = '')
+ save = os.getcwd()
+
+ try:
+ os.chdir(test.workpath())
+
+ env = self.TestEnvironment(FOO = 'xyzzy')
+
+ def func(arg):
+ pass
+
+ c = env.Configure()
+ assert c is not None, c
+ c.Finish()
+
+ c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func})
+ assert c is not None, c
+ assert hasattr(c, 'foo')
+ assert hasattr(c, 'xyzzy')
+ c.Finish()
+ finally:
+ os.chdir(save)
+
+ def test_Depends(self):
+ """Test the explicit Depends method."""
+ env = self.TestEnvironment(FOO = 'xxx', BAR='yyy')
+ env.Dir('dir1')
+ env.Dir('dir2')
+ env.File('xxx.py')
+ env.File('yyy.py')
+ t = env.Depends(target='EnvironmentTest.py',
+ dependency='Environment.py')[0]
+ assert t.__class__.__name__ == 'Entry', t.__class__.__name__
+ assert t.path == 'EnvironmentTest.py'
+ assert len(t.depends) == 1
+ d = t.depends[0]
+ assert d.__class__.__name__ == 'Entry', d.__class__.__name__
+ assert d.path == 'Environment.py'
+
+ t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
+ assert t.__class__.__name__ == 'File', t.__class__.__name__
+ assert t.path == 'xxx.py'
+ assert len(t.depends) == 1
+ d = t.depends[0]
+ assert d.__class__.__name__ == 'File', d.__class__.__name__
+ assert d.path == 'yyy.py'
+
+ t = env.Depends(target='dir1', dependency='dir2')[0]
+ assert t.__class__.__name__ == 'Dir', t.__class__.__name__
+ assert t.path == 'dir1'
+ assert len(t.depends) == 1
+ d = t.depends[0]
+ assert d.__class__.__name__ == 'Dir', d.__class__.__name__
+ assert d.path == 'dir2'
+
+ def test_Dir(self):
+ """Test the Dir() method"""
+ class MyFS:
+ def Dir(self, name):
+ return 'Dir(%s)' % name
+
+ env = self.TestEnvironment(FOO = 'foodir', BAR = 'bardir')
+ env.fs = MyFS()
+
+ d = env.Dir('d')
+ assert d == 'Dir(d)', d
+
+ d = env.Dir('$FOO')
+ assert d == 'Dir(foodir)', d
+
+ d = env.Dir('${BAR}_$BAR')
+ assert d == 'Dir(bardir_bardir)', d
+
+ d = env.Dir(['dir1'])
+ assert d == ['Dir(dir1)'], d
+
+ d = env.Dir(['dir1', 'dir2'])
+ assert d == ['Dir(dir1)', 'Dir(dir2)'], d
+
+ def test_NoClean(self):
+ """Test the NoClean() method"""
+ env = self.TestEnvironment(FOO='ggg', BAR='hhh')
+ env.Dir('p_hhhb')
+ env.File('p_d')
+ t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
+
+ assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
+ assert t[0].path == 'p_a'
+ assert t[0].noclean
+ assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
+ assert t[1].path == 'p_hhhb'
+ assert t[1].noclean
+ assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
+ assert t[2].path == 'p_c'
+ assert t[2].noclean
+ assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
+ assert t[3].path == 'p_d'
+ assert t[3].noclean
+ assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
+ assert t[4].path == 'p_ggg'
+ assert t[4].noclean
+
+ def test_Dump(self):
+ """Test the Dump() method"""
+
+ env = self.TestEnvironment(FOO = 'foo')
+ assert env.Dump('FOO') == "'foo'", env.Dump('FOO')
+ assert len(env.Dump()) > 200, env.Dump() # no args version
+
+ def test_Environment(self):
+ """Test the Environment() method"""
+ env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy')
+
+ e2 = env.Environment(X = '$FOO', Y = '$BAR')
+ assert e2['X'] == 'xxx', e2['X']
+ assert e2['Y'] == 'yyy', e2['Y']
+
+ def test_Execute(self):
+ """Test the Execute() method"""
+
+ class MyAction:
+ def __init__(self, *args, **kw):
+ self.args = args
+ def __call__(self, target, source, env):
+ return "%s executed" % self.args
+
+ env = Environment()
+ env.Action = MyAction
+
+ result = env.Execute("foo")
+ assert result == "foo executed", result
+
+ def test_Entry(self):
+ """Test the Entry() method"""
+ class MyFS:
+ def Entry(self, name):
+ return 'Entry(%s)' % name
+
+ env = self.TestEnvironment(FOO = 'fooentry', BAR = 'barentry')
+ env.fs = MyFS()
+
+ e = env.Entry('e')
+ assert e == 'Entry(e)', e
+
+ e = env.Entry('$FOO')
+ assert e == 'Entry(fooentry)', e
+
+ e = env.Entry('${BAR}_$BAR')
+ assert e == 'Entry(barentry_barentry)', e
+
+ e = env.Entry(['entry1'])
+ assert e == ['Entry(entry1)'], e
+
+ e = env.Entry(['entry1', 'entry2'])
+ assert e == ['Entry(entry1)', 'Entry(entry2)'], e
+
+ def test_File(self):
+ """Test the File() method"""
+ class MyFS:
+ def File(self, name):
+ return 'File(%s)' % name
+
+ env = self.TestEnvironment(FOO = 'foofile', BAR = 'barfile')
+ env.fs = MyFS()
+
+ f = env.File('f')
+ assert f == 'File(f)', f
+
+ f = env.File('$FOO')
+ assert f == 'File(foofile)', f
+
+ f = env.File('${BAR}_$BAR')
+ assert f == 'File(barfile_barfile)', f
+
+ f = env.File(['file1'])
+ assert f == ['File(file1)'], f
+
+ f = env.File(['file1', 'file2'])
+ assert f == ['File(file1)', 'File(file2)'], f
+
+ def test_FindFile(self):
+ """Test the FindFile() method"""
+ env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
+
+ r = env.FindFile('foo', ['no_such_directory'])
+ assert r is None, r
+
+ # XXX
+
+ def test_Flatten(self):
+ """Test the Flatten() method"""
+ env = Environment()
+ l = env.Flatten([1])
+ assert l == [1]
+ l = env.Flatten([1, [2, [3, [4]]]])
+ assert l == [1, 2, 3, 4], l
+
+ def test_GetBuildPath(self):
+ """Test the GetBuildPath() method."""
+ env = self.TestEnvironment(MAGIC = 'xyzzy')
+
+ p = env.GetBuildPath('foo')
+ assert p == 'foo', p
+
+ p = env.GetBuildPath('$MAGIC')
+ assert p == 'xyzzy', p
+
+ def test_Ignore(self):
+ """Test the explicit Ignore method."""
+ env = self.TestEnvironment(FOO='yyy', BAR='zzz')
+ env.Dir('dir1')
+ env.Dir('dir2')
+ env.File('yyyzzz')
+ env.File('zzzyyy')
+
+ t = env.Ignore(target='targ.py', dependency='dep.py')[0]
+ assert t.__class__.__name__ == 'Entry', t.__class__.__name__
+ assert t.path == 'targ.py'
+ assert len(t.ignore) == 1
+ i = t.ignore[0]
+ assert i.__class__.__name__ == 'Entry', i.__class__.__name__
+ assert i.path == 'dep.py'
+
+ t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
+ assert t.__class__.__name__ == 'File', t.__class__.__name__
+ assert t.path == 'yyyzzz'
+ assert len(t.ignore) == 1
+ i = t.ignore[0]
+ assert i.__class__.__name__ == 'File', i.__class__.__name__
+ assert i.path == 'zzzyyy'
+
+ t = env.Ignore(target='dir1', dependency='dir2')[0]
+ assert t.__class__.__name__ == 'Dir', t.__class__.__name__
+ assert t.path == 'dir1'
+ assert len(t.ignore) == 1
+ i = t.ignore[0]
+ assert i.__class__.__name__ == 'Dir', i.__class__.__name__
+ assert i.path == 'dir2'
+
+ def test_Literal(self):
+ """Test the Literal() method"""
+ env = self.TestEnvironment(FOO='fff', BAR='bbb')
+ list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0]
+ assert list == ['$FOO', 'bbb'], list
+ list = env.subst_list(['$FOO', env.Literal('$BAR')])[0]
+ assert list == ['fff', '$BAR'], list
+
+ def test_Local(self):
+ """Test the Local() method."""
+ env = self.TestEnvironment(FOO='lll')
+
+ l = env.Local(env.fs.File('fff'))
+ assert str(l[0]) == 'fff', l[0]
+
+ l = env.Local('ggg', '$FOO')
+ assert str(l[0]) == 'ggg', l[0]
+ assert str(l[1]) == 'lll', l[1]
+
+ def test_Precious(self):
+ """Test the Precious() method"""
+ env = self.TestEnvironment(FOO='ggg', BAR='hhh')
+ env.Dir('p_hhhb')
+ env.File('p_d')
+ t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
+
+ assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
+ assert t[0].path == 'p_a'
+ assert t[0].precious
+ assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
+ assert t[1].path == 'p_hhhb'
+ assert t[1].precious
+ assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
+ assert t[2].path == 'p_c'
+ assert t[2].precious
+ assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
+ assert t[3].path == 'p_d'
+ assert t[3].precious
+ assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
+ assert t[4].path == 'p_ggg'
+ assert t[4].precious
+
+ def test_Repository(self):
+ """Test the Repository() method."""
+ class MyFS:
+ def __init__(self):
+ self.list = []
+ def Repository(self, *dirs):
+ self.list.extend(list(dirs))
+ def Dir(self, name):
+ return name
+ env = self.TestEnvironment(FOO='rrr', BAR='sss')
+ env.fs = MyFS()
+ env.Repository('/tmp/foo')
+ env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
+ expect = ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo']
+ assert env.fs.list == expect, env.fs.list
+
+ def test_Scanner(self):
+ """Test the Scanner() method"""
+ def scan(node, env, target, arg):
+ pass
+
+ env = self.TestEnvironment(FOO = scan)
+
+ s = env.Scanner('foo')
+ assert s is not None, s
+
+ s = env.Scanner(function = 'foo')
+ assert s is not None, s
+
+ if 0:
+ s = env.Scanner('$FOO')
+ assert s is not None, s
+
+ s = env.Scanner(function = '$FOO')
+ assert s is not None, s
+
+ def test_SConsignFile(self):
+ """Test the SConsignFile() method"""
+ import SCons.SConsign
+
+ class MyFS:
+ SConstruct_dir = os.sep + 'dir'
+
+ env = self.TestEnvironment(FOO = 'SConsign',
+ BAR = os.path.join(os.sep, 'File'))
+ env.fs = MyFS()
+ env.Execute = lambda action: None
+
+ try:
+ fnames = []
+ dbms = []
+ def capture(name, dbm_module, fnames=fnames, dbms=dbms):
+ fnames.append(name)
+ dbms.append(dbm_module)
+
+ save_SConsign_File = SCons.SConsign.File
+ SCons.SConsign.File = capture
+
+ env.SConsignFile('foo')
+ assert fnames[-1] == os.path.join(os.sep, 'dir', 'foo'), fnames
+ assert dbms[-1] is None, dbms
+
+ env.SConsignFile('$FOO')
+ assert fnames[-1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames
+ assert dbms[-1] is None, dbms
+
+ env.SConsignFile('/$FOO')
+ assert fnames[-1] == os.sep + 'SConsign', fnames
+ assert dbms[-1] is None, dbms
+
+ env.SConsignFile(os.sep + '$FOO')
+ assert fnames[-1] == os.sep + 'SConsign', fnames
+ assert dbms[-1] is None, dbms
+
+ env.SConsignFile('$BAR', 'x')
+ assert fnames[-1] == os.path.join(os.sep, 'File'), fnames
+ assert dbms[-1] == 'x', dbms
+
+ env.SConsignFile('__$BAR', 7)
+ assert fnames[-1] == os.path.join(os.sep, 'dir', '__', 'File'), fnames
+ assert dbms[-1] == 7, dbms
+
+ env.SConsignFile()
+ assert fnames[-1] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
+ assert dbms[-1] is None, dbms
+
+ env.SConsignFile(None)
+ assert fnames[-1] is None, fnames
+ assert dbms[-1] is None, dbms
+ finally:
+ SCons.SConsign.File = save_SConsign_File
+
+ def test_SideEffect(self):
+ """Test the SideEffect() method"""
+ env = self.TestEnvironment(LIB='lll', FOO='fff', BAR='bbb')
+ env.File('mylll.pdb')
+ env.Dir('mymmm.pdb')
+
+ foo = env.Object('foo.obj', 'foo.cpp')[0]
+ bar = env.Object('bar.obj', 'bar.cpp')[0]
+ s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0]
+ assert s.__class__.__name__ == 'Entry', s.__class__.__name__
+ assert s.path == 'mylib.pdb'
+ assert s.side_effect
+ assert foo.side_effects == [s]
+ assert bar.side_effects == [s]
+
+ fff = env.Object('fff.obj', 'fff.cpp')[0]
+ bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
+ s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0]
+ assert s.__class__.__name__ == 'File', s.__class__.__name__
+ assert s.path == 'mylll.pdb'
+ assert s.side_effect
+ assert fff.side_effects == [s], fff.side_effects
+ assert bbb.side_effects == [s], bbb.side_effects
+
+ ggg = env.Object('ggg.obj', 'ggg.cpp')[0]
+ ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
+ s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0]
+ assert s.__class__.__name__ == 'Dir', s.__class__.__name__
+ assert s.path == 'mymmm.pdb'
+ assert s.side_effect
+ assert ggg.side_effects == [s], ggg.side_effects
+ assert ccc.side_effects == [s], ccc.side_effects
+
+ def test_SourceCode(self):
+ """Test the SourceCode() method."""
+ env = self.TestEnvironment(FOO='mmm', BAR='nnn')
+ e = env.SourceCode('foo', None)[0]
+ assert e.path == 'foo'
+ s = e.src_builder()
+ assert s is None, s
+
+ b = Builder()
+ e = env.SourceCode(e, b)[0]
+ assert e.path == 'foo'
+ s = e.src_builder()
+ assert s is b, s
+
+ e = env.SourceCode('$BAR$FOO', None)[0]
+ assert e.path == 'nnnmmm'
+ s = e.src_builder()
+ assert s is None, s
+
+ def test_SourceSignatures(type):
+ """Test the SourceSignatures() method"""
+ import SCons.Errors
+
+ env = type.TestEnvironment(M = 'MD5', T = 'timestamp')
+
+ exc_caught = None
+ try:
+ env.SourceSignatures('invalid_type')
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected UserError"
+
+ env.SourceSignatures('MD5')
+ assert env.src_sig_type == 'MD5', env.src_sig_type
+
+ env.SourceSignatures('$M')
+ assert env.src_sig_type == 'MD5', env.src_sig_type
+
+ env.SourceSignatures('timestamp')
+ assert env.src_sig_type == 'timestamp', env.src_sig_type
+
+ env.SourceSignatures('$T')
+ assert env.src_sig_type == 'timestamp', env.src_sig_type
+
+ try:
+ import SCons.Util
+ save_md5 = SCons.Util.md5
+ SCons.Util.md5 = None
+ try:
+ env.SourceSignatures('MD5')
+ except SCons.Errors.UserError:
+ pass
+ else:
+ self.fail('Did not catch expected UserError')
+ finally:
+ SCons.Util.md5 = save_md5
+
+ def test_Split(self):
+ """Test the Split() method"""
+ env = self.TestEnvironment(FOO='fff', BAR='bbb')
+ s = env.Split("foo bar")
+ assert s == ["foo", "bar"], s
+ s = env.Split("$FOO bar")
+ assert s == ["fff", "bar"], s
+ s = env.Split(["foo", "bar"])
+ assert s == ["foo", "bar"], s
+ s = env.Split(["foo", "${BAR}-bbb"])
+ assert s == ["foo", "bbb-bbb"], s
+ s = env.Split("foo")
+ assert s == ["foo"], s
+ s = env.Split("$FOO$BAR")
+ assert s == ["fffbbb"], s
+
+ def test_TargetSignatures(type):
+ """Test the TargetSignatures() method"""
+ import SCons.Errors
+
+ env = type.TestEnvironment(B = 'build', C = 'content')
+
+ exc_caught = None
+ try:
+ env.TargetSignatures('invalid_type')
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected UserError"
+ assert not hasattr(env, '_build_signature')
+
+ env.TargetSignatures('build')
+ assert env.tgt_sig_type == 'build', env.tgt_sig_type
+
+ env.TargetSignatures('$B')
+ assert env.tgt_sig_type == 'build', env.tgt_sig_type
+
+ env.TargetSignatures('content')
+ assert env.tgt_sig_type == 'content', env.tgt_sig_type
+
+ env.TargetSignatures('$C')
+ assert env.tgt_sig_type == 'content', env.tgt_sig_type
+
+ env.TargetSignatures('MD5')
+ assert env.tgt_sig_type == 'MD5', env.tgt_sig_type
+
+ env.TargetSignatures('timestamp')
+ assert env.tgt_sig_type == 'timestamp', env.tgt_sig_type
+
+ try:
+ import SCons.Util
+ save_md5 = SCons.Util.md5
+ SCons.Util.md5 = None
+ try:
+ env.TargetSignatures('MD5')
+ except SCons.Errors.UserError:
+ pass
+ else:
+ self.fail('Did not catch expected UserError')
+ try:
+ env.TargetSignatures('content')
+ except SCons.Errors.UserError:
+ pass
+ else:
+ self.fail('Did not catch expected UserError')
+ finally:
+ SCons.Util.md5 = save_md5
+
+ def test_Value(self):
+ """Test creating a Value() object
+ """
+ env = Environment()
+ v1 = env.Value('a')
+ assert v1.value == 'a', v1.value
+
+ value2 = 'a'
+ v2 = env.Value(value2)
+ assert v2.value == value2, v2.value
+ assert v2.value is value2, v2.value
+
+ assert not v1 is v2
+ assert v1.value == v2.value
+
+ v3 = env.Value('c', 'build-c')
+ assert v3.value == 'c', v3.value
+
+
+
+ def test_Environment_global_variable(type):
+ """Test setting Environment variable to an Environment.Base subclass"""
+ class MyEnv(SCons.Environment.Base):
+ def xxx(self, string):
+ return self.subst(string)
+
+ SCons.Environment.Environment = MyEnv
+
+ env = SCons.Environment.Environment(FOO = 'foo')
+
+ f = env.subst('$FOO')
+ assert f == 'foo', f
+
+ f = env.xxx('$FOO')
+ assert f == 'foo', f
+
+ def test_bad_keywords(self):
+ """Test trying to use reserved keywords in an Environment"""
+ added = []
+
+ env = self.TestEnvironment(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ INIT = 'init')
+ bad_msg = '%s is not reserved, but got omitted; see Environment.construction_var_name_ok'
+ added.append('INIT')
+ for x in self.reserved_variables:
+ assert not env.has_key(x), env[x]
+ for x in added:
+ assert env.has_key(x), bad_msg % x
+
+ env.Append(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ APPEND = 'append')
+ added.append('APPEND')
+ for x in self.reserved_variables:
+ assert not env.has_key(x), env[x]
+ for x in added:
+ assert env.has_key(x), bad_msg % x
+
+ env.AppendUnique(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ APPENDUNIQUE = 'appendunique')
+ added.append('APPENDUNIQUE')
+ for x in self.reserved_variables:
+ assert not env.has_key(x), env[x]
+ for x in added:
+ assert env.has_key(x), bad_msg % x
+
+ env.Prepend(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ PREPEND = 'prepend')
+ added.append('PREPEND')
+ for x in self.reserved_variables:
+ assert not env.has_key(x), env[x]
+ for x in added:
+ assert env.has_key(x), bad_msg % x
+
+ env.Prepend(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ PREPENDUNIQUE = 'prependunique')
+ added.append('PREPENDUNIQUE')
+ for x in self.reserved_variables:
+ assert not env.has_key(x), env[x]
+ for x in added:
+ assert env.has_key(x), bad_msg % x
+
+ env.Replace(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ REPLACE = 'replace')
+ added.append('REPLACE')
+ for x in self.reserved_variables:
+ assert not env.has_key(x), env[x]
+ for x in added:
+ assert env.has_key(x), bad_msg % x
+
+ copy = env.Clone(TARGETS = 'targets',
+ SOURCES = 'sources',
+ SOURCE = 'source',
+ TARGET = 'target',
+ CHANGED_SOURCES = 'changed_sources',
+ CHANGED_TARGETS = 'changed_targets',
+ UNCHANGED_SOURCES = 'unchanged_sources',
+ UNCHANGED_TARGETS = 'unchanged_targets',
+ COPY = 'copy')
+ for x in self.reserved_variables:
+ assert not copy.has_key(x), env[x]
+ for x in added + ['COPY']:
+ assert copy.has_key(x), bad_msg % x
+
+ over = env.Override({'TARGETS' : 'targets',
+ 'SOURCES' : 'sources',
+ 'SOURCE' : 'source',
+ 'TARGET' : 'target',
+ 'CHANGED_SOURCES' : 'changed_sources',
+ 'CHANGED_TARGETS' : 'changed_targets',
+ 'UNCHANGED_SOURCES' : 'unchanged_sources',
+ 'UNCHANGED_TARGETS' : 'unchanged_targets',
+ 'OVERRIDE' : 'override'})
+ for x in self.reserved_variables:
+ assert not over.has_key(x), over[x]
+ for x in added + ['OVERRIDE']:
+ assert over.has_key(x), bad_msg % x
+
+ def test_parse_flags(self):
+ '''Test the Base class parse_flags argument'''
+ # all we have to show is that it gets to MergeFlags internally
+ env = Environment(tools=[], parse_flags = '-X')
+ assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+
+ env = Environment(tools=[], CCFLAGS=None, parse_flags = '-Y')
+ assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
+
+ env = Environment(tools=[], CPPDEFINES = 'FOO', parse_flags = '-std=c99 -X -DBAR')
+ assert env['CFLAGS'] == ['-std=c99'], env['CFLAGS']
+ assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+ assert env['CPPDEFINES'] == ['FOO', 'BAR'], env['CPPDEFINES']
+
+ def test_clone_parse_flags(self):
+ '''Test the env.Clone() parse_flags argument'''
+ # all we have to show is that it gets to MergeFlags internally
+ env = Environment(tools = [])
+ env2 = env.Clone(parse_flags = '-X')
+ assert not env.has_key('CCFLAGS')
+ assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+
+ env = Environment(tools = [], CCFLAGS=None)
+ env2 = env.Clone(parse_flags = '-Y')
+ assert env['CCFLAGS'] is None, env['CCFLAGS']
+ assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
+
+ env = Environment(tools = [], CPPDEFINES = 'FOO')
+ env2 = env.Clone(parse_flags = '-std=c99 -X -DBAR')
+ assert not env.has_key('CFLAGS')
+ assert env2['CFLAGS'] == ['-std=c99'], env2['CFLAGS']
+ assert not env.has_key('CCFLAGS')
+ assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+ assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
+ assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
+
+
+
+class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
+
+ def setUp(self):
+ env = Environment()
+ env._dict = {'XXX' : 'x', 'YYY' : 'y'}
+ env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
+ self.envs = [ env, env2, env3 ]
+
+ def checkpath(self, node, expect):
+ return str(node) == os.path.normpath(expect)
+
+ def test___init__(self):
+ """Test OverrideEnvironment initialization"""
+ env, env2, env3 = self.envs
+ assert env['XXX'] == 'x', env['XXX']
+ assert env2['XXX'] == 'x2', env2['XXX']
+ assert env3['XXX'] == 'x3', env3['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+ assert env2['YYY'] == 'y', env2['YYY']
+ assert env3['YYY'] == 'y3', env3['YYY']
+
+ def test___delitem__(self):
+ """Test deleting variables from an OverrideEnvironment"""
+ env, env2, env3 = self.envs
+
+ del env3['XXX']
+ assert not env.has_key('XXX'), "env has XXX?"
+ assert not env2.has_key('XXX'), "env2 has XXX?"
+ assert not env3.has_key('XXX'), "env3 has XXX?"
+
+ del env3['YYY']
+ assert not env.has_key('YYY'), "env has YYY?"
+ assert not env2.has_key('YYY'), "env2 has YYY?"
+ assert not env3.has_key('YYY'), "env3 has YYY?"
+
+ del env3['ZZZ']
+ assert not env.has_key('ZZZ'), "env has ZZZ?"
+ assert not env2.has_key('ZZZ'), "env2 has ZZZ?"
+ assert not env3.has_key('ZZZ'), "env3 has ZZZ?"
+
+ def test_get(self):
+ """Test the OverrideEnvironment get() method"""
+ env, env2, env3 = self.envs
+ assert env.get('XXX') == 'x', env.get('XXX')
+ assert env2.get('XXX') == 'x2', env2.get('XXX')
+ assert env3.get('XXX') == 'x3', env3.get('XXX')
+ assert env.get('YYY') == 'y', env.get('YYY')
+ assert env2.get('YYY') == 'y', env2.get('YYY')
+ assert env3.get('YYY') == 'y3', env3.get('YYY')
+ assert env.get('ZZZ') is None, env.get('ZZZ')
+ assert env2.get('ZZZ') is None, env2.get('ZZZ')
+ assert env3.get('ZZZ') == 'z3', env3.get('ZZZ')
+
+ def test_has_key(self):
+ """Test the OverrideEnvironment has_key() method"""
+ env, env2, env3 = self.envs
+ assert env.has_key('XXX'), env.has_key('XXX')
+ assert env2.has_key('XXX'), env2.has_key('XXX')
+ assert env3.has_key('XXX'), env3.has_key('XXX')
+ assert env.has_key('YYY'), env.has_key('YYY')
+ assert env2.has_key('YYY'), env2.has_key('YYY')
+ assert env3.has_key('YYY'), env3.has_key('YYY')
+ assert not env.has_key('ZZZ'), env.has_key('ZZZ')
+ assert not env2.has_key('ZZZ'), env2.has_key('ZZZ')
+ assert env3.has_key('ZZZ'), env3.has_key('ZZZ')
+
+ def test_contains(self):
+ """Test the OverrideEnvironment __contains__() method"""
+ try:
+ 'x' in {'x':1}
+ except TypeError:
+ # TODO(1.5)
+ # An early version of Python that doesn't support "in"
+ # on dictionaries. Just pass the test.
+ pass
+ else:
+ env, env2, env3 = self.envs
+ assert 'XXX' in env
+ assert 'XXX' in env2
+ assert 'XXX' in env3
+ assert 'YYY' in env
+ assert 'YYY' in env2
+ assert 'YYY' in env3
+ assert not 'ZZZ' in env
+ assert not 'ZZZ' in env2
+ assert 'ZZZ' in env3
+
+ def test_items(self):
+ """Test the OverrideEnvironment Dictionary() method"""
+ env, env2, env3 = self.envs
+ items = env.Dictionary()
+ assert items == {'XXX' : 'x', 'YYY' : 'y'}, items
+ items = env2.Dictionary()
+ assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items
+ items = env3.Dictionary()
+ assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
+
+ def test_items(self):
+ """Test the OverrideEnvironment items() method"""
+ env, env2, env3 = self.envs
+ items = env.items()
+ items.sort()
+ assert items == [('XXX', 'x'), ('YYY', 'y')], items
+ items = env2.items()
+ items.sort()
+ assert items == [('XXX', 'x2'), ('YYY', 'y')], items
+ items = env3.items()
+ items.sort()
+ assert items == [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items
+
+ def test_gvars(self):
+ """Test the OverrideEnvironment gvars() method"""
+ env, env2, env3 = self.envs
+ gvars = env.gvars()
+ assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+ gvars = env2.gvars()
+ assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+ gvars = env3.gvars()
+ assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+
+ def test_lvars(self):
+ """Test the OverrideEnvironment lvars() method"""
+ env, env2, env3 = self.envs
+ lvars = env.lvars()
+ assert lvars == {}, lvars
+ lvars = env2.lvars()
+ assert lvars == {'XXX' : 'x2'}, lvars
+ lvars = env3.lvars()
+ assert lvars == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars
+
+ def test_Replace(self):
+ """Test the OverrideEnvironment Replace() method"""
+ env, env2, env3 = self.envs
+ assert env['XXX'] == 'x', env['XXX']
+ assert env2['XXX'] == 'x2', env2['XXX']
+ assert env3['XXX'] == 'x3', env3['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+ assert env2['YYY'] == 'y', env2['YYY']
+ assert env3['YYY'] == 'y3', env3['YYY']
+
+ env.Replace(YYY = 'y4')
+
+ assert env['XXX'] == 'x', env['XXX']
+ assert env2['XXX'] == 'x2', env2['XXX']
+ assert env3['XXX'] == 'x3', env3['XXX']
+ assert env['YYY'] == 'y4', env['YYY']
+ assert env2['YYY'] == 'y4', env2['YYY']
+ assert env3['YYY'] == 'y3', env3['YYY']
+
+ # Tests a number of Base methods through an OverrideEnvironment to
+ # make sure they handle overridden constructionv variables properly.
+ #
+ # The following Base methods also call self.subst(), and so could
+ # theoretically be subject to problems with evaluating overridden
+ # variables, but they're never really called that way in the rest
+ # of our code, so we won't worry about them (at least for now):
+ #
+ # ParseConfig()
+ # ParseDepends()
+ # Platform()
+ # Tool()
+ #
+ # Action()
+ # Alias()
+ # Builder()
+ # CacheDir()
+ # Configure()
+ # Environment()
+ # FindFile()
+ # Scanner()
+ # SourceSignatures()
+ # TargetSignatures()
+
+ # It's unlikely Clone() will ever be called this way, so let the
+ # other methods test that handling overridden values works.
+ #def test_Clone(self):
+ # """Test the OverrideEnvironment Clone() method"""
+ # pass
+
+ def test_FindIxes(self):
+ """Test the OverrideEnvironment FindIxes() method"""
+ env, env2, env3 = self.envs
+ x = env.FindIxes(['xaaay'], 'XXX', 'YYY')
+ assert x == 'xaaay', x
+ x = env2.FindIxes(['x2aaay'], 'XXX', 'YYY')
+ assert x == 'x2aaay', x
+ x = env3.FindIxes(['x3aaay3'], 'XXX', 'YYY')
+ assert x == 'x3aaay3', x
+
+ def test_ReplaceIxes(self):
+ """Test the OverrideEnvironment ReplaceIxes() method"""
+ env, env2, env3 = self.envs
+ x = env.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX')
+ assert x == 'yaaax', x
+ x = env2.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX')
+ assert x == 'yaaax2', x
+ x = env3.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX')
+ assert x == 'y3aaax3', x
+
+ # It's unlikely WhereIs() will ever be called this way, so let the
+ # other methods test that handling overridden values works.
+ #def test_WhereIs(self):
+ # """Test the OverrideEnvironment WhereIs() method"""
+ # pass
+
+ def test_Dir(self):
+ """Test the OverrideEnvironment Dir() method"""
+ env, env2, env3 = self.envs
+ x = env.Dir('ddir/$XXX')
+ assert self.checkpath(x, 'ddir/x'), str(x)
+ x = env2.Dir('ddir/$XXX')
+ assert self.checkpath(x, 'ddir/x2'), str(x)
+ x = env3.Dir('ddir/$XXX')
+ assert self.checkpath(x, 'ddir/x3'), str(x)
+
+ def test_Entry(self):
+ """Test the OverrideEnvironment Entry() method"""
+ env, env2, env3 = self.envs
+ x = env.Entry('edir/$XXX')
+ assert self.checkpath(x, 'edir/x'), str(x)
+ x = env2.Entry('edir/$XXX')
+ assert self.checkpath(x, 'edir/x2'), str(x)
+ x = env3.Entry('edir/$XXX')
+ assert self.checkpath(x, 'edir/x3'), str(x)
+
+ def test_File(self):
+ """Test the OverrideEnvironment File() method"""
+ env, env2, env3 = self.envs
+ x = env.File('fdir/$XXX')
+ assert self.checkpath(x, 'fdir/x'), str(x)
+ x = env2.File('fdir/$XXX')
+ assert self.checkpath(x, 'fdir/x2'), str(x)
+ x = env3.File('fdir/$XXX')
+ assert self.checkpath(x, 'fdir/x3'), str(x)
+
+ def test_Split(self):
+ """Test the OverrideEnvironment Split() method"""
+ env, env2, env3 = self.envs
+ env['AAA'] = '$XXX $YYY $ZZZ'
+ x = env.Split('$AAA')
+ assert x == ['x', 'y'], x
+ x = env2.Split('$AAA')
+ assert x == ['x2', 'y'], x
+ x = env3.Split('$AAA')
+ assert x == ['x3', 'y3', 'z3'], x
+
+ def test_parse_flags(self):
+ '''Test the OverrideEnvironment parse_flags argument'''
+ # all we have to show is that it gets to MergeFlags internally
+ env = SubstitutionEnvironment()
+ env2 = env.Override({'parse_flags' : '-X'})
+ assert not env.has_key('CCFLAGS')
+ assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+
+ env = SubstitutionEnvironment(CCFLAGS=None)
+ env2 = env.Override({'parse_flags' : '-Y'})
+ assert env['CCFLAGS'] is None, env['CCFLAGS']
+ assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
+
+ env = SubstitutionEnvironment(CPPDEFINES = 'FOO')
+ env2 = env.Override({'parse_flags' : '-std=c99 -X -DBAR'})
+ assert not env.has_key('CFLAGS')
+ assert env2['CFLAGS'] == ['-std=c99'], env2['CFLAGS']
+ assert not env.has_key('CCFLAGS')
+ assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+ assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
+ assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
+
+
+
+class NoSubstitutionProxyTestCase(unittest.TestCase,TestEnvironmentFixture):
+
+ def test___init__(self):
+ """Test NoSubstitutionProxy initialization"""
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ assert env['XXX'] == 'x', env['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+
+ proxy = NoSubstitutionProxy(env)
+ assert proxy['XXX'] == 'x', proxy['XXX']
+ assert proxy['YYY'] == 'y', proxy['YYY']
+
+ def test_attributes(self):
+ """Test getting and setting NoSubstitutionProxy attributes"""
+ env = Environment()
+ setattr(env, 'env_attr', 'value1')
+
+ proxy = NoSubstitutionProxy(env)
+ setattr(proxy, 'proxy_attr', 'value2')
+
+ x = getattr(env, 'env_attr')
+ assert x == 'value1', x
+ x = getattr(proxy, 'env_attr')
+ assert x == 'value1', x
+
+ x = getattr(env, 'proxy_attr')
+ assert x == 'value2', x
+ x = getattr(proxy, 'proxy_attr')
+ assert x == 'value2', x
+
+ def test_subst(self):
+ """Test the NoSubstitutionProxy.subst() method"""
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ assert env['XXX'] == 'x', env['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+
+ proxy = NoSubstitutionProxy(env)
+ assert proxy['XXX'] == 'x', proxy['XXX']
+ assert proxy['YYY'] == 'y', proxy['YYY']
+
+ x = env.subst('$XXX')
+ assert x == 'x', x
+ x = proxy.subst('$XXX')
+ assert x == '$XXX', x
+
+ x = proxy.subst('$YYY', raw=7, target=None, source=None,
+ conv=None,
+ extra_meaningless_keyword_argument=None)
+ assert x == '$YYY', x
+
+ def test_subst_kw(self):
+ """Test the NoSubstitutionProxy.subst_kw() method"""
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ assert env['XXX'] == 'x', env['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+
+ proxy = NoSubstitutionProxy(env)
+ assert proxy['XXX'] == 'x', proxy['XXX']
+ assert proxy['YYY'] == 'y', proxy['YYY']
+
+ x = env.subst_kw({'$XXX':'$YYY'})
+ assert x == {'x':'y'}, x
+ x = proxy.subst_kw({'$XXX':'$YYY'})
+ assert x == {'$XXX':'$YYY'}, x
+
+ def test_subst_list(self):
+ """Test the NoSubstitutionProxy.subst_list() method"""
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ assert env['XXX'] == 'x', env['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+
+ proxy = NoSubstitutionProxy(env)
+ assert proxy['XXX'] == 'x', proxy['XXX']
+ assert proxy['YYY'] == 'y', proxy['YYY']
+
+ x = env.subst_list('$XXX')
+ assert x == [['x']], x
+ x = proxy.subst_list('$XXX')
+ assert x == [[]], x
+
+ x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None)
+ assert x == [[]], x
+
+ def test_subst_target_source(self):
+ """Test the NoSubstitutionProxy.subst_target_source() method"""
+ env = self.TestEnvironment(XXX = 'x', YYY = 'y')
+ assert env['XXX'] == 'x', env['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+
+ proxy = NoSubstitutionProxy(env)
+ assert proxy['XXX'] == 'x', proxy['XXX']
+ assert proxy['YYY'] == 'y', proxy['YYY']
+
+ args = ('$XXX $TARGET $SOURCE $YYY',)
+ kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
+ x = apply(env.subst_target_source, args, kw)
+ assert x == 'x ttt sss y', x
+ x = apply(proxy.subst_target_source, args, kw)
+ assert x == ' ttt sss ', x
+
+class EnvironmentVariableTestCase(unittest.TestCase):
+
+ def test_is_valid_construction_var(self):
+ """Testing is_valid_construction_var()"""
+ r = is_valid_construction_var("_a")
+ assert r is not None, r
+ r = is_valid_construction_var("z_")
+ assert r is not None, r
+ r = is_valid_construction_var("X_")
+ assert r is not None, r
+ r = is_valid_construction_var("2a")
+ assert r is None, r
+ r = is_valid_construction_var("a2_")
+ assert r is not None, r
+ r = is_valid_construction_var("/")
+ assert r is None, r
+ r = is_valid_construction_var("_/")
+ assert r is None, r
+ r = is_valid_construction_var("a/")
+ assert r is None, r
+ r = is_valid_construction_var(".b")
+ assert r is None, r
+ r = is_valid_construction_var("_.b")
+ assert r is None, r
+ r = is_valid_construction_var("b1._")
+ assert r is None, r
+ r = is_valid_construction_var("-b")
+ assert r is None, r
+ r = is_valid_construction_var("_-b")
+ assert r is None, r
+ r = is_valid_construction_var("b1-_")
+ assert r is None, r
+
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ SubstitutionTestCase,
+ BaseTestCase,
+ OverrideEnvironmentTestCase,
+ NoSubstitutionProxyTestCase,
+ EnvironmentVariableTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Errors.py b/src/engine/SCons/Errors.py
new file mode 100644
index 0000000..3044de9
--- /dev/null
+++ b/src/engine/SCons/Errors.py
@@ -0,0 +1,207 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""SCons.Errors
+
+This file contains the exception classes used to handle internal
+and user errors in SCons.
+
+"""
+
+__revision__ = "src/engine/SCons/Errors.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+import exceptions
+
+class BuildError(Exception):
+ """ Errors occuring while building.
+
+ BuildError have the following attributes:
+
+ Information about the cause of the build error:
+ -----------------------------------------------
+
+ errstr : a description of the error message
+
+ status : the return code of the action that caused the build
+ error. Must be set to a non-zero value even if the
+ build error is not due to an action returning a
+ non-zero returned code.
+
+ exitstatus : SCons exit status due to this build error.
+ Must be nonzero unless due to an explicit Exit()
+ call. Not always the same as status, since
+ actions return a status code that should be
+ respected, but SCons typically exits with 2
+ irrespective of the return value of the failed
+ action.
+
+ filename : The name of the file or directory that caused the
+ build error. Set to None if no files are associated with
+ this error. This might be different from the target
+ being built. For example, failure to create the
+ directory in which the target file will appear. It
+ can be None if the error is not due to a particular
+ filename.
+
+ exc_info : Info about exception that caused the build
+ error. Set to (None, None, None) if this build
+ error is not due to an exception.
+
+
+ Information about the cause of the location of the error:
+ ---------------------------------------------------------
+
+ node : the error occured while building this target node(s)
+
+ executor : the executor that caused the build to fail (might
+ be None if the build failures is not due to the
+ executor failing)
+
+ action : the action that caused the build to fail (might be
+ None if the build failures is not due to the an
+ action failure)
+
+ command : the command line for the action that caused the
+ build to fail (might be None if the build failures
+ is not due to the an action failure)
+ """
+
+ def __init__(self,
+ node=None, errstr="Unknown error", status=2, exitstatus=2,
+ filename=None, executor=None, action=None, command=None,
+ exc_info=(None, None, None)):
+
+ self.errstr = errstr
+ self.status = status
+ self.exitstatus = exitstatus
+ self.filename = filename
+ self.exc_info = exc_info
+
+ self.node = node
+ self.executor = executor
+ self.action = action
+ self.command = command
+
+ Exception.__init__(self, node, errstr, status, exitstatus, filename,
+ executor, action, command, exc_info)
+
+ def __str__(self):
+ if self.filename:
+ return self.filename + ': ' + self.errstr
+ else:
+ return self.errstr
+
+class InternalError(Exception):
+ pass
+
+class UserError(Exception):
+ pass
+
+class StopError(Exception):
+ pass
+
+class EnvironmentError(Exception):
+ pass
+
+class MSVCError(IOError):
+ pass
+
+class ExplicitExit(Exception):
+ def __init__(self, node=None, status=None, *args):
+ self.node = node
+ self.status = status
+ self.exitstatus = status
+ apply(Exception.__init__, (self,) + args)
+
+def convert_to_BuildError(status, exc_info=None):
+ """
+ Convert any return code a BuildError Exception.
+
+ `status' can either be a return code or an Exception.
+ The buildError.status we set here will normally be
+ used as the exit status of the "scons" process.
+ """
+ if not exc_info and isinstance(status, Exception):
+ exc_info = (status.__class__, status, None)
+
+ if isinstance(status, BuildError):
+ buildError = status
+ buildError.exitstatus = 2 # always exit with 2 on build errors
+ elif isinstance(status, ExplicitExit):
+ status = status.status
+ errstr = 'Explicit exit, status %s' % status
+ buildError = BuildError(
+ errstr=errstr,
+ status=status, # might be 0, OK here
+ exitstatus=status, # might be 0, OK here
+ exc_info=exc_info)
+ # TODO(1.5):
+ #elif isinstance(status, (StopError, UserError)):
+ elif isinstance(status, StopError) or isinstance(status, UserError):
+ buildError = BuildError(
+ errstr=str(status),
+ status=2,
+ exitstatus=2,
+ exc_info=exc_info)
+ elif isinstance(status, exceptions.EnvironmentError):
+ # If an IOError/OSError happens, raise a BuildError.
+ # Report the name of the file or directory that caused the
+ # error, which might be different from the target being built
+ # (for example, failure to create the directory in which the
+ # target file will appear).
+ try: filename = status.filename
+ except AttributeError: filename = None
+ buildError = BuildError(
+ errstr=status.strerror,
+ status=status.errno,
+ exitstatus=2,
+ filename=filename,
+ exc_info=exc_info)
+ elif isinstance(status, Exception):
+ buildError = BuildError(
+ errstr='%s : %s' % (status.__class__.__name__, status),
+ status=2,
+ exitstatus=2,
+ exc_info=exc_info)
+ elif SCons.Util.is_String(status):
+ buildError = BuildError(
+ errstr=status,
+ status=2,
+ exitstatus=2)
+ else:
+ buildError = BuildError(
+ errstr="Error %s" % status,
+ status=status,
+ exitstatus=2)
+
+ #import sys
+ #sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status))
+ return buildError
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py
new file mode 100644
index 0000000..cf5437b
--- /dev/null
+++ b/src/engine/SCons/ErrorsTests.py
@@ -0,0 +1,109 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/ErrorsTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+import SCons.Errors
+
+
+class ErrorsTestCase(unittest.TestCase):
+ def test_BuildError(self):
+ """Test the BuildError exception."""
+ try:
+ raise SCons.Errors.BuildError(
+ errstr = "foo", status=57, filename="file", exc_info=(1,2,3),
+ node = "n", executor="e", action="a", command="c")
+ except SCons.Errors.BuildError, e:
+ assert e.errstr == "foo"
+ assert e.status == 57
+ assert e.exitstatus == 2, e.exitstatus
+ assert e.filename == "file"
+ assert e.exc_info == (1,2,3)
+
+ assert e.node == "n"
+ assert e.executor == "e"
+ assert e.action == "a"
+ assert e.command == "c"
+
+ try:
+ raise SCons.Errors.BuildError("n", "foo", 57, 3, "file",
+ "e", "a", "c", (1,2,3))
+ except SCons.Errors.BuildError, e:
+ assert e.errstr == "foo", e.errstr
+ assert e.status == 57, e.status
+ assert e.exitstatus == 3, e.exitstatus
+ assert e.filename == "file", e.filename
+ assert e.exc_info == (1,2,3), e.exc_info
+
+ assert e.node == "n"
+ assert e.executor == "e"
+ assert e.action == "a"
+ assert e.command == "c"
+
+ try:
+ raise SCons.Errors.BuildError()
+ except SCons.Errors.BuildError, e:
+ assert e.errstr == "Unknown error"
+ assert e.status == 2
+ assert e.exitstatus == 2
+ assert e.filename is None
+ assert e.exc_info == (None, None, None)
+
+ assert e.node is None
+ assert e.executor is None
+ assert e.action is None
+ assert e.command is None
+
+ def test_InternalError(self):
+ """Test the InternalError exception."""
+ try:
+ raise SCons.Errors.InternalError, "test internal error"
+ except SCons.Errors.InternalError, e:
+ assert e.args == ("test internal error",)
+
+ def test_UserError(self):
+ """Test the UserError exception."""
+ try:
+ raise SCons.Errors.UserError, "test user error"
+ except SCons.Errors.UserError, e:
+ assert e.args == ("test user error",)
+
+ def test_ExplicitExit(self):
+ """Test the ExplicitExit exception."""
+ try:
+ raise SCons.Errors.ExplicitExit, "node"
+ except SCons.Errors.ExplicitExit, e:
+ assert e.node == "node"
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(ErrorsTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
new file mode 100644
index 0000000..2e873eb
--- /dev/null
+++ b/src/engine/SCons/Executor.py
@@ -0,0 +1,636 @@
+"""SCons.Executor
+
+A module for executing actions with specific lists of target and source
+Nodes.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Executor.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+import UserList
+
+from SCons.Debug import logInstanceCreation
+import SCons.Errors
+import SCons.Memoize
+
+
+class Batch:
+ """Remembers exact association between targets
+ and sources of executor."""
+ def __init__(self, targets=[], sources=[]):
+ self.targets = targets
+ self.sources = sources
+
+
+
+class TSList(UserList.UserList):
+ """A class that implements $TARGETS or $SOURCES expansions by wrapping
+ an executor Method. This class is used in the Executor.lvars()
+ to delay creation of NodeList objects until they're needed.
+
+ Note that we subclass UserList.UserList purely so that the
+ is_Sequence() function will identify an object of this class as
+ a list during variable expansion. We're not really using any
+ UserList.UserList methods in practice.
+ """
+ def __init__(self, func):
+ self.func = func
+ def __getattr__(self, attr):
+ nl = self.func()
+ return getattr(nl, attr)
+ def __getitem__(self, i):
+ nl = self.func()
+ return nl[i]
+ def __getslice__(self, i, j):
+ nl = self.func()
+ i = max(i, 0); j = max(j, 0)
+ return nl[i:j]
+ def __str__(self):
+ nl = self.func()
+ return str(nl)
+ def __repr__(self):
+ nl = self.func()
+ return repr(nl)
+
+class TSObject:
+ """A class that implements $TARGET or $SOURCE expansions by wrapping
+ an Executor method.
+ """
+ def __init__(self, func):
+ self.func = func
+ def __getattr__(self, attr):
+ n = self.func()
+ return getattr(n, attr)
+ def __str__(self):
+ n = self.func()
+ if n:
+ return str(n)
+ return ''
+ def __repr__(self):
+ n = self.func()
+ if n:
+ return repr(n)
+ return ''
+
+def rfile(node):
+ """
+ A function to return the results of a Node's rfile() method,
+ if it exists, and the Node itself otherwise (if it's a Value
+ Node, e.g.).
+ """
+ try:
+ rfile = node.rfile
+ except AttributeError:
+ return node
+ else:
+ return rfile()
+
+
+class Executor:
+ """A class for controlling instances of executing an action.
+
+ This largely exists to hold a single association of an action,
+ environment, list of environment override dictionaries, targets
+ and sources for later processing as needed.
+ """
+
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ def __init__(self, action, env=None, overridelist=[{}],
+ targets=[], sources=[], builder_kw={}):
+ if __debug__: logInstanceCreation(self, 'Executor.Executor')
+ self.set_action_list(action)
+ self.pre_actions = []
+ self.post_actions = []
+ self.env = env
+ self.overridelist = overridelist
+ if targets or sources:
+ self.batches = [Batch(targets[:], sources[:])]
+ else:
+ self.batches = []
+ self.builder_kw = builder_kw
+ self._memo = {}
+
+ def get_lvars(self):
+ try:
+ return self.lvars
+ except AttributeError:
+ self.lvars = {
+ 'CHANGED_SOURCES' : TSList(self._get_changed_sources),
+ 'CHANGED_TARGETS' : TSList(self._get_changed_targets),
+ 'SOURCE' : TSObject(self._get_source),
+ 'SOURCES' : TSList(self._get_sources),
+ 'TARGET' : TSObject(self._get_target),
+ 'TARGETS' : TSList(self._get_targets),
+ 'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources),
+ 'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets),
+ }
+ return self.lvars
+
+ def _get_changes(self):
+ cs = []
+ ct = []
+ us = []
+ ut = []
+ for b in self.batches:
+ if b.targets[0].is_up_to_date():
+ us.extend(map(rfile, b.sources))
+ ut.extend(b.targets)
+ else:
+ cs.extend(map(rfile, b.sources))
+ ct.extend(b.targets)
+ self._changed_sources_list = SCons.Util.NodeList(cs)
+ self._changed_targets_list = SCons.Util.NodeList(ct)
+ self._unchanged_sources_list = SCons.Util.NodeList(us)
+ self._unchanged_targets_list = SCons.Util.NodeList(ut)
+
+ def _get_changed_sources(self, *args, **kw):
+ try:
+ return self._changed_sources_list
+ except AttributeError:
+ self._get_changes()
+ return self._changed_sources_list
+
+ def _get_changed_targets(self, *args, **kw):
+ try:
+ return self._changed_targets_list
+ except AttributeError:
+ self._get_changes()
+ return self._changed_targets_list
+
+ def _get_source(self, *args, **kw):
+ #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst_proxy()])
+ return rfile(self.batches[0].sources[0]).get_subst_proxy()
+
+ def _get_sources(self, *args, **kw):
+ return SCons.Util.NodeList(map(lambda n: rfile(n).get_subst_proxy(), self.get_all_sources()))
+
+ def _get_target(self, *args, **kw):
+ #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy()])
+ return self.batches[0].targets[0].get_subst_proxy()
+
+ def _get_targets(self, *args, **kw):
+ return SCons.Util.NodeList(map(lambda n: n.get_subst_proxy(), self.get_all_targets()))
+
+ def _get_unchanged_sources(self, *args, **kw):
+ try:
+ return self._unchanged_sources_list
+ except AttributeError:
+ self._get_changes()
+ return self._unchanged_sources_list
+
+ def _get_unchanged_targets(self, *args, **kw):
+ try:
+ return self._unchanged_targets_list
+ except AttributeError:
+ self._get_changes()
+ return self._unchanged_targets_list
+
+ def get_action_targets(self):
+ if not self.action_list:
+ return []
+ targets_string = self.action_list[0].get_targets(self.env, self)
+ if targets_string[0] == '$':
+ targets_string = targets_string[1:]
+ return self.get_lvars()[targets_string]
+
+ def set_action_list(self, action):
+ import SCons.Util
+ if not SCons.Util.is_List(action):
+ if not action:
+ import SCons.Errors
+ raise SCons.Errors.UserError, "Executor must have an action."
+ action = [action]
+ self.action_list = action
+
+ def get_action_list(self):
+ return self.pre_actions + self.action_list + self.post_actions
+
+ def get_all_targets(self):
+ """Returns all targets for all batches of this Executor."""
+ result = []
+ for batch in self.batches:
+ # TODO(1.5): remove the list() cast
+ result.extend(list(batch.targets))
+ return result
+
+ def get_all_sources(self):
+ """Returns all sources for all batches of this Executor."""
+ result = []
+ for batch in self.batches:
+ # TODO(1.5): remove the list() cast
+ result.extend(list(batch.sources))
+ return result
+
+ def get_all_children(self):
+ """Returns all unique children (dependencies) for all batches
+ of this Executor.
+
+ The Taskmaster can recognize when it's already evaluated a
+ Node, so we don't have to make this list unique for its intended
+ canonical use case, but we expect there to be a lot of redundancy
+ (long lists of batched .cc files #including the same .h files
+ over and over), so removing the duplicates once up front should
+ save the Taskmaster a lot of work.
+ """
+ result = SCons.Util.UniqueList([])
+ for target in self.get_all_targets():
+ result.extend(target.children())
+ return result
+
+ def get_all_prerequisites(self):
+ """Returns all unique (order-only) prerequisites for all batches
+ of this Executor.
+ """
+ result = SCons.Util.UniqueList([])
+ for target in self.get_all_targets():
+ # TODO(1.5): remove the list() cast
+ result.extend(list(target.prerequisites))
+ return result
+
+ def get_action_side_effects(self):
+
+ """Returns all side effects for all batches of this
+ Executor used by the underlying Action.
+ """
+ result = SCons.Util.UniqueList([])
+ for target in self.get_action_targets():
+ result.extend(target.side_effects)
+ return result
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
+
+ def get_build_env(self):
+ """Fetch or create the appropriate build Environment
+ for this Executor.
+ """
+ try:
+ return self._memo['get_build_env']
+ except KeyError:
+ pass
+
+ # Create the build environment instance with appropriate
+ # overrides. These get evaluated against the current
+ # environment's construction variables so that users can
+ # add to existing values by referencing the variable in
+ # the expansion.
+ overrides = {}
+ for odict in self.overridelist:
+ overrides.update(odict)
+
+ import SCons.Defaults
+ env = self.env or SCons.Defaults.DefaultEnvironment()
+ build_env = env.Override(overrides)
+
+ self._memo['get_build_env'] = build_env
+
+ return build_env
+
+ def get_build_scanner_path(self, scanner):
+ """Fetch the scanner path for this executor's targets and sources.
+ """
+ env = self.get_build_env()
+ try:
+ cwd = self.batches[0].targets[0].cwd
+ except (IndexError, AttributeError):
+ cwd = None
+ return scanner.path(env, cwd,
+ self.get_all_targets(),
+ self.get_all_sources())
+
+ def get_kw(self, kw={}):
+ result = self.builder_kw.copy()
+ result.update(kw)
+ result['executor'] = self
+ return result
+
+ def do_nothing(self, target, kw):
+ return 0
+
+ def do_execute(self, target, kw):
+ """Actually execute the action list."""
+ env = self.get_build_env()
+ kw = self.get_kw(kw)
+ status = 0
+ for act in self.get_action_list():
+ #args = (self.get_all_targets(), self.get_all_sources(), env)
+ args = ([], [], env)
+ status = apply(act, args, kw)
+ if isinstance(status, SCons.Errors.BuildError):
+ status.executor = self
+ raise status
+ elif status:
+ msg = "Error %s" % status
+ raise SCons.Errors.BuildError(
+ errstr=msg,
+ node=self.batches[0].targets,
+ executor=self,
+ action=act)
+ return status
+
+ # use extra indirection because with new-style objects (Python 2.2
+ # and above) we can't override special methods, and nullify() needs
+ # to be able to do this.
+
+ def __call__(self, target, **kw):
+ return self.do_execute(target, kw)
+
+ def cleanup(self):
+ self._memo = {}
+
+ def add_sources(self, sources):
+ """Add source files to this Executor's list. This is necessary
+ for "multi" Builders that can be called repeatedly to build up
+ a source file list for a given target."""
+ # TODO(batch): extend to multiple batches
+ assert (len(self.batches) == 1)
+ # TODO(batch): remove duplicates?
+ sources = filter(lambda x, s=self.batches[0].sources: x not in s, sources)
+ self.batches[0].sources.extend(sources)
+
+ def get_sources(self):
+ return self.batches[0].sources
+
+ def add_batch(self, targets, sources):
+ """Add pair of associated target and source to this Executor's list.
+ This is necessary for "batch" Builders that can be called repeatedly
+ to build up a list of matching target and source files that will be
+ used in order to update multiple target files at once from multiple
+ corresponding source files, for tools like MSVC that support it."""
+ self.batches.append(Batch(targets, sources))
+
+ def prepare(self):
+ """
+ Preparatory checks for whether this Executor can go ahead
+ and (try to) build its targets.
+ """
+ for s in self.get_all_sources():
+ if s.missing():
+ msg = "Source `%s' not found, needed by target `%s'."
+ raise SCons.Errors.StopError, msg % (s, self.batches[0].targets[0])
+
+ def add_pre_action(self, action):
+ self.pre_actions.append(action)
+
+ def add_post_action(self, action):
+ self.post_actions.append(action)
+
+ # another extra indirection for new-style objects and nullify...
+
+ def my_str(self):
+ env = self.get_build_env()
+ get = lambda action, t=self.get_all_targets(), s=self.get_all_sources(), e=env: \
+ action.genstring(t, s, e)
+ return string.join(map(get, self.get_action_list()), "\n")
+
+
+ def __str__(self):
+ return self.my_str()
+
+ def nullify(self):
+ self.cleanup()
+ self.do_execute = self.do_nothing
+ self.my_str = lambda S=self: ''
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
+
+ def get_contents(self):
+ """Fetch the signature contents. This is the main reason this
+ class exists, so we can compute this once and cache it regardless
+ of how many target or source Nodes there are.
+ """
+ try:
+ return self._memo['get_contents']
+ except KeyError:
+ pass
+ env = self.get_build_env()
+ get = lambda action, t=self.get_all_targets(), s=self.get_all_sources(), e=env: \
+ action.get_contents(t, s, e)
+ result = string.join(map(get, self.get_action_list()), "")
+ self._memo['get_contents'] = result
+ return result
+
+ def get_timestamp(self):
+ """Fetch a time stamp for this Executor. We don't have one, of
+ course (only files do), but this is the interface used by the
+ timestamp module.
+ """
+ return 0
+
+ def scan_targets(self, scanner):
+ # TODO(batch): scan by batches
+ self.scan(scanner, self.get_all_targets())
+
+ def scan_sources(self, scanner):
+ # TODO(batch): scan by batches
+ if self.batches[0].sources:
+ self.scan(scanner, self.get_all_sources())
+
+ def scan(self, scanner, node_list):
+ """Scan a list of this Executor's files (targets or sources) for
+ implicit dependencies and update all of the targets with them.
+ This essentially short-circuits an N*M scan of the sources for
+ each individual target, which is a hell of a lot more efficient.
+ """
+ env = self.get_build_env()
+
+ # TODO(batch): scan by batches)
+ deps = []
+ if scanner:
+ for node in node_list:
+ node.disambiguate()
+ s = scanner.select(node)
+ if not s:
+ continue
+ path = self.get_build_scanner_path(s)
+ deps.extend(node.get_implicit_deps(env, s, path))
+ else:
+ kw = self.get_kw()
+ for node in node_list:
+ node.disambiguate()
+ scanner = node.get_env_scanner(env, kw)
+ if not scanner:
+ continue
+ scanner = scanner.select(node)
+ if not scanner:
+ continue
+ path = self.get_build_scanner_path(scanner)
+ deps.extend(node.get_implicit_deps(env, scanner, path))
+
+ deps.extend(self.get_implicit_deps())
+
+ for tgt in self.get_all_targets():
+ tgt.add_to_implicit(deps)
+
+ def _get_unignored_sources_key(self, node, ignore=()):
+ return (node,) + tuple(ignore)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
+
+ def get_unignored_sources(self, node, ignore=()):
+ key = (node,) + tuple(ignore)
+ try:
+ memo_dict = self._memo['get_unignored_sources']
+ except KeyError:
+ memo_dict = {}
+ self._memo['get_unignored_sources'] = memo_dict
+ else:
+ try:
+ return memo_dict[key]
+ except KeyError:
+ pass
+
+ if node:
+ # TODO: better way to do this (it's a linear search,
+ # but it may not be critical path)?
+ sourcelist = []
+ for b in self.batches:
+ if node in b.targets:
+ sourcelist = b.sources
+ break
+ else:
+ sourcelist = self.get_all_sources()
+ if ignore:
+ idict = {}
+ for i in ignore:
+ idict[i] = 1
+ sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist)
+
+ memo_dict[key] = sourcelist
+
+ return sourcelist
+
+ def get_implicit_deps(self):
+ """Return the executor's implicit dependencies, i.e. the nodes of
+ the commands to be executed."""
+ result = []
+ build_env = self.get_build_env()
+ for act in self.get_action_list():
+ deps = act.get_implicit_deps(self.get_all_targets(),
+ self.get_all_sources(),
+ build_env)
+ result.extend(deps)
+ return result
+
+
+
+_batch_executors = {}
+
+def GetBatchExecutor(key):
+ return _batch_executors[key]
+
+def AddBatchExecutor(key, executor):
+ assert not _batch_executors.has_key(key)
+ _batch_executors[key] = executor
+
+nullenv = None
+
+
+def get_NullEnvironment():
+ """Use singleton pattern for Null Environments."""
+ global nullenv
+
+ import SCons.Util
+ class NullEnvironment(SCons.Util.Null):
+ import SCons.CacheDir
+ _CacheDir_path = None
+ _CacheDir = SCons.CacheDir.CacheDir(None)
+ def get_CacheDir(self):
+ return self._CacheDir
+
+ if not nullenv:
+ nullenv = NullEnvironment()
+ return nullenv
+
+class Null:
+ """A null Executor, with a null build Environment, that does
+ nothing when the rest of the methods call it.
+
+ This might be able to disapper when we refactor things to
+ disassociate Builders from Nodes entirely, so we're not
+ going to worry about unit tests for this--at least for now.
+ """
+ def __init__(self, *args, **kw):
+ if __debug__: logInstanceCreation(self, 'Executor.Null')
+ self.batches = [Batch(kw['targets'][:], [])]
+ def get_build_env(self):
+ return get_NullEnvironment()
+ def get_build_scanner_path(self):
+ return None
+ def cleanup(self):
+ pass
+ def prepare(self):
+ pass
+ def get_unignored_sources(self, *args, **kw):
+ return tuple(())
+ def get_action_targets(self):
+ return []
+ def get_action_list(self):
+ return []
+ def get_all_targets(self):
+ return self.batches[0].targets
+ def get_all_sources(self):
+ return self.batches[0].targets[0].sources
+ def get_all_children(self):
+ return self.get_all_sources()
+ def get_all_prerequisites(self):
+ return []
+ def get_action_side_effects(self):
+ return []
+ def __call__(self, *args, **kw):
+ return 0
+ def get_contents(self):
+ return ''
+ def _morph(self):
+ """Morph this Null executor to a real Executor object."""
+ batches = self.batches
+ self.__class__ = Executor
+ self.__init__([])
+ self.batches = batches
+
+ # The following methods require morphing this Null Executor to a
+ # real Executor object.
+
+ def add_pre_action(self, action):
+ self._morph()
+ self.add_pre_action(action)
+ def add_post_action(self, action):
+ self._morph()
+ self.add_post_action(action)
+ def set_action_list(self, action):
+ self._morph()
+ self.set_action_list(action)
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py
new file mode 100644
index 0000000..a0194e9
--- /dev/null
+++ b/src/engine/SCons/ExecutorTests.py
@@ -0,0 +1,466 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/ExecutorTests.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+import sys
+import unittest
+
+import SCons.Executor
+
+
+class MyEnvironment:
+ def __init__(self, **kw):
+ self._dict = {}
+ self._dict.update(kw)
+ def __getitem__(self, key):
+ return self._dict[key]
+ def Override(self, overrides):
+ d = self._dict.copy()
+ d.update(overrides)
+ return apply(MyEnvironment, (), d)
+ def _update(self, dict):
+ self._dict.update(dict)
+
+class MyAction:
+ def __init__(self, actions=['action1', 'action2']):
+ self.actions = actions
+ def __call__(self, target, source, env, **kw):
+ for action in self.actions:
+ apply(action, (target, source, env), kw)
+ def genstring(self, target, source, env):
+ return string.join(['GENSTRING'] + map(str, self.actions) + target + source)
+ def get_contents(self, target, source, env):
+ return string.join(self.actions + target + source)
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class MyBuilder:
+ def __init__(self, env, overrides):
+ self.env = env
+ self.overrides = overrides
+ self.action = MyAction()
+
+class MyNode:
+ def __init__(self, name=None, pre=[], post=[]):
+ self.name = name
+ self.implicit = []
+ self.pre_actions = pre
+ self.post_actions = post
+ self.missing_val = None
+ def __str__(self):
+ return self.name
+ def build(self):
+ executor = SCons.Executor.Executor(MyAction(self.pre_actions +
+ [self.builder.action] +
+ self.post_actions),
+ self.builder.env,
+ [],
+ [self],
+ ['s1', 's2'])
+ apply(executor, (self), {})
+ def get_env_scanner(self, env, kw):
+ return MyScanner('dep-')
+ def get_implicit_deps(self, env, scanner, path):
+ return [scanner.prefix + str(self)]
+ def add_to_implicit(self, deps):
+ self.implicit.extend(deps)
+ def missing(self):
+ return self.missing_val
+ def calc_signature(self, calc):
+ return 'cs-'+calc+'-'+self.name
+ def disambiguate(self):
+ return self
+
+class MyScanner:
+ def __init__(self, prefix):
+ self.prefix = prefix
+ def path(self, env, cwd, target, source):
+ return ()
+ def select(self, node):
+ return self
+
+class ExecutorTestCase(unittest.TestCase):
+
+ def test__init__(self):
+ """Test creating an Executor"""
+ source_list = ['s1', 's2']
+ x = SCons.Executor.Executor('a', 'e', ['o'], 't', source_list)
+ assert x.action_list == ['a'], x.action_list
+ assert x.env == 'e', x.env
+ assert x.overridelist == ['o'], x.overridelist
+ targets = x.get_all_targets()
+ assert targets == ['t'], targets
+ source_list.append('s3')
+ sources = x.get_all_sources()
+ assert sources == ['s1', 's2'], sources
+ try:
+ x = SCons.Executor.Executor(None, 'e', ['o'], 't', source_list)
+ except SCons.Errors.UserError:
+ pass
+ else:
+ raise "Did not catch expected UserError"
+
+ def test__action_list(self):
+ """Test the {get,set}_action_list() methods"""
+ x = SCons.Executor.Executor('a', 'e', 'o', 't', ['s1', 's2'])
+
+ l = x.get_action_list()
+ assert l == ['a'], l
+
+ x.add_pre_action('pre')
+ x.add_post_action('post')
+ l = x.get_action_list()
+ assert l == ['pre', 'a', 'post'], l
+
+ x.set_action_list('b')
+ l = x.get_action_list()
+ assert l == ['pre', 'b', 'post'], l
+
+ x.set_action_list(['c'])
+ l = x.get_action_list()
+ assert l == ['pre', 'c', 'post'], l
+
+ def test_get_build_env(self):
+ """Test fetching and generating a build environment"""
+ x = SCons.Executor.Executor(MyAction(), MyEnvironment(e=1), [],
+ 't', ['s1', 's2'])
+ x.env = MyEnvironment(eee=1)
+ be = x.get_build_env()
+ assert be['eee'] == 1, be
+
+ env = MyEnvironment(X='xxx')
+ x = SCons.Executor.Executor(MyAction(),
+ env,
+ [{'O':'o2'}],
+ 't',
+ ['s1', 's2'])
+ be = x.get_build_env()
+ assert be['O'] == 'o2', be['O']
+ assert be['X'] == 'xxx', be['X']
+
+ env = MyEnvironment(Y='yyy')
+ overrides = [{'O':'ob3'}, {'O':'oo3'}]
+ x = SCons.Executor.Executor(MyAction(), env, overrides, ['t'], ['s'])
+ be = x.get_build_env()
+ assert be['O'] == 'oo3', be['O']
+ assert be['Y'] == 'yyy', be['Y']
+ overrides = [{'O':'ob3'}]
+ x = SCons.Executor.Executor(MyAction(), env, overrides, ['t'], ['s'])
+ be = x.get_build_env()
+ assert be['O'] == 'ob3', be['O']
+ assert be['Y'] == 'yyy', be['Y']
+
+ def test_get_build_scanner_path(self):
+ """Test fetching the path for the specified scanner."""
+ t = MyNode('t')
+ t.cwd = 'here'
+ x = SCons.Executor.Executor(MyAction(),
+ MyEnvironment(SCANNERVAL='sss'),
+ [],
+ [t],
+ ['s1', 's2'])
+
+ class LocalScanner:
+ def path(self, env, dir, target, source):
+ target = map(str, target)
+ source = map(str, source)
+ return "scanner: %s, %s, %s, %s" % (env['SCANNERVAL'], dir, target, source)
+ s = LocalScanner()
+
+ p = x.get_build_scanner_path(s)
+ assert p == "scanner: sss, here, ['t'], ['s1', 's2']", p
+
+ def test_get_kw(self):
+ """Test the get_kw() method"""
+ t = MyNode('t')
+ x = SCons.Executor.Executor(MyAction(),
+ MyEnvironment(),
+ [],
+ [t],
+ ['s1', 's2'],
+ builder_kw={'X':1, 'Y':2})
+ kw = x.get_kw()
+ assert kw == {'X':1, 'Y':2, 'executor':x}, kw
+ kw = x.get_kw({'Z':3})
+ assert kw == {'X':1, 'Y':2, 'Z':3, 'executor':x}, kw
+ kw = x.get_kw({'X':4})
+ assert kw == {'X':4, 'Y':2, 'executor':x}, kw
+
+ def test__call__(self):
+ """Test calling an Executor"""
+ result = []
+ def pre(target, source, env, result=result, **kw):
+ result.append('pre')
+ def action1(target, source, env, result=result, **kw):
+ result.append('action1')
+ def action2(target, source, env, result=result, **kw):
+ result.append('action2')
+ def post(target, source, env, result=result, **kw):
+ result.append('post')
+
+ env = MyEnvironment()
+ a = MyAction([action1, action2])
+ t = MyNode('t')
+
+ x = SCons.Executor.Executor(a, env, [], [t], ['s1', 's2'])
+ x.add_pre_action(pre)
+ x.add_post_action(post)
+ x(t)
+ assert result == ['pre', 'action1', 'action2', 'post'], result
+ del result[:]
+
+ def pre_err(target, source, env, result=result, **kw):
+ result.append('pre_err')
+ return 1
+
+ x = SCons.Executor.Executor(a, env, [], [t], ['s1', 's2'])
+ x.add_pre_action(pre_err)
+ x.add_post_action(post)
+ try:
+ x(t)
+ except SCons.Errors.BuildError:
+ pass
+ else:
+ raise Exception, "Did not catch expected BuildError"
+ assert result == ['pre_err'], result
+ del result[:]
+
+ def test_cleanup(self):
+ """Test cleaning up an Executor"""
+ orig_env = MyEnvironment(e=1)
+ x = SCons.Executor.Executor('b', orig_env, [{'o':1}],
+ 't', ['s1', 's2'])
+
+ be = x.get_build_env()
+ assert be['e'] == 1, be['e']
+
+ x.cleanup()
+
+ x.env = MyEnvironment(eee=1)
+ be = x.get_build_env()
+ assert be['eee'] == 1, be['eee']
+
+ x.cleanup()
+
+ be = x.get_build_env()
+ assert be['eee'] == 1, be['eee']
+
+ def test_add_sources(self):
+ """Test adding sources to an Executor"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ sources = x.get_all_sources()
+ assert sources == ['s1', 's2'], sources
+
+ x.add_sources(['s1', 's2'])
+ sources = x.get_all_sources()
+ assert sources == ['s1', 's2'], sources
+
+ x.add_sources(['s3', 's1', 's4'])
+ sources = x.get_all_sources()
+ assert sources == ['s1', 's2', 's3', 's4'], sources
+
+ def test_get_sources(self):
+ """Test getting sources from an Executor"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ sources = x.get_sources()
+ assert sources == ['s1', 's2'], sources
+
+ x.add_sources(['s1', 's2'])
+ sources = x.get_sources()
+ assert sources == ['s1', 's2'], sources
+
+ x.add_sources(['s3', 's1', 's4'])
+ sources = x.get_sources()
+ assert sources == ['s1', 's2', 's3', 's4'], sources
+
+ def test_prepare(self):
+ """Test the Executor's prepare() method"""
+ env = MyEnvironment()
+ t1 = MyNode('t1')
+ s1 = MyNode('s1')
+ s2 = MyNode('s2')
+ s3 = MyNode('s3')
+ x = SCons.Executor.Executor('b', env, [{}], [t1], [s1, s2, s3])
+
+ s2.missing_val = True
+
+ try:
+ r = x.prepare()
+ except SCons.Errors.StopError, e:
+ assert str(e) == "Source `s2' not found, needed by target `t1'.", e
+ else:
+ raise AssertionError, "did not catch expected StopError: %s" % r
+
+ def test_add_pre_action(self):
+ """Test adding pre-actions to an Executor"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ x.add_pre_action('a1')
+ assert x.pre_actions == ['a1']
+ x.add_pre_action('a2')
+ assert x.pre_actions == ['a1', 'a2']
+
+ def test_add_post_action(self):
+ """Test adding post-actions to an Executor"""
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ x.add_post_action('a1')
+ assert x.post_actions == ['a1']
+ x.add_post_action('a2')
+ assert x.post_actions == ['a1', 'a2']
+
+ def test___str__(self):
+ """Test the __str__() method"""
+ env = MyEnvironment(S='string')
+
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
+ c = str(x)
+ assert c == 'GENSTRING action1 action2 t s', c
+
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
+ x.add_pre_action(MyAction(['pre']))
+ x.add_post_action(MyAction(['post']))
+ c = str(x)
+ expect = 'GENSTRING pre t s\n' + \
+ 'GENSTRING action1 action2 t s\n' + \
+ 'GENSTRING post t s'
+ assert c == expect, c
+
+ def test_nullify(self):
+ """Test the nullify() method"""
+ env = MyEnvironment(S='string')
+
+ result = []
+ def action1(target, source, env, result=result, **kw):
+ result.append('action1')
+
+ env = MyEnvironment()
+ a = MyAction([action1])
+ x = SCons.Executor.Executor(a, env, [], ['t1', 't2'], ['s1', 's2'])
+
+ x(MyNode('', [], []))
+ assert result == ['action1'], result
+ s = str(x)
+ assert s[:10] == 'GENSTRING ', s
+
+ del result[:]
+ x.nullify()
+
+ assert result == [], result
+ x(MyNode('', [], []))
+ assert result == [], result
+ s = str(x)
+ assert s == '', s
+
+ def test_get_contents(self):
+ """Test fetching the signatures contents"""
+ env = MyEnvironment(C='contents')
+
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
+ c = x.get_contents()
+ assert c == 'action1 action2 t s', c
+
+ x = SCons.Executor.Executor(MyAction(actions=['grow']), env, [],
+ ['t'], ['s'])
+ x.add_pre_action(MyAction(['pre']))
+ x.add_post_action(MyAction(['post']))
+ c = x.get_contents()
+ assert c == 'pre t sgrow t spost t s', c
+
+ def test_get_timestamp(self):
+ """Test fetching the "timestamp" """
+ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
+ ts = x.get_timestamp()
+ assert ts == 0, ts
+
+ def test_scan_targets(self):
+ """Test scanning the targets for implicit dependencies"""
+ env = MyEnvironment(S='string')
+ t1 = MyNode('t1')
+ t2 = MyNode('t2')
+ sources = [MyNode('s1'), MyNode('s2')]
+ x = SCons.Executor.Executor(MyAction(), env, [{}], [t1, t2], sources)
+
+ deps = x.scan_targets(None)
+ assert t1.implicit == ['dep-t1', 'dep-t2'], t1.implicit
+ assert t2.implicit == ['dep-t1', 'dep-t2'], t2.implicit
+
+ t1.implicit = []
+ t2.implicit = []
+
+ deps = x.scan_targets(MyScanner('scanner-'))
+ assert t1.implicit == ['scanner-t1', 'scanner-t2'], t1.implicit
+ assert t2.implicit == ['scanner-t1', 'scanner-t2'], t2.implicit
+
+ def test_scan_sources(self):
+ """Test scanning the sources for implicit dependencies"""
+ env = MyEnvironment(S='string')
+ t1 = MyNode('t1')
+ t2 = MyNode('t2')
+ sources = [MyNode('s1'), MyNode('s2')]
+ x = SCons.Executor.Executor(MyAction(), env, [{}], [t1, t2], sources)
+
+ deps = x.scan_sources(None)
+ assert t1.implicit == ['dep-s1', 'dep-s2'], t1.implicit
+ assert t2.implicit == ['dep-s1', 'dep-s2'], t2.implicit
+
+ t1.implicit = []
+ t2.implicit = []
+
+ deps = x.scan_sources(MyScanner('scanner-'))
+ assert t1.implicit == ['scanner-s1', 'scanner-s2'], t1.implicit
+ assert t2.implicit == ['scanner-s1', 'scanner-s2'], t2.implicit
+
+ def test_get_unignored_sources(self):
+ """Test fetching the unignored source list"""
+ env = MyEnvironment()
+ s1 = MyNode('s1')
+ s2 = MyNode('s2')
+ s3 = MyNode('s3')
+ x = SCons.Executor.Executor('b', env, [{}], [], [s1, s2, s3])
+
+ r = x.get_unignored_sources(None, [])
+ assert r == [s1, s2, s3], map(str, r)
+
+ r = x.get_unignored_sources(None, [s2])
+ assert r == [s1, s3], map(str, r)
+
+ r = x.get_unignored_sources(None, [s1, s3])
+ assert r == [s2], map(str, r)
+
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ ExecutorTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Job.py b/src/engine/SCons/Job.py
new file mode 100644
index 0000000..6d652bf
--- /dev/null
+++ b/src/engine/SCons/Job.py
@@ -0,0 +1,435 @@
+"""SCons.Job
+
+This module defines the Serial and Parallel classes that execute tasks to
+complete a build. The Jobs class provides a higher level interface to start,
+stop, and wait on jobs.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Job.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import signal
+
+import SCons.Errors
+
+# The default stack size (in kilobytes) of the threads used to execute
+# jobs in parallel.
+#
+# We use a stack size of 256 kilobytes. The default on some platforms
+# is too large and prevents us from creating enough threads to fully
+# parallelized the build. For example, the default stack size on linux
+# is 8 MBytes.
+
+explicit_stack_size = None
+default_stack_size = 256
+
+interrupt_msg = 'Build interrupted.'
+
+
+class InterruptState:
+ def __init__(self):
+ self.interrupted = False
+
+ def set(self):
+ self.interrupted = True
+
+ def __call__(self):
+ return self.interrupted
+
+
+class Jobs:
+ """An instance of this class initializes N jobs, and provides
+ methods for starting, stopping, and waiting on all N jobs.
+ """
+
+ def __init__(self, num, taskmaster):
+ """
+ create 'num' jobs using the given taskmaster.
+
+ If 'num' is 1 or less, then a serial job will be used,
+ otherwise a parallel job with 'num' worker threads will
+ be used.
+
+ The 'num_jobs' attribute will be set to the actual number of jobs
+ allocated. If more than one job is requested but the Parallel
+ class can't do it, it gets reset to 1. Wrapping interfaces that
+ care should check the value of 'num_jobs' after initialization.
+ """
+
+ self.job = None
+ if num > 1:
+ stack_size = explicit_stack_size
+ if stack_size is None:
+ stack_size = default_stack_size
+
+ try:
+ self.job = Parallel(taskmaster, num, stack_size)
+ self.num_jobs = num
+ except NameError:
+ pass
+ if self.job is None:
+ self.job = Serial(taskmaster)
+ self.num_jobs = 1
+
+ def run(self, postfunc=lambda: None):
+ """Run the jobs.
+
+ postfunc() will be invoked after the jobs has run. It will be
+ invoked even if the jobs are interrupted by a keyboard
+ interrupt (well, in fact by a signal such as either SIGINT,
+ SIGTERM or SIGHUP). The execution of postfunc() is protected
+ against keyboard interrupts and is guaranteed to run to
+ completion."""
+ self._setup_sig_handler()
+ try:
+ self.job.start()
+ finally:
+ postfunc()
+ self._reset_sig_handler()
+
+ def were_interrupted(self):
+ """Returns whether the jobs were interrupted by a signal."""
+ return self.job.interrupted()
+
+ def _setup_sig_handler(self):
+ """Setup an interrupt handler so that SCons can shutdown cleanly in
+ various conditions:
+
+ a) SIGINT: Keyboard interrupt
+ b) SIGTERM: kill or system shutdown
+ c) SIGHUP: Controlling shell exiting
+
+ We handle all of these cases by stopping the taskmaster. It
+ turns out that it very difficult to stop the build process
+ by throwing asynchronously an exception such as
+ KeyboardInterrupt. For example, the python Condition
+ variables (threading.Condition) and Queue's do not seem to
+ asynchronous-exception-safe. It would require adding a whole
+ bunch of try/finally block and except KeyboardInterrupt all
+ over the place.
+
+ Note also that we have to be careful to handle the case when
+ SCons forks before executing another process. In that case, we
+ want the child to exit immediately.
+ """
+ def handler(signum, stack, self=self, parentpid=os.getpid()):
+ if os.getpid() == parentpid:
+ self.job.taskmaster.stop()
+ self.job.interrupted.set()
+ else:
+ os._exit(2)
+
+ self.old_sigint = signal.signal(signal.SIGINT, handler)
+ self.old_sigterm = signal.signal(signal.SIGTERM, handler)
+ try:
+ self.old_sighup = signal.signal(signal.SIGHUP, handler)
+ except AttributeError:
+ pass
+
+ def _reset_sig_handler(self):
+ """Restore the signal handlers to their previous state (before the
+ call to _setup_sig_handler()."""
+
+ signal.signal(signal.SIGINT, self.old_sigint)
+ signal.signal(signal.SIGTERM, self.old_sigterm)
+ try:
+ signal.signal(signal.SIGHUP, self.old_sighup)
+ except AttributeError:
+ pass
+
+class Serial:
+ """This class is used to execute tasks in series, and is more efficient
+ than Parallel, but is only appropriate for non-parallel builds. Only
+ one instance of this class should be in existence at a time.
+
+ This class is not thread safe.
+ """
+
+ def __init__(self, taskmaster):
+ """Create a new serial job given a taskmaster.
+
+ The taskmaster's next_task() method should return the next task
+ that needs to be executed, or None if there are no more tasks. The
+ taskmaster's executed() method will be called for each task when it
+ is successfully executed or failed() will be called if it failed to
+ execute (e.g. execute() raised an exception)."""
+
+ self.taskmaster = taskmaster
+ self.interrupted = InterruptState()
+
+ def start(self):
+ """Start the job. This will begin pulling tasks from the taskmaster
+ and executing them, and return when there are no more tasks. If a task
+ fails to execute (i.e. execute() raises an exception), then the job will
+ stop."""
+
+ while 1:
+ task = self.taskmaster.next_task()
+
+ if task is None:
+ break
+
+ try:
+ task.prepare()
+ if task.needs_execute():
+ task.execute()
+ except:
+ if self.interrupted():
+ try:
+ raise SCons.Errors.BuildError(
+ task.targets[0], errstr=interrupt_msg)
+ except:
+ task.exception_set()
+ else:
+ task.exception_set()
+
+ # Let the failed() callback function arrange for the
+ # build to stop if that's appropriate.
+ task.failed()
+ else:
+ task.executed()
+
+ task.postprocess()
+ self.taskmaster.cleanup()
+
+
+# Trap import failure so that everything in the Job module but the
+# Parallel class (and its dependent classes) will work if the interpreter
+# doesn't support threads.
+try:
+ import Queue
+ import threading
+except ImportError:
+ pass
+else:
+ class Worker(threading.Thread):
+ """A worker thread waits on a task to be posted to its request queue,
+ dequeues the task, executes it, and posts a tuple including the task
+ and a boolean indicating whether the task executed successfully. """
+
+ def __init__(self, requestQueue, resultsQueue, interrupted):
+ threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.requestQueue = requestQueue
+ self.resultsQueue = resultsQueue
+ self.interrupted = interrupted
+ self.start()
+
+ def run(self):
+ while 1:
+ task = self.requestQueue.get()
+
+ if task is None:
+ # The "None" value is used as a sentinel by
+ # ThreadPool.cleanup(). This indicates that there
+ # are no more tasks, so we should quit.
+ break
+
+ try:
+ if self.interrupted():
+ raise SCons.Errors.BuildError(
+ task.targets[0], errstr=interrupt_msg)
+ task.execute()
+ except:
+ task.exception_set()
+ ok = False
+ else:
+ ok = True
+
+ self.resultsQueue.put((task, ok))
+
+ class ThreadPool:
+ """This class is responsible for spawning and managing worker threads."""
+
+ def __init__(self, num, stack_size, interrupted):
+ """Create the request and reply queues, and 'num' worker threads.
+
+ One must specify the stack size of the worker threads. The
+ stack size is specified in kilobytes.
+ """
+ self.requestQueue = Queue.Queue(0)
+ self.resultsQueue = Queue.Queue(0)
+
+ try:
+ prev_size = threading.stack_size(stack_size*1024)
+ except AttributeError, e:
+ # Only print a warning if the stack size has been
+ # explicitly set.
+ if not explicit_stack_size is None:
+ msg = "Setting stack size is unsupported by this version of Python:\n " + \
+ e.args[0]
+ SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
+ except ValueError, e:
+ msg = "Setting stack size failed:\n " + str(e)
+ SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
+
+ # Create worker threads
+ self.workers = []
+ for _ in range(num):
+ worker = Worker(self.requestQueue, self.resultsQueue, interrupted)
+ self.workers.append(worker)
+
+ # Once we drop Python 1.5 we can change the following to:
+ #if 'prev_size' in locals():
+ if 'prev_size' in locals().keys():
+ threading.stack_size(prev_size)
+
+ def put(self, task):
+ """Put task into request queue."""
+ self.requestQueue.put(task)
+
+ def get(self):
+ """Remove and return a result tuple from the results queue."""
+ return self.resultsQueue.get()
+
+ def preparation_failed(self, task):
+ self.resultsQueue.put((task, False))
+
+ def cleanup(self):
+ """
+ Shuts down the thread pool, giving each worker thread a
+ chance to shut down gracefully.
+ """
+ # For each worker thread, put a sentinel "None" value
+ # on the requestQueue (indicating that there's no work
+ # to be done) so that each worker thread will get one and
+ # terminate gracefully.
+ for _ in self.workers:
+ self.requestQueue.put(None)
+
+ # Wait for all of the workers to terminate.
+ #
+ # If we don't do this, later Python versions (2.4, 2.5) often
+ # seem to raise exceptions during shutdown. This happens
+ # in requestQueue.get(), as an assertion failure that
+ # requestQueue.not_full is notified while not acquired,
+ # seemingly because the main thread has shut down (or is
+ # in the process of doing so) while the workers are still
+ # trying to pull sentinels off the requestQueue.
+ #
+ # Normally these terminations should happen fairly quickly,
+ # but we'll stick a one-second timeout on here just in case
+ # someone gets hung.
+ for worker in self.workers:
+ worker.join(1.0)
+ self.workers = []
+
+ class Parallel:
+ """This class is used to execute tasks in parallel, and is somewhat
+ less efficient than Serial, but is appropriate for parallel builds.
+
+ This class is thread safe.
+ """
+
+ def __init__(self, taskmaster, num, stack_size):
+ """Create a new parallel job given a taskmaster.
+
+ The taskmaster's next_task() method should return the next
+ task that needs to be executed, or None if there are no more
+ tasks. The taskmaster's executed() method will be called
+ for each task when it is successfully executed or failed()
+ will be called if the task failed to execute (i.e. execute()
+ raised an exception).
+
+ Note: calls to taskmaster are serialized, but calls to
+ execute() on distinct tasks are not serialized, because
+ that is the whole point of parallel jobs: they can execute
+ multiple tasks simultaneously. """
+
+ self.taskmaster = taskmaster
+ self.interrupted = InterruptState()
+ self.tp = ThreadPool(num, stack_size, self.interrupted)
+
+ self.maxjobs = num
+
+ def start(self):
+ """Start the job. This will begin pulling tasks from the
+ taskmaster and executing them, and return when there are no
+ more tasks. If a task fails to execute (i.e. execute() raises
+ an exception), then the job will stop."""
+
+ jobs = 0
+
+ while 1:
+ # Start up as many available tasks as we're
+ # allowed to.
+ while jobs < self.maxjobs:
+ task = self.taskmaster.next_task()
+ if task is None:
+ break
+
+ try:
+ # prepare task for execution
+ task.prepare()
+ except:
+ task.exception_set()
+ task.failed()
+ task.postprocess()
+ else:
+ if task.needs_execute():
+ # dispatch task
+ self.tp.put(task)
+ jobs = jobs + 1
+ else:
+ task.executed()
+ task.postprocess()
+
+ if not task and not jobs: break
+
+ # Let any/all completed tasks finish up before we go
+ # back and put the next batch of tasks on the queue.
+ while 1:
+ task, ok = self.tp.get()
+ jobs = jobs - 1
+
+ if ok:
+ task.executed()
+ else:
+ if self.interrupted():
+ try:
+ raise SCons.Errors.BuildError(
+ task.targets[0], errstr=interrupt_msg)
+ except:
+ task.exception_set()
+
+ # Let the failed() callback function arrange
+ # for the build to stop if that's appropriate.
+ task.failed()
+
+ task.postprocess()
+
+ if self.tp.resultsQueue.empty():
+ break
+
+ self.tp.cleanup()
+ self.taskmaster.cleanup()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py
new file mode 100644
index 0000000..00e83bd
--- /dev/null
+++ b/src/engine/SCons/JobTests.py
@@ -0,0 +1,539 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/JobTests.py 4577 2009/12/27 19:44:43 scons"
+
+import unittest
+import random
+import math
+import SCons.Job
+import sys
+import time
+
+# a large number
+num_sines = 10000
+
+# how many parallel jobs to perform for the test
+num_jobs = 11
+
+# how many tasks to perform for the test
+num_tasks = num_jobs*5
+
+class DummyLock:
+ "fake lock class to use if threads are not supported"
+ def acquire(self):
+ pass
+
+ def release(self):
+ pass
+
+class NoThreadsException:
+ "raised by the ParallelTestCase if threads are not supported"
+
+ def __str__(self):
+ return "the interpreter doesn't support threads"
+
+class Task:
+ """A dummy task class for testing purposes."""
+
+ def __init__(self, i, taskmaster):
+ self.i = i
+ self.taskmaster = taskmaster
+ self.was_executed = 0
+ self.was_prepared = 0
+
+ def prepare(self):
+ self.was_prepared = 1
+
+ def _do_something(self):
+ pass
+
+ def needs_execute(self):
+ return True
+
+ def execute(self):
+ self.taskmaster.test_case.failUnless(self.was_prepared,
+ "the task wasn't prepared")
+
+ self.taskmaster.guard.acquire()
+ self.taskmaster.begin_list.append(self.i)
+ self.taskmaster.guard.release()
+
+ self._do_something()
+
+ self.was_executed = 1
+
+ self.taskmaster.guard.acquire()
+ self.taskmaster.end_list.append(self.i)
+ self.taskmaster.guard.release()
+
+ def executed(self):
+ self.taskmaster.num_executed = self.taskmaster.num_executed + 1
+
+ self.taskmaster.test_case.failUnless(self.was_prepared,
+ "the task wasn't prepared")
+ self.taskmaster.test_case.failUnless(self.was_executed,
+ "the task wasn't really executed")
+ self.taskmaster.test_case.failUnless(isinstance(self, Task),
+ "the task wasn't really a Task instance")
+
+ def failed(self):
+ self.taskmaster.num_failed = self.taskmaster.num_failed + 1
+ self.taskmaster.stop = 1
+ self.taskmaster.test_case.failUnless(self.was_prepared,
+ "the task wasn't prepared")
+
+ def postprocess(self):
+ self.taskmaster.num_postprocessed = self.taskmaster.num_postprocessed + 1
+
+class RandomTask(Task):
+ def _do_something(self):
+ # do something that will take some random amount of time:
+ for i in range(random.randrange(0, num_sines, 1)):
+ x = math.sin(i)
+ time.sleep(0.01)
+
+class ExceptionTask:
+ """A dummy task class for testing purposes."""
+
+ def __init__(self, i, taskmaster):
+ self.taskmaster = taskmaster
+ self.was_prepared = 0
+
+ def prepare(self):
+ self.was_prepared = 1
+
+ def needs_execute(self):
+ return True
+
+ def execute(self):
+ raise Exception
+
+ def executed(self):
+ self.taskmaster.num_executed = self.taskmaster.num_executed + 1
+
+ self.taskmaster.test_case.failUnless(self.was_prepared,
+ "the task wasn't prepared")
+ self.taskmaster.test_case.failUnless(self.was_executed,
+ "the task wasn't really executed")
+ self.taskmaster.test_case.failUnless(self.__class__ is Task,
+ "the task wasn't really a Task instance")
+
+ def failed(self):
+ self.taskmaster.num_failed = self.taskmaster.num_failed + 1
+ self.taskmaster.stop = 1
+ self.taskmaster.test_case.failUnless(self.was_prepared,
+ "the task wasn't prepared")
+
+ def postprocess(self):
+ self.taskmaster.num_postprocessed = self.taskmaster.num_postprocessed + 1
+
+ def exception_set(self):
+ self.taskmaster.exception_set()
+
+class Taskmaster:
+ """A dummy taskmaster class for testing the job classes."""
+
+ def __init__(self, n, test_case, Task):
+ """n is the number of dummy tasks to perform."""
+
+ self.test_case = test_case
+ self.stop = None
+ self.num_tasks = n
+ self.num_iterated = 0
+ self.num_executed = 0
+ self.num_failed = 0
+ self.num_postprocessed = 0
+ self.Task = Task
+ # 'guard' guards 'task_begin_list' and 'task_end_list'
+ try:
+ import threading
+ self.guard = threading.Lock()
+ except:
+ self.guard = DummyLock()
+
+ # keep track of the order tasks are begun in
+ self.begin_list = []
+
+ # keep track of the order tasks are completed in
+ self.end_list = []
+
+ def next_task(self):
+ if self.stop or self.all_tasks_are_iterated():
+ return None
+ else:
+ self.num_iterated = self.num_iterated + 1
+ return self.Task(self.num_iterated, self)
+
+ def all_tasks_are_executed(self):
+ return self.num_executed == self.num_tasks
+
+ def all_tasks_are_iterated(self):
+ return self.num_iterated == self.num_tasks
+
+ def all_tasks_are_postprocessed(self):
+ return self.num_postprocessed == self.num_tasks
+
+ def tasks_were_serial(self):
+ "analyze the task order to see if they were serial"
+ serial = 1 # assume the tasks were serial
+ for i in range(num_tasks):
+ serial = serial and (self.begin_list[i]
+ == self.end_list[i]
+ == (i + 1))
+ return serial
+
+ def exception_set(self):
+ pass
+
+ def cleanup(self):
+ pass
+
+SaveThreadPool = None
+ThreadPoolCallList = []
+
+class ParallelTestCase(unittest.TestCase):
+ def runTest(self):
+ "test parallel jobs"
+
+ try:
+ import threading
+ except:
+ raise NoThreadsException()
+
+ taskmaster = Taskmaster(num_tasks, self, RandomTask)
+ jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ jobs.run()
+
+ self.failUnless(not taskmaster.tasks_were_serial(),
+ "the tasks were not executed in parallel")
+ self.failUnless(taskmaster.all_tasks_are_executed(),
+ "all the tests were not executed")
+ self.failUnless(taskmaster.all_tasks_are_iterated(),
+ "all the tests were not iterated over")
+ self.failUnless(taskmaster.all_tasks_are_postprocessed(),
+ "all the tests were not postprocessed")
+ self.failIf(taskmaster.num_failed,
+ "some task(s) failed to execute")
+
+ # Verify that parallel jobs will pull all of the completed tasks
+ # out of the queue at once, instead of one by one. We do this by
+ # replacing the default ThreadPool class with one that records the
+ # order in which tasks are put() and get() to/from the pool, and
+ # which sleeps a little bit before call get() to let the initial
+ # tasks complete and get their notifications on the resultsQueue.
+
+ class SleepTask(Task):
+ def _do_something(self):
+ time.sleep(0.1)
+
+ global SaveThreadPool
+ SaveThreadPool = SCons.Job.ThreadPool
+
+ class WaitThreadPool(SaveThreadPool):
+ def put(self, task):
+ ThreadPoolCallList.append('put(%s)' % task.i)
+ return SaveThreadPool.put(self, task)
+ def get(self):
+ time.sleep(0.5)
+ result = SaveThreadPool.get(self)
+ ThreadPoolCallList.append('get(%s)' % result[0].i)
+ return result
+
+ SCons.Job.ThreadPool = WaitThreadPool
+
+ try:
+ taskmaster = Taskmaster(3, self, SleepTask)
+ jobs = SCons.Job.Jobs(2, taskmaster)
+ jobs.run()
+
+ # The key here is that we get(1) and get(2) from the
+ # resultsQueue before we put(3), but get(1) and get(2) can
+ # be in either order depending on how the first two parallel
+ # tasks get scheduled by the operating system.
+ expect = [
+ ['put(1)', 'put(2)', 'get(1)', 'get(2)', 'put(3)', 'get(3)'],
+ ['put(1)', 'put(2)', 'get(2)', 'get(1)', 'put(3)', 'get(3)'],
+ ]
+ assert ThreadPoolCallList in expect, ThreadPoolCallList
+
+ finally:
+ SCons.Job.ThreadPool = SaveThreadPool
+
+class SerialTestCase(unittest.TestCase):
+ def runTest(self):
+ "test a serial job"
+
+ taskmaster = Taskmaster(num_tasks, self, RandomTask)
+ jobs = SCons.Job.Jobs(1, taskmaster)
+ jobs.run()
+
+ self.failUnless(taskmaster.tasks_were_serial(),
+ "the tasks were not executed in series")
+ self.failUnless(taskmaster.all_tasks_are_executed(),
+ "all the tests were not executed")
+ self.failUnless(taskmaster.all_tasks_are_iterated(),
+ "all the tests were not iterated over")
+ self.failUnless(taskmaster.all_tasks_are_postprocessed(),
+ "all the tests were not postprocessed")
+ self.failIf(taskmaster.num_failed,
+ "some task(s) failed to execute")
+
+class NoParallelTestCase(unittest.TestCase):
+ def runTest(self):
+ "test handling lack of parallel support"
+ def NoParallel(tm, num, stack_size):
+ raise NameError
+ save_Parallel = SCons.Job.Parallel
+ SCons.Job.Parallel = NoParallel
+ try:
+ taskmaster = Taskmaster(num_tasks, self, RandomTask)
+ jobs = SCons.Job.Jobs(2, taskmaster)
+ self.failUnless(jobs.num_jobs == 1,
+ "unexpected number of jobs %d" % jobs.num_jobs)
+ jobs.run()
+ self.failUnless(taskmaster.tasks_were_serial(),
+ "the tasks were not executed in series")
+ self.failUnless(taskmaster.all_tasks_are_executed(),
+ "all the tests were not executed")
+ self.failUnless(taskmaster.all_tasks_are_iterated(),
+ "all the tests were not iterated over")
+ self.failUnless(taskmaster.all_tasks_are_postprocessed(),
+ "all the tests were not postprocessed")
+ self.failIf(taskmaster.num_failed,
+ "some task(s) failed to execute")
+ finally:
+ SCons.Job.Parallel = save_Parallel
+
+
+class SerialExceptionTestCase(unittest.TestCase):
+ def runTest(self):
+ "test a serial job with tasks that raise exceptions"
+
+ taskmaster = Taskmaster(num_tasks, self, ExceptionTask)
+ jobs = SCons.Job.Jobs(1, taskmaster)
+ jobs.run()
+
+ self.failIf(taskmaster.num_executed,
+ "a task was executed")
+ self.failUnless(taskmaster.num_iterated == 1,
+ "exactly one task should have been iterated")
+ self.failUnless(taskmaster.num_failed == 1,
+ "exactly one task should have failed")
+ self.failUnless(taskmaster.num_postprocessed == 1,
+ "exactly one task should have been postprocessed")
+
+class ParallelExceptionTestCase(unittest.TestCase):
+ def runTest(self):
+ "test parallel jobs with tasks that raise exceptions"
+
+ taskmaster = Taskmaster(num_tasks, self, ExceptionTask)
+ jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ jobs.run()
+
+ self.failIf(taskmaster.num_executed,
+ "a task was executed")
+ self.failUnless(taskmaster.num_iterated >= 1,
+ "one or more task should have been iterated")
+ self.failUnless(taskmaster.num_failed >= 1,
+ "one or more tasks should have failed")
+ self.failUnless(taskmaster.num_postprocessed >= 1,
+ "one or more tasks should have been postprocessed")
+
+#---------------------------------------------------------------------
+# Above tested Job object with contrived Task and Taskmaster objects.
+# Now test Job object with actual Task and Taskmaster objects.
+
+import SCons.Taskmaster
+import SCons.Node
+import time
+
+class DummyNodeInfo:
+ def update(self, obj):
+ pass
+
+class testnode (SCons.Node.Node):
+ def __init__(self):
+ SCons.Node.Node.__init__(self)
+ self.expect_to_be = SCons.Node.executed
+ self.ninfo = DummyNodeInfo()
+
+class goodnode (testnode):
+ def __init__(self):
+ SCons.Node.Node.__init__(self)
+ self.expect_to_be = SCons.Node.up_to_date
+ self.ninfo = DummyNodeInfo()
+
+class slowgoodnode (goodnode):
+ def prepare(self):
+ # Delay to allow scheduled Jobs to run while the dispatcher
+ # sleeps. Keep this short because it affects the time taken
+ # by this test.
+ time.sleep(0.15)
+ goodnode.prepare(self)
+
+class badnode (goodnode):
+ def __init__(self):
+ goodnode.__init__(self)
+ self.expect_to_be = SCons.Node.failed
+ def build(self, **kw):
+ raise Exception, 'badnode exception'
+
+class slowbadnode (badnode):
+ def build(self, **kw):
+ # Appears to take a while to build, allowing faster builds to
+ # overlap. Time duration is not especially important, but if
+ # it is faster than slowgoodnode then these could complete
+ # while the scheduler is sleeping.
+ time.sleep(0.05)
+ raise Exception, 'slowbadnode exception'
+
+class badpreparenode (badnode):
+ def prepare(self):
+ raise Exception, 'badpreparenode exception'
+
+class _SConsTaskTest(unittest.TestCase):
+
+ def _test_seq(self, num_jobs):
+ for node_seq in [
+ [goodnode],
+ [badnode],
+ [slowbadnode],
+ [slowgoodnode],
+ [badpreparenode],
+ [goodnode, badnode],
+ [slowgoodnode, badnode],
+ [goodnode, slowbadnode],
+ [goodnode, goodnode, goodnode, slowbadnode],
+ [goodnode, slowbadnode, badpreparenode, slowgoodnode],
+ [goodnode, slowbadnode, slowgoodnode, badnode]
+ ]:
+
+ self._do_test(num_jobs, node_seq)
+
+ def _do_test(self, num_jobs, node_seq):
+
+ testnodes = []
+ for tnum in range(num_tasks):
+ testnodes.append(node_seq[tnum % len(node_seq)]())
+
+ taskmaster = SCons.Taskmaster.Taskmaster(testnodes,
+ tasker=SCons.Taskmaster.AlwaysTask)
+
+ jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+
+ # Exceptions thrown by tasks are not actually propagated to
+ # this level, but are instead stored in the Taskmaster.
+
+ jobs.run()
+
+ # Now figure out if tests proceeded correctly. The first test
+ # that fails will shutdown the initiation of subsequent tests,
+ # but any tests currently queued for execution will still be
+ # processed, and any tests that completed before the failure
+ # would have resulted in new tests being queued for execution.
+
+ # Apply the following operational heuristics of Job.py:
+ # 0) An initial jobset of tasks will be queued before any
+ # good/bad results are obtained (from "execute" of task in
+ # thread).
+ # 1) A goodnode will complete immediately on its thread and
+ # allow another node to be queued for execution.
+ # 2) A badnode will complete immediately and suppress any
+ # subsequent execution queuing, but all currently queued
+ # tasks will still be processed.
+ # 3) A slowbadnode will fail later. It will block slots in
+ # the job queue. Nodes that complete immediately will
+ # allow other nodes to be queued in their place, and this
+ # will continue until either (#2) above or until all job
+ # slots are filled with slowbadnode entries.
+
+ # One approach to validating this test would be to try to
+ # determine exactly how many nodes executed, how many didn't,
+ # and the results of each, and then to assert failure on any
+ # mismatch (including the total number of built nodes).
+ # However, while this is possible to do for a single-processor
+ # system, it is nearly impossible to predict correctly for a
+ # multi-processor system and still test the characteristics of
+ # delayed execution nodes. Stated another way, multithreading
+ # is inherently non-deterministic unless you can completely
+ # characterize the entire system, and since that's not
+ # possible here, we shouldn't try.
+
+ # Therefore, this test will simply scan the set of nodes to
+ # see if the node was executed or not and if it was executed
+ # that it obtained the expected value for that node
+ # (i.e. verifying we don't get failure crossovers or
+ # mislabelling of results).
+
+ for N in testnodes:
+ state = N.get_state()
+ self.failUnless(state in [SCons.Node.no_state, N.expect_to_be],
+ "Node %s got unexpected result: %s" % (N, state))
+
+ self.failUnless(filter(lambda N: N.get_state(), testnodes),
+ "no nodes ran at all.")
+
+
+class SerialTaskTest(_SConsTaskTest):
+ def runTest(self):
+ "test serial jobs with actual Taskmaster and Task"
+ self._test_seq(1)
+
+
+class ParallelTaskTest(_SConsTaskTest):
+ def runTest(self):
+ "test parallel jobs with actual Taskmaster and Task"
+ self._test_seq(num_jobs)
+
+
+
+#---------------------------------------------------------------------
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(ParallelTestCase())
+ suite.addTest(SerialTestCase())
+ suite.addTest(NoParallelTestCase())
+ suite.addTest(SerialExceptionTestCase())
+ suite.addTest(ParallelExceptionTestCase())
+ suite.addTest(SerialTaskTest())
+ suite.addTest(ParallelTaskTest())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if (len(result.failures) == 0
+ and len(result.errors) == 1
+ and type(result.errors[0][0]) == SerialTestCase
+ and type(result.errors[0][1][0]) == NoThreadsException):
+ sys.exit(2)
+ elif not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Memoize.py b/src/engine/SCons/Memoize.py
new file mode 100644
index 0000000..fc79157
--- /dev/null
+++ b/src/engine/SCons/Memoize.py
@@ -0,0 +1,292 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Memoize.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Memoizer
+
+A metaclass implementation to count hits and misses of the computed
+values that various methods cache in memory.
+
+Use of this modules assumes that wrapped methods be coded to cache their
+values in a consistent way. Here is an example of wrapping a method
+that returns a computed value, with no input parameters:
+
+ memoizer_counters = [] # Memoization
+
+ memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization
+
+ def foo(self):
+
+ try: # Memoization
+ return self._memo['foo'] # Memoization
+ except KeyError: # Memoization
+ pass # Memoization
+
+ result = self.compute_foo_value()
+
+ self._memo['foo'] = result # Memoization
+
+ return result
+
+Here is an example of wrapping a method that will return different values
+based on one or more input arguments:
+
+ def _bar_key(self, argument): # Memoization
+ return argument # Memoization
+
+ memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization
+
+ def bar(self, argument):
+
+ memo_key = argument # Memoization
+ try: # Memoization
+ memo_dict = self._memo['bar'] # Memoization
+ except KeyError: # Memoization
+ memo_dict = {} # Memoization
+ self._memo['dict'] = memo_dict # Memoization
+ else: # Memoization
+ try: # Memoization
+ return memo_dict[memo_key] # Memoization
+ except KeyError: # Memoization
+ pass # Memoization
+
+ result = self.compute_bar_value(argument)
+
+ memo_dict[memo_key] = result # Memoization
+
+ return result
+
+At one point we avoided replicating this sort of logic in all the methods
+by putting it right into this module, but we've moved away from that at
+present (see the "Historical Note," below.).
+
+Deciding what to cache is tricky, because different configurations
+can have radically different performance tradeoffs, and because the
+tradeoffs involved are often so non-obvious. Consequently, deciding
+whether or not to cache a given method will likely be more of an art than
+a science, but should still be based on available data from this module.
+Here are some VERY GENERAL guidelines about deciding whether or not to
+cache return values from a method that's being called a lot:
+
+ -- The first question to ask is, "Can we change the calling code
+ so this method isn't called so often?" Sometimes this can be
+ done by changing the algorithm. Sometimes the *caller* should
+ be memoized, not the method you're looking at.
+
+ -- The memoized function should be timed with multiple configurations
+ to make sure it doesn't inadvertently slow down some other
+ configuration.
+
+ -- When memoizing values based on a dictionary key composed of
+ input arguments, you don't need to use all of the arguments
+ if some of them don't affect the return values.
+
+Historical Note: The initial Memoizer implementation actually handled
+the caching of values for the wrapped methods, based on a set of generic
+algorithms for computing hashable values based on the method's arguments.
+This collected caching logic nicely, but had two drawbacks:
+
+ Running arguments through a generic key-conversion mechanism is slower
+ (and less flexible) than just coding these things directly. Since the
+ methods that need memoized values are generally performance-critical,
+ slowing them down in order to collect the logic isn't the right
+ tradeoff.
+
+ Use of the memoizer really obscured what was being called, because
+ all the memoized methods were wrapped with re-used generic methods.
+ This made it more difficult, for example, to use the Python profiler
+ to figure out how to optimize the underlying methods.
+"""
+
+import new
+
+# A flag controlling whether or not we actually use memoization.
+use_memoizer = None
+
+CounterList = []
+
+class Counter:
+ """
+ Base class for counting memoization hits and misses.
+
+ We expect that the metaclass initialization will have filled in
+ the .name attribute that represents the name of the function
+ being counted.
+ """
+ def __init__(self, method_name):
+ """
+ """
+ self.method_name = method_name
+ self.hit = 0
+ self.miss = 0
+ CounterList.append(self)
+ def display(self):
+ fmt = " %7d hits %7d misses %s()"
+ print fmt % (self.hit, self.miss, self.name)
+ def __cmp__(self, other):
+ try:
+ return cmp(self.name, other.name)
+ except AttributeError:
+ return 0
+
+class CountValue(Counter):
+ """
+ A counter class for simple, atomic memoized values.
+
+ A CountValue object should be instantiated in a class for each of
+ the class's methods that memoizes its return value by simply storing
+ the return value in its _memo dictionary.
+
+ We expect that the metaclass initialization will fill in the
+ .underlying_method attribute with the method that we're wrapping.
+ We then call the underlying_method method after counting whether
+ its memoized value has already been set (a hit) or not (a miss).
+ """
+ def __call__(self, *args, **kw):
+ obj = args[0]
+ if obj._memo.has_key(self.method_name):
+ self.hit = self.hit + 1
+ else:
+ self.miss = self.miss + 1
+ return apply(self.underlying_method, args, kw)
+
+class CountDict(Counter):
+ """
+ A counter class for memoized values stored in a dictionary, with
+ keys based on the method's input arguments.
+
+ A CountDict object is instantiated in a class for each of the
+ class's methods that memoizes its return value in a dictionary,
+ indexed by some key that can be computed from one or more of
+ its input arguments.
+
+ We expect that the metaclass initialization will fill in the
+ .underlying_method attribute with the method that we're wrapping.
+ We then call the underlying_method method after counting whether the
+ computed key value is already present in the memoization dictionary
+ (a hit) or not (a miss).
+ """
+ def __init__(self, method_name, keymaker):
+ """
+ """
+ Counter.__init__(self, method_name)
+ self.keymaker = keymaker
+ def __call__(self, *args, **kw):
+ obj = args[0]
+ try:
+ memo_dict = obj._memo[self.method_name]
+ except KeyError:
+ self.miss = self.miss + 1
+ else:
+ key = apply(self.keymaker, args, kw)
+ if memo_dict.has_key(key):
+ self.hit = self.hit + 1
+ else:
+ self.miss = self.miss + 1
+ return apply(self.underlying_method, args, kw)
+
+class Memoizer:
+ """Object which performs caching of method calls for its 'primary'
+ instance."""
+
+ def __init__(self):
+ pass
+
+# Find out if we support metaclasses (Python 2.2 and later).
+
+class M:
+ def __init__(cls, name, bases, cls_dict):
+ cls.use_metaclass = 1
+ def fake_method(self):
+ pass
+ new.instancemethod(fake_method, None, cls)
+
+try:
+ class A:
+ __metaclass__ = M
+
+ use_metaclass = A.use_metaclass
+except AttributeError:
+ use_metaclass = None
+ reason = 'no metaclasses'
+except TypeError:
+ use_metaclass = None
+ reason = 'new.instancemethod() bug'
+else:
+ del A
+
+del M
+
+if not use_metaclass:
+
+ def Dump(title):
+ pass
+
+ try:
+ class Memoized_Metaclass(type):
+ # Just a place-holder so pre-metaclass Python versions don't
+ # have to have special code for the Memoized classes.
+ pass
+ except TypeError:
+ class Memoized_Metaclass:
+ # A place-holder so pre-metaclass Python versions don't
+ # have to have special code for the Memoized classes.
+ pass
+
+ def EnableMemoization():
+ import SCons.Warnings
+ msg = 'memoization is not supported in this version of Python (%s)'
+ raise SCons.Warnings.NoMetaclassSupportWarning, msg % reason
+
+else:
+
+ def Dump(title=None):
+ if title:
+ print title
+ CounterList.sort()
+ for counter in CounterList:
+ counter.display()
+
+ class Memoized_Metaclass(type):
+ def __init__(cls, name, bases, cls_dict):
+ super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict)
+
+ for counter in cls_dict.get('memoizer_counters', []):
+ method_name = counter.method_name
+
+ counter.name = cls.__name__ + '.' + method_name
+ counter.underlying_method = cls_dict[method_name]
+
+ replacement_method = new.instancemethod(counter, None, cls)
+ setattr(cls, method_name, replacement_method)
+
+ def EnableMemoization():
+ global use_memoizer
+ use_memoizer = 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/MemoizeTests.py b/src/engine/SCons/MemoizeTests.py
new file mode 100644
index 0000000..e9f55a6
--- /dev/null
+++ b/src/engine/SCons/MemoizeTests.py
@@ -0,0 +1,198 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/MemoizeTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Memoize
+
+
+
+class FakeObject:
+
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ def __init__(self):
+ self._memo = {}
+
+ def _dict_key(self, argument):
+ return argument
+
+ memoizer_counters.append(SCons.Memoize.CountDict('dict', _dict_key))
+
+ def dict(self, argument):
+
+ memo_key = argument
+ try:
+ memo_dict = self._memo['dict']
+ except KeyError:
+ memo_dict = {}
+ self._memo['dict'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+
+ result = self.compute_dict(argument)
+
+ memo_dict[memo_key] = result
+
+ return result
+
+ memoizer_counters.append(SCons.Memoize.CountValue('value'))
+
+ def value(self):
+
+ try:
+ return self._memo['value']
+ except KeyError:
+ pass
+
+ result = self.compute_value()
+
+ self._memo['value'] = result
+
+ return result
+
+ def get_memoizer_counter(self, name):
+ for mc in self.memoizer_counters:
+ if mc.method_name == name:
+ return mc
+ return None
+
+class Returner:
+ def __init__(self, result):
+ self.result = result
+ self.calls = 0
+ def __call__(self, *args, **kw):
+ self.calls = self.calls + 1
+ return self.result
+
+
+class CountDictTestCase(unittest.TestCase):
+
+ def test___call__(self):
+ """Calling a Memoized dict method
+ """
+ obj = FakeObject()
+
+ called = []
+
+ fd1 = Returner(1)
+ fd2 = Returner(2)
+
+ obj.compute_dict = fd1
+
+ r = obj.dict(11)
+ assert r == 1, r
+
+ obj.compute_dict = fd2
+
+ r = obj.dict(12)
+ assert r == 2, r
+
+ r = obj.dict(11)
+ assert r == 1, r
+
+ obj.compute_dict = fd1
+
+ r = obj.dict(11)
+ assert r == 1, r
+
+ r = obj.dict(12)
+ assert r == 2, r
+
+ assert fd1.calls == 1, fd1.calls
+ assert fd2.calls == 1, fd2.calls
+
+ c = obj.get_memoizer_counter('dict')
+
+ if SCons.Memoize.use_metaclass:
+ assert c.hit == 3, c.hit
+ assert c.miss == 2, c.miss
+ else:
+ assert c.hit == 0, c.hit
+ assert c.miss == 0, c.miss
+
+
+class CountValueTestCase(unittest.TestCase):
+
+ def test___call__(self):
+ """Calling a Memoized value method
+ """
+ obj = FakeObject()
+
+ called = []
+
+ fv1 = Returner(1)
+ fv2 = Returner(2)
+
+ obj.compute_value = fv1
+
+ r = obj.value()
+ assert r == 1, r
+ r = obj.value()
+ assert r == 1, r
+
+ obj.compute_value = fv2
+
+ r = obj.value()
+ assert r == 1, r
+ r = obj.value()
+ assert r == 1, r
+
+ assert fv1.calls == 1, fv1.calls
+ assert fv2.calls == 0, fv2.calls
+
+ c = obj.get_memoizer_counter('value')
+
+ if SCons.Memoize.use_metaclass:
+ assert c.hit == 3, c.hit
+ assert c.miss == 1, c.miss
+ else:
+ assert c.hit == 0, c.hit
+ assert c.miss == 0, c.miss
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ CountDictTestCase,
+ CountValueTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py
new file mode 100644
index 0000000..2aa5821
--- /dev/null
+++ b/src/engine/SCons/Node/Alias.py
@@ -0,0 +1,153 @@
+
+"""scons.Node.Alias
+
+Alias nodes.
+
+This creates a hash of global Aliases (dummy targets).
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/Alias.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+import UserDict
+
+import SCons.Errors
+import SCons.Node
+import SCons.Util
+
+class AliasNameSpace(UserDict.UserDict):
+ def Alias(self, name, **kw):
+ if isinstance(name, SCons.Node.Alias.Alias):
+ return name
+ try:
+ a = self[name]
+ except KeyError:
+ a = apply(SCons.Node.Alias.Alias, (name,), kw)
+ self[name] = a
+ return a
+
+ def lookup(self, name, **kw):
+ try:
+ return self[name]
+ except KeyError:
+ return None
+
+class AliasNodeInfo(SCons.Node.NodeInfoBase):
+ current_version_id = 1
+ field_list = ['csig']
+ def str_to_node(self, s):
+ return default_ans.Alias(s)
+
+class AliasBuildInfo(SCons.Node.BuildInfoBase):
+ current_version_id = 1
+
+class Alias(SCons.Node.Node):
+
+ NodeInfo = AliasNodeInfo
+ BuildInfo = AliasBuildInfo
+
+ def __init__(self, name):
+ SCons.Node.Node.__init__(self)
+ self.name = name
+
+ def str_for_display(self):
+ return '"' + self.__str__() + '"'
+
+ def __str__(self):
+ return self.name
+
+ def make_ready(self):
+ self.get_csig()
+
+ really_build = SCons.Node.Node.build
+ is_up_to_date = SCons.Node.Node.children_are_up_to_date
+
+ def is_under(self, dir):
+ # Make Alias nodes get built regardless of
+ # what directory scons was run from. Alias nodes
+ # are outside the filesystem:
+ return 1
+
+ def get_contents(self):
+ """The contents of an alias is the concatenation
+ of the content signatures of all its sources."""
+ childsigs = map(lambda n: n.get_csig(), self.children())
+ return string.join(childsigs, '')
+
+ def sconsign(self):
+ """An Alias is not recorded in .sconsign files"""
+ pass
+
+ #
+ #
+ #
+
+ def changed_since_last_build(self, target, prev_ni):
+ cur_csig = self.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+ def build(self):
+ """A "builder" for aliases."""
+ pass
+
+ def convert(self):
+ try: del self.builder
+ except AttributeError: pass
+ self.reset_executor()
+ self.build = self.really_build
+
+ def get_csig(self):
+ """
+ Generate a node's content signature, the digested signature
+ of its content.
+
+ node - the node
+ cache - alternate node to use for the signature cache
+ returns - the content signature
+ """
+ try:
+ return self.ninfo.csig
+ except AttributeError:
+ pass
+
+ contents = self.get_contents()
+ csig = SCons.Util.MD5signature(contents)
+ self.get_ninfo().csig = csig
+ return csig
+
+default_ans = AliasNameSpace()
+
+SCons.Node.arg2nodes_lookups.append(default_ans.lookup)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py
new file mode 100644
index 0000000..8828c19
--- /dev/null
+++ b/src/engine/SCons/Node/AliasTests.py
@@ -0,0 +1,130 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/AliasTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Node.Alias
+
+class AliasTestCase(unittest.TestCase):
+
+ def test_AliasNameSpace(self):
+ """Test creating an Alias name space
+ """
+ ans = SCons.Node.Alias.AliasNameSpace()
+ assert ans is not None, ans
+
+ def test_ANS_Alias(self):
+ """Test the Alias() factory
+ """
+ ans = SCons.Node.Alias.AliasNameSpace()
+
+ a1 = ans.Alias('a1')
+ assert a1.name == 'a1', a1.name
+
+ a2 = ans.Alias('a1')
+ assert a1 is a2, (a1, a2)
+
+ def test_get_contents(self):
+ """Test the get_contents() method
+ """
+ class DummyNode:
+ def __init__(self, contents):
+ self.contents = contents
+ def get_csig(self):
+ return self.contents
+ def get_contents(self):
+ return self.contents
+
+ ans = SCons.Node.Alias.AliasNameSpace()
+
+ ans.Alias('a1')
+ a = ans.lookup('a1')
+
+ a.sources = [ DummyNode('one'), DummyNode('two'), DummyNode('three') ]
+
+ c = a.get_contents()
+ assert c == 'onetwothree', c
+
+ def test_lookup(self):
+ """Test the lookup() method
+ """
+ ans = SCons.Node.Alias.AliasNameSpace()
+
+ ans.Alias('a1')
+ a = ans.lookup('a1')
+ assert a.name == 'a1', a.name
+
+ a1 = ans.lookup('a1')
+ assert a is a1, a1
+
+ a = ans.lookup('a2')
+ assert a is None, a
+
+ def test_Alias(self):
+ """Test creating an Alias() object
+ """
+ a1 = SCons.Node.Alias.Alias('a')
+ assert a1.name == 'a', a1.name
+
+ a2 = SCons.Node.Alias.Alias('a')
+ assert a2.name == 'a', a2.name
+
+ assert not a1 is a2
+ assert a1.name == a2.name
+
+class AliasNodeInfoTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test AliasNodeInfo initialization"""
+ ans = SCons.Node.Alias.AliasNameSpace()
+ aaa = ans.Alias('aaa')
+ ni = SCons.Node.Alias.AliasNodeInfo(aaa)
+
+class AliasBuildInfoTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test AliasBuildInfo initialization"""
+ ans = SCons.Node.Alias.AliasNameSpace()
+ aaa = ans.Alias('aaa')
+ bi = SCons.Node.Alias.AliasBuildInfo(aaa)
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ AliasTestCase,
+ AliasBuildInfoTestCase,
+ AliasNodeInfoTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
new file mode 100644
index 0000000..0e9f14f
--- /dev/null
+++ b/src/engine/SCons/Node/FS.py
@@ -0,0 +1,3220 @@
+"""scons.Node.FS
+
+File system nodes.
+
+These Nodes represent the canonical external objects that people think
+of when they think of building software: files and directories.
+
+This holds a "default_fs" variable that should be initialized with an FS
+that can be used by scripts or modules looking for the canonical default.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/FS.py 4577 2009/12/27 19:44:43 scons"
+
+from itertools import izip
+import cStringIO
+import fnmatch
+import os
+import os.path
+import re
+import shutil
+import stat
+import string
+import sys
+import time
+
+try:
+ import codecs
+except ImportError:
+ pass
+else:
+ # TODO(2.2): Remove when 2.3 becomes the minimal supported version.
+ try:
+ codecs.BOM_UTF8
+ except AttributeError:
+ codecs.BOM_UTF8 = '\xef\xbb\xbf'
+ try:
+ codecs.BOM_UTF16_LE
+ codecs.BOM_UTF16_BE
+ except AttributeError:
+ codecs.BOM_UTF16_LE = '\xff\xfe'
+ codecs.BOM_UTF16_BE = '\xfe\xff'
+
+ # Provide a wrapper function to handle decoding differences in
+ # different versions of Python. Normally, we'd try to do this in the
+ # compat layer (and maybe it still makes sense to move there?) but
+ # that doesn't provide a way to supply the string class used in
+ # pre-2.3 Python versions with a .decode() method that all strings
+ # naturally have. Plus, the 2.[01] encodings behave differently
+ # enough that we have to settle for a lowest-common-denominator
+ # wrapper approach.
+ #
+ # Note that the 2.[012] implementations below may be inefficient
+ # because they perform an explicit look up of the encoding for every
+ # decode, but they're old enough (and we want to stop supporting
+ # them soon enough) that it's not worth complicating the interface.
+ # Think of it as additional incentive for people to upgrade...
+ try:
+ ''.decode
+ except AttributeError:
+ # 2.0 through 2.2: strings have no .decode() method
+ try:
+ codecs.lookup('ascii').decode
+ except AttributeError:
+ # 2.0 and 2.1: encodings are a tuple of functions, and the
+ # decode() function returns a (result, length) tuple.
+ def my_decode(contents, encoding):
+ return codecs.lookup(encoding)[1](contents)[0]
+ else:
+ # 2.2: encodings are an object with methods, and the
+ # .decode() method returns just the decoded bytes.
+ def my_decode(contents, encoding):
+ return codecs.lookup(encoding).decode(contents)
+ else:
+ # 2.3 or later: use the .decode() string method
+ def my_decode(contents, encoding):
+ return contents.decode(encoding)
+
+import SCons.Action
+from SCons.Debug import logInstanceCreation
+import SCons.Errors
+import SCons.Memoize
+import SCons.Node
+import SCons.Node.Alias
+import SCons.Subst
+import SCons.Util
+import SCons.Warnings
+
+from SCons.Debug import Trace
+
+do_store_info = True
+
+
+class EntryProxyAttributeError(AttributeError):
+ """
+ An AttributeError subclass for recording and displaying the name
+ of the underlying Entry involved in an AttributeError exception.
+ """
+ def __init__(self, entry_proxy, attribute):
+ AttributeError.__init__(self)
+ self.entry_proxy = entry_proxy
+ self.attribute = attribute
+ def __str__(self):
+ entry = self.entry_proxy.get()
+ fmt = "%s instance %s has no attribute %s"
+ return fmt % (entry.__class__.__name__,
+ repr(entry.name),
+ repr(self.attribute))
+
+# The max_drift value: by default, use a cached signature value for
+# any file that's been untouched for more than two days.
+default_max_drift = 2*24*60*60
+
+#
+# We stringify these file system Nodes a lot. Turning a file system Node
+# into a string is non-trivial, because the final string representation
+# can depend on a lot of factors: whether it's a derived target or not,
+# whether it's linked to a repository or source directory, and whether
+# there's duplication going on. The normal technique for optimizing
+# calculations like this is to memoize (cache) the string value, so you
+# only have to do the calculation once.
+#
+# A number of the above factors, however, can be set after we've already
+# been asked to return a string for a Node, because a Repository() or
+# VariantDir() call or the like may not occur until later in SConscript
+# files. So this variable controls whether we bother trying to save
+# string values for Nodes. The wrapper interface can set this whenever
+# they're done mucking with Repository and VariantDir and the other stuff,
+# to let this module know it can start returning saved string values
+# for Nodes.
+#
+Save_Strings = None
+
+def save_strings(val):
+ global Save_Strings
+ Save_Strings = val
+
+#
+# Avoid unnecessary function calls by recording a Boolean value that
+# tells us whether or not os.path.splitdrive() actually does anything
+# on this system, and therefore whether we need to bother calling it
+# when looking up path names in various methods below.
+#
+
+do_splitdrive = None
+
+def initialize_do_splitdrive():
+ global do_splitdrive
+ drive, path = os.path.splitdrive('X:/foo')
+ do_splitdrive = not not drive
+
+initialize_do_splitdrive()
+
+#
+
+needs_normpath_check = None
+
+def initialize_normpath_check():
+ """
+ Initialize the normpath_check regular expression.
+
+ This function is used by the unit tests to re-initialize the pattern
+ when testing for behavior with different values of os.sep.
+ """
+ global needs_normpath_check
+ if os.sep == '/':
+ pattern = r'.*/|\.$|\.\.$'
+ else:
+ pattern = r'.*[/%s]|\.$|\.\.$' % re.escape(os.sep)
+ needs_normpath_check = re.compile(pattern)
+
+initialize_normpath_check()
+
+#
+# SCons.Action objects for interacting with the outside world.
+#
+# The Node.FS methods in this module should use these actions to
+# create and/or remove files and directories; they should *not* use
+# os.{link,symlink,unlink,mkdir}(), etc., directly.
+#
+# Using these SCons.Action objects ensures that descriptions of these
+# external activities are properly displayed, that the displays are
+# suppressed when the -s (silent) option is used, and (most importantly)
+# the actions are disabled when the the -n option is used, in which case
+# there should be *no* changes to the external file system(s)...
+#
+
+if hasattr(os, 'link'):
+ def _hardlink_func(fs, src, dst):
+ # If the source is a symlink, we can't just hard-link to it
+ # because a relative symlink may point somewhere completely
+ # different. We must disambiguate the symlink and then
+ # hard-link the final destination file.
+ while fs.islink(src):
+ link = fs.readlink(src)
+ if not os.path.isabs(link):
+ src = link
+ else:
+ src = os.path.join(os.path.dirname(src), link)
+ fs.link(src, dst)
+else:
+ _hardlink_func = None
+
+if hasattr(os, 'symlink'):
+ def _softlink_func(fs, src, dst):
+ fs.symlink(src, dst)
+else:
+ _softlink_func = None
+
+def _copy_func(fs, src, dest):
+ shutil.copy2(src, dest)
+ st = fs.stat(src)
+ fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+
+
+Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy',
+ 'hard-copy', 'soft-copy', 'copy']
+
+Link_Funcs = [] # contains the callables of the specified duplication style
+
+def set_duplicate(duplicate):
+ # Fill in the Link_Funcs list according to the argument
+ # (discarding those not available on the platform).
+
+ # Set up the dictionary that maps the argument names to the
+ # underlying implementations. We do this inside this function,
+ # not in the top-level module code, so that we can remap os.link
+ # and os.symlink for testing purposes.
+ link_dict = {
+ 'hard' : _hardlink_func,
+ 'soft' : _softlink_func,
+ 'copy' : _copy_func
+ }
+
+ if not duplicate in Valid_Duplicates:
+ raise SCons.Errors.InternalError, ("The argument of set_duplicate "
+ "should be in Valid_Duplicates")
+ global Link_Funcs
+ Link_Funcs = []
+ for func in string.split(duplicate,'-'):
+ if link_dict[func]:
+ Link_Funcs.append(link_dict[func])
+
+def LinkFunc(target, source, env):
+ # Relative paths cause problems with symbolic links, so
+ # we use absolute paths, which may be a problem for people
+ # who want to move their soft-linked src-trees around. Those
+ # people should use the 'hard-copy' mode, softlinks cannot be
+ # used for that; at least I have no idea how ...
+ src = source[0].abspath
+ dest = target[0].abspath
+ dir, file = os.path.split(dest)
+ if dir and not target[0].fs.isdir(dir):
+ os.makedirs(dir)
+ if not Link_Funcs:
+ # Set a default order of link functions.
+ set_duplicate('hard-soft-copy')
+ fs = source[0].fs
+ # Now link the files with the previously specified order.
+ for func in Link_Funcs:
+ try:
+ func(fs, src, dest)
+ break
+ except (IOError, OSError):
+ # An OSError indicates something happened like a permissions
+ # problem or an attempt to symlink across file-system
+ # boundaries. An IOError indicates something like the file
+ # not existing. In either case, keeping trying additional
+ # functions in the list and only raise an error if the last
+ # one failed.
+ if func == Link_Funcs[-1]:
+ # exception of the last link method (copy) are fatal
+ raise
+ return 0
+
+Link = SCons.Action.Action(LinkFunc, None)
+def LocalString(target, source, env):
+ return 'Local copy of %s from %s' % (target[0], source[0])
+
+LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
+
+def UnlinkFunc(target, source, env):
+ t = target[0]
+ t.fs.unlink(t.abspath)
+ return 0
+
+Unlink = SCons.Action.Action(UnlinkFunc, None)
+
+def MkdirFunc(target, source, env):
+ t = target[0]
+ if not t.exists():
+ t.fs.mkdir(t.abspath)
+ return 0
+
+Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
+
+MkdirBuilder = None
+
+def get_MkdirBuilder():
+ global MkdirBuilder
+ if MkdirBuilder is None:
+ import SCons.Builder
+ import SCons.Defaults
+ # "env" will get filled in by Executor.get_build_env()
+ # calling SCons.Defaults.DefaultEnvironment() when necessary.
+ MkdirBuilder = SCons.Builder.Builder(action = Mkdir,
+ env = None,
+ explain = None,
+ is_explicit = None,
+ target_scanner = SCons.Defaults.DirEntryScanner,
+ name = "MkdirBuilder")
+ return MkdirBuilder
+
+class _Null:
+ pass
+
+_null = _Null()
+
+DefaultSCCSBuilder = None
+DefaultRCSBuilder = None
+
+def get_DefaultSCCSBuilder():
+ global DefaultSCCSBuilder
+ if DefaultSCCSBuilder is None:
+ import SCons.Builder
+ # "env" will get filled in by Executor.get_build_env()
+ # calling SCons.Defaults.DefaultEnvironment() when necessary.
+ act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR')
+ DefaultSCCSBuilder = SCons.Builder.Builder(action = act,
+ env = None,
+ name = "DefaultSCCSBuilder")
+ return DefaultSCCSBuilder
+
+def get_DefaultRCSBuilder():
+ global DefaultRCSBuilder
+ if DefaultRCSBuilder is None:
+ import SCons.Builder
+ # "env" will get filled in by Executor.get_build_env()
+ # calling SCons.Defaults.DefaultEnvironment() when necessary.
+ act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR')
+ DefaultRCSBuilder = SCons.Builder.Builder(action = act,
+ env = None,
+ name = "DefaultRCSBuilder")
+ return DefaultRCSBuilder
+
+# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem.
+_is_cygwin = sys.platform == "cygwin"
+if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin:
+ def _my_normcase(x):
+ return x
+else:
+ def _my_normcase(x):
+ return string.upper(x)
+
+
+
+class DiskChecker:
+ def __init__(self, type, do, ignore):
+ self.type = type
+ self.do = do
+ self.ignore = ignore
+ self.set_do()
+ def set_do(self):
+ self.__call__ = self.do
+ def set_ignore(self):
+ self.__call__ = self.ignore
+ def set(self, list):
+ if self.type in list:
+ self.set_do()
+ else:
+ self.set_ignore()
+
+def do_diskcheck_match(node, predicate, errorfmt):
+ result = predicate()
+ try:
+ # If calling the predicate() cached a None value from stat(),
+ # remove it so it doesn't interfere with later attempts to
+ # build this Node as we walk the DAG. (This isn't a great way
+ # to do this, we're reaching into an interface that doesn't
+ # really belong to us, but it's all about performance, so
+ # for now we'll just document the dependency...)
+ if node._memo['stat'] is None:
+ del node._memo['stat']
+ except (AttributeError, KeyError):
+ pass
+ if result:
+ raise TypeError, errorfmt % node.abspath
+
+def ignore_diskcheck_match(node, predicate, errorfmt):
+ pass
+
+def do_diskcheck_rcs(node, name):
+ try:
+ rcs_dir = node.rcs_dir
+ except AttributeError:
+ if node.entry_exists_on_disk('RCS'):
+ rcs_dir = node.Dir('RCS')
+ else:
+ rcs_dir = None
+ node.rcs_dir = rcs_dir
+ if rcs_dir:
+ return rcs_dir.entry_exists_on_disk(name+',v')
+ return None
+
+def ignore_diskcheck_rcs(node, name):
+ return None
+
+def do_diskcheck_sccs(node, name):
+ try:
+ sccs_dir = node.sccs_dir
+ except AttributeError:
+ if node.entry_exists_on_disk('SCCS'):
+ sccs_dir = node.Dir('SCCS')
+ else:
+ sccs_dir = None
+ node.sccs_dir = sccs_dir
+ if sccs_dir:
+ return sccs_dir.entry_exists_on_disk('s.'+name)
+ return None
+
+def ignore_diskcheck_sccs(node, name):
+ return None
+
+diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match)
+diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs)
+diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs)
+
+diskcheckers = [
+ diskcheck_match,
+ diskcheck_rcs,
+ diskcheck_sccs,
+]
+
+def set_diskcheck(list):
+ for dc in diskcheckers:
+ dc.set(list)
+
+def diskcheck_types():
+ return map(lambda dc: dc.type, diskcheckers)
+
+
+
+class EntryProxy(SCons.Util.Proxy):
+ def __get_abspath(self):
+ entry = self.get()
+ return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(),
+ entry.name + "_abspath")
+
+ def __get_filebase(self):
+ name = self.get().name
+ return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0],
+ name + "_filebase")
+
+ def __get_suffix(self):
+ name = self.get().name
+ return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1],
+ name + "_suffix")
+
+ def __get_file(self):
+ name = self.get().name
+ return SCons.Subst.SpecialAttrWrapper(name, name + "_file")
+
+ def __get_base_path(self):
+ """Return the file's directory and file name, with the
+ suffix stripped."""
+ entry = self.get()
+ return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0],
+ entry.name + "_base")
+
+ def __get_posix_path(self):
+ """Return the path with / as the path separator,
+ regardless of platform."""
+ if os.sep == '/':
+ return self
+ else:
+ entry = self.get()
+ r = string.replace(entry.get_path(), os.sep, '/')
+ return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix")
+
+ def __get_windows_path(self):
+ """Return the path with \ as the path separator,
+ regardless of platform."""
+ if os.sep == '\\':
+ return self
+ else:
+ entry = self.get()
+ r = string.replace(entry.get_path(), os.sep, '\\')
+ return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows")
+
+ def __get_srcnode(self):
+ return EntryProxy(self.get().srcnode())
+
+ def __get_srcdir(self):
+ """Returns the directory containing the source node linked to this
+ node via VariantDir(), or the directory of this node if not linked."""
+ return EntryProxy(self.get().srcnode().dir)
+
+ def __get_rsrcnode(self):
+ return EntryProxy(self.get().srcnode().rfile())
+
+ def __get_rsrcdir(self):
+ """Returns the directory containing the source node linked to this
+ node via VariantDir(), or the directory of this node if not linked."""
+ return EntryProxy(self.get().srcnode().rfile().dir)
+
+ def __get_dir(self):
+ return EntryProxy(self.get().dir)
+
+ dictSpecialAttrs = { "base" : __get_base_path,
+ "posix" : __get_posix_path,
+ "windows" : __get_windows_path,
+ "win32" : __get_windows_path,
+ "srcpath" : __get_srcnode,
+ "srcdir" : __get_srcdir,
+ "dir" : __get_dir,
+ "abspath" : __get_abspath,
+ "filebase" : __get_filebase,
+ "suffix" : __get_suffix,
+ "file" : __get_file,
+ "rsrcpath" : __get_rsrcnode,
+ "rsrcdir" : __get_rsrcdir,
+ }
+
+ def __getattr__(self, name):
+ # This is how we implement the "special" attributes
+ # such as base, posix, srcdir, etc.
+ try:
+ attr_function = self.dictSpecialAttrs[name]
+ except KeyError:
+ try:
+ attr = SCons.Util.Proxy.__getattr__(self, name)
+ except AttributeError, e:
+ # Raise our own AttributeError subclass with an
+ # overridden __str__() method that identifies the
+ # name of the entry that caused the exception.
+ raise EntryProxyAttributeError(self, name)
+ return attr
+ else:
+ return attr_function(self)
+
+class Base(SCons.Node.Node):
+ """A generic class for file system entries. This class is for
+ when we don't know yet whether the entry being looked up is a file
+ or a directory. Instances of this class can morph into either
+ Dir or File objects by a later, more precise lookup.
+
+ Note: this class does not define __cmp__ and __hash__ for
+ efficiency reasons. SCons does a lot of comparing of
+ Node.FS.{Base,Entry,File,Dir} objects, so those operations must be
+ as fast as possible, which means we want to use Python's built-in
+ object identity comparisons.
+ """
+
+ memoizer_counters = []
+
+ def __init__(self, name, directory, fs):
+ """Initialize a generic Node.FS.Base object.
+
+ Call the superclass initialization, take care of setting up
+ our relative and absolute paths, identify our parent
+ directory, and indicate that this node should use
+ signatures."""
+ if __debug__: logInstanceCreation(self, 'Node.FS.Base')
+ SCons.Node.Node.__init__(self)
+
+ # Filenames and paths are probably reused and are intern'ed to
+ # save some memory.
+ self.name = SCons.Util.silent_intern(name)
+ self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1])
+ self.fs = fs
+
+ assert directory, "A directory must be provided"
+
+ self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name))
+ self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name))
+ if directory.path == '.':
+ self.path = SCons.Util.silent_intern(name)
+ else:
+ self.path = SCons.Util.silent_intern(directory.entry_path(name))
+ if directory.tpath == '.':
+ self.tpath = SCons.Util.silent_intern(name)
+ else:
+ self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name))
+ self.path_elements = directory.path_elements + [self]
+
+ self.dir = directory
+ self.cwd = None # will hold the SConscript directory for target nodes
+ self.duplicate = directory.duplicate
+
+ def str_for_display(self):
+ return '"' + self.__str__() + '"'
+
+ def must_be_same(self, klass):
+ """
+ This node, which already existed, is being looked up as the
+ specified klass. Raise an exception if it isn't.
+ """
+ if isinstance(self, klass) or klass is Entry:
+ return
+ raise TypeError, "Tried to lookup %s '%s' as a %s." %\
+ (self.__class__.__name__, self.path, klass.__name__)
+
+ def get_dir(self):
+ return self.dir
+
+ def get_suffix(self):
+ return self.suffix
+
+ def rfile(self):
+ return self
+
+ def __str__(self):
+ """A Node.FS.Base object's string representation is its path
+ name."""
+ global Save_Strings
+ if Save_Strings:
+ return self._save_str()
+ return self._get_str()
+
+ memoizer_counters.append(SCons.Memoize.CountValue('_save_str'))
+
+ def _save_str(self):
+ try:
+ return self._memo['_save_str']
+ except KeyError:
+ pass
+ result = intern(self._get_str())
+ self._memo['_save_str'] = result
+ return result
+
+ def _get_str(self):
+ global Save_Strings
+ if self.duplicate or self.is_derived():
+ return self.get_path()
+ srcnode = self.srcnode()
+ if srcnode.stat() is None and self.stat() is not None:
+ result = self.get_path()
+ else:
+ result = srcnode.get_path()
+ if not Save_Strings:
+ # We're not at the point where we're saving the string string
+ # representations of FS Nodes (because we haven't finished
+ # reading the SConscript files and need to have str() return
+ # things relative to them). That also means we can't yet
+ # cache values returned (or not returned) by stat(), since
+ # Python code in the SConscript files might still create
+ # or otherwise affect the on-disk file. So get rid of the
+ # values that the underlying stat() method saved.
+ try: del self._memo['stat']
+ except KeyError: pass
+ if self is not srcnode:
+ try: del srcnode._memo['stat']
+ except KeyError: pass
+ return result
+
+ rstr = __str__
+
+ memoizer_counters.append(SCons.Memoize.CountValue('stat'))
+
+ def stat(self):
+ try: return self._memo['stat']
+ except KeyError: pass
+ try: result = self.fs.stat(self.abspath)
+ except os.error: result = None
+ self._memo['stat'] = result
+ return result
+
+ def exists(self):
+ return self.stat() is not None
+
+ def rexists(self):
+ return self.rfile().exists()
+
+ def getmtime(self):
+ st = self.stat()
+ if st: return st[stat.ST_MTIME]
+ else: return None
+
+ def getsize(self):
+ st = self.stat()
+ if st: return st[stat.ST_SIZE]
+ else: return None
+
+ def isdir(self):
+ st = self.stat()
+ return st is not None and stat.S_ISDIR(st[stat.ST_MODE])
+
+ def isfile(self):
+ st = self.stat()
+ return st is not None and stat.S_ISREG(st[stat.ST_MODE])
+
+ if hasattr(os, 'symlink'):
+ def islink(self):
+ try: st = self.fs.lstat(self.abspath)
+ except os.error: return 0
+ return stat.S_ISLNK(st[stat.ST_MODE])
+ else:
+ def islink(self):
+ return 0 # no symlinks
+
+ def is_under(self, dir):
+ if self is dir:
+ return 1
+ else:
+ return self.dir.is_under(dir)
+
+ def set_local(self):
+ self._local = 1
+
+ def srcnode(self):
+ """If this node is in a build path, return the node
+ corresponding to its source file. Otherwise, return
+ ourself.
+ """
+ srcdir_list = self.dir.srcdir_list()
+ if srcdir_list:
+ srcnode = srcdir_list[0].Entry(self.name)
+ srcnode.must_be_same(self.__class__)
+ return srcnode
+ return self
+
+ def get_path(self, dir=None):
+ """Return path relative to the current working directory of the
+ Node.FS.Base object that owns us."""
+ if not dir:
+ dir = self.fs.getcwd()
+ if self == dir:
+ return '.'
+ path_elems = self.path_elements
+ try: i = path_elems.index(dir)
+ except ValueError: pass
+ else: path_elems = path_elems[i+1:]
+ path_elems = map(lambda n: n.name, path_elems)
+ return string.join(path_elems, os.sep)
+
+ def set_src_builder(self, builder):
+ """Set the source code builder for this node."""
+ self.sbuilder = builder
+ if not self.has_builder():
+ self.builder_set(builder)
+
+ def src_builder(self):
+ """Fetch the source code builder for this node.
+
+ If there isn't one, we cache the source code builder specified
+ for the directory (which in turn will cache the value from its
+ parent directory, and so on up to the file system root).
+ """
+ try:
+ scb = self.sbuilder
+ except AttributeError:
+ scb = self.dir.src_builder()
+ self.sbuilder = scb
+ return scb
+
+ def get_abspath(self):
+ """Get the absolute path of the file."""
+ return self.abspath
+
+ def for_signature(self):
+ # Return just our name. Even an absolute path would not work,
+ # because that can change thanks to symlinks or remapped network
+ # paths.
+ return self.name
+
+ def get_subst_proxy(self):
+ try:
+ return self._proxy
+ except AttributeError:
+ ret = EntryProxy(self)
+ self._proxy = ret
+ return ret
+
+ def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext):
+ """
+
+ Generates a target entry that corresponds to this entry (usually
+ a source file) with the specified prefix and suffix.
+
+ Note that this method can be overridden dynamically for generated
+ files that need different behavior. See Tool/swig.py for
+ an example.
+ """
+ return self.dir.Entry(prefix + splitext(self.name)[0] + suffix)
+
+ def _Rfindalldirs_key(self, pathlist):
+ return pathlist
+
+ memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key))
+
+ def Rfindalldirs(self, pathlist):
+ """
+ Return all of the directories for a given path list, including
+ corresponding "backing" directories in any repositories.
+
+ The Node lookups are relative to this Node (typically a
+ directory), so memoizing result saves cycles from looking
+ up the same path for each target in a given directory.
+ """
+ try:
+ memo_dict = self._memo['Rfindalldirs']
+ except KeyError:
+ memo_dict = {}
+ self._memo['Rfindalldirs'] = memo_dict
+ else:
+ try:
+ return memo_dict[pathlist]
+ except KeyError:
+ pass
+
+ create_dir_relative_to_self = self.Dir
+ result = []
+ for path in pathlist:
+ if isinstance(path, SCons.Node.Node):
+ result.append(path)
+ else:
+ dir = create_dir_relative_to_self(path)
+ result.extend(dir.get_all_rdirs())
+
+ memo_dict[pathlist] = result
+
+ return result
+
+ def RDirs(self, pathlist):
+ """Search for a list of directories in the Repository list."""
+ cwd = self.cwd or self.fs._cwd
+ return cwd.Rfindalldirs(pathlist)
+
+ memoizer_counters.append(SCons.Memoize.CountValue('rentry'))
+
+ def rentry(self):
+ try:
+ return self._memo['rentry']
+ except KeyError:
+ pass
+ result = self
+ if not self.exists():
+ norm_name = _my_normcase(self.name)
+ for dir in self.dir.get_all_rdirs():
+ try:
+ node = dir.entries[norm_name]
+ except KeyError:
+ if dir.entry_exists_on_disk(self.name):
+ result = dir.Entry(self.name)
+ break
+ self._memo['rentry'] = result
+ return result
+
+ def _glob1(self, pattern, ondisk=True, source=False, strings=False):
+ return []
+
+class Entry(Base):
+ """This is the class for generic Node.FS entries--that is, things
+ that could be a File or a Dir, but we're just not sure yet.
+ Consequently, the methods in this class really exist just to
+ transform their associated object into the right class when the
+ time comes, and then call the same-named method in the transformed
+ class."""
+
+ def diskcheck_match(self):
+ pass
+
+ def disambiguate(self, must_exist=None):
+ """
+ """
+ if self.isdir():
+ self.__class__ = Dir
+ self._morph()
+ elif self.isfile():
+ self.__class__ = File
+ self._morph()
+ self.clear()
+ else:
+ # There was nothing on-disk at this location, so look in
+ # the src directory.
+ #
+ # We can't just use self.srcnode() straight away because
+ # that would create an actual Node for this file in the src
+ # directory, and there might not be one. Instead, use the
+ # dir_on_disk() method to see if there's something on-disk
+ # with that name, in which case we can go ahead and call
+ # self.srcnode() to create the right type of entry.
+ srcdir = self.dir.srcnode()
+ if srcdir != self.dir and \
+ srcdir.entry_exists_on_disk(self.name) and \
+ self.srcnode().isdir():
+ self.__class__ = Dir
+ self._morph()
+ elif must_exist:
+ msg = "No such file or directory: '%s'" % self.abspath
+ raise SCons.Errors.UserError, msg
+ else:
+ self.__class__ = File
+ self._morph()
+ self.clear()
+ return self
+
+ def rfile(self):
+ """We're a generic Entry, but the caller is actually looking for
+ a File at this point, so morph into one."""
+ self.__class__ = File
+ self._morph()
+ self.clear()
+ return File.rfile(self)
+
+ def scanner_key(self):
+ return self.get_suffix()
+
+ def get_contents(self):
+ """Fetch the contents of the entry. Returns the exact binary
+ contents of the file."""
+ try:
+ self = self.disambiguate(must_exist=1)
+ except SCons.Errors.UserError:
+ # There was nothing on disk with which to disambiguate
+ # this entry. Leave it as an Entry, but return a null
+ # string so calls to get_contents() in emitters and the
+ # like (e.g. in qt.py) don't have to disambiguate by hand
+ # or catch the exception.
+ return ''
+ else:
+ return self.get_contents()
+
+ def get_text_contents(self):
+ """Fetch the decoded text contents of a Unicode encoded Entry.
+
+ Since this should return the text contents from the file
+ system, we check to see into what sort of subclass we should
+ morph this Entry."""
+ try:
+ self = self.disambiguate(must_exist=1)
+ except SCons.Errors.UserError:
+ # There was nothing on disk with which to disambiguate
+ # this entry. Leave it as an Entry, but return a null
+ # string so calls to get_text_contents() in emitters and
+ # the like (e.g. in qt.py) don't have to disambiguate by
+ # hand or catch the exception.
+ return ''
+ else:
+ return self.get_text_contents()
+
+ def must_be_same(self, klass):
+ """Called to make sure a Node is a Dir. Since we're an
+ Entry, we can morph into one."""
+ if self.__class__ is not klass:
+ self.__class__ = klass
+ self._morph()
+ self.clear()
+
+ # The following methods can get called before the Taskmaster has
+ # had a chance to call disambiguate() directly to see if this Entry
+ # should really be a Dir or a File. We therefore use these to call
+ # disambiguate() transparently (from our caller's point of view).
+ #
+ # Right now, this minimal set of methods has been derived by just
+ # looking at some of the methods that will obviously be called early
+ # in any of the various Taskmasters' calling sequences, and then
+ # empirically figuring out which additional methods are necessary
+ # to make various tests pass.
+
+ def exists(self):
+ """Return if the Entry exists. Check the file system to see
+ what we should turn into first. Assume a file if there's no
+ directory."""
+ return self.disambiguate().exists()
+
+ def rel_path(self, other):
+ d = self.disambiguate()
+ if d.__class__ is Entry:
+ raise "rel_path() could not disambiguate File/Dir"
+ return d.rel_path(other)
+
+ def new_ninfo(self):
+ return self.disambiguate().new_ninfo()
+
+ def changed_since_last_build(self, target, prev_ni):
+ return self.disambiguate().changed_since_last_build(target, prev_ni)
+
+ def _glob1(self, pattern, ondisk=True, source=False, strings=False):
+ return self.disambiguate()._glob1(pattern, ondisk, source, strings)
+
+ def get_subst_proxy(self):
+ return self.disambiguate().get_subst_proxy()
+
+# This is for later so we can differentiate between Entry the class and Entry
+# the method of the FS class.
+_classEntry = Entry
+
+
+class LocalFS:
+
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ # This class implements an abstraction layer for operations involving
+ # a local file system. Essentially, this wraps any function in
+ # the os, os.path or shutil modules that we use to actually go do
+ # anything with or to the local file system.
+ #
+ # Note that there's a very good chance we'll refactor this part of
+ # the architecture in some way as we really implement the interface(s)
+ # for remote file system Nodes. For example, the right architecture
+ # might be to have this be a subclass instead of a base class.
+ # Nevertheless, we're using this as a first step in that direction.
+ #
+ # We're not using chdir() yet because the calling subclass method
+ # needs to use os.chdir() directly to avoid recursion. Will we
+ # really need this one?
+ #def chdir(self, path):
+ # return os.chdir(path)
+ def chmod(self, path, mode):
+ return os.chmod(path, mode)
+ def copy(self, src, dst):
+ return shutil.copy(src, dst)
+ def copy2(self, src, dst):
+ return shutil.copy2(src, dst)
+ def exists(self, path):
+ return os.path.exists(path)
+ def getmtime(self, path):
+ return os.path.getmtime(path)
+ def getsize(self, path):
+ return os.path.getsize(path)
+ def isdir(self, path):
+ return os.path.isdir(path)
+ def isfile(self, path):
+ return os.path.isfile(path)
+ def link(self, src, dst):
+ return os.link(src, dst)
+ def lstat(self, path):
+ return os.lstat(path)
+ def listdir(self, path):
+ return os.listdir(path)
+ def makedirs(self, path):
+ return os.makedirs(path)
+ def mkdir(self, path):
+ return os.mkdir(path)
+ def rename(self, old, new):
+ return os.rename(old, new)
+ def stat(self, path):
+ return os.stat(path)
+ def symlink(self, src, dst):
+ return os.symlink(src, dst)
+ def open(self, path):
+ return open(path)
+ def unlink(self, path):
+ return os.unlink(path)
+
+ if hasattr(os, 'symlink'):
+ def islink(self, path):
+ return os.path.islink(path)
+ else:
+ def islink(self, path):
+ return 0 # no symlinks
+
+ if hasattr(os, 'readlink'):
+ def readlink(self, file):
+ return os.readlink(file)
+ else:
+ def readlink(self, file):
+ return ''
+
+
+#class RemoteFS:
+# # Skeleton for the obvious methods we might need from the
+# # abstraction layer for a remote filesystem.
+# def upload(self, local_src, remote_dst):
+# pass
+# def download(self, remote_src, local_dst):
+# pass
+
+
+class FS(LocalFS):
+
+ memoizer_counters = []
+
+ def __init__(self, path = None):
+ """Initialize the Node.FS subsystem.
+
+ The supplied path is the top of the source tree, where we
+ expect to find the top-level build file. If no path is
+ supplied, the current directory is the default.
+
+ The path argument must be a valid absolute path.
+ """
+ if __debug__: logInstanceCreation(self, 'Node.FS')
+
+ self._memo = {}
+
+ self.Root = {}
+ self.SConstruct_dir = None
+ self.max_drift = default_max_drift
+
+ self.Top = None
+ if path is None:
+ self.pathTop = os.getcwd()
+ else:
+ self.pathTop = path
+ self.defaultDrive = _my_normcase(os.path.splitdrive(self.pathTop)[0])
+
+ self.Top = self.Dir(self.pathTop)
+ self.Top.path = '.'
+ self.Top.tpath = '.'
+ self._cwd = self.Top
+
+ DirNodeInfo.fs = self
+ FileNodeInfo.fs = self
+
+ def set_SConstruct_dir(self, dir):
+ self.SConstruct_dir = dir
+
+ def get_max_drift(self):
+ return self.max_drift
+
+ def set_max_drift(self, max_drift):
+ self.max_drift = max_drift
+
+ def getcwd(self):
+ return self._cwd
+
+ def chdir(self, dir, change_os_dir=0):
+ """Change the current working directory for lookups.
+ If change_os_dir is true, we will also change the "real" cwd
+ to match.
+ """
+ curr=self._cwd
+ try:
+ if dir is not None:
+ self._cwd = dir
+ if change_os_dir:
+ os.chdir(dir.abspath)
+ except OSError:
+ self._cwd = curr
+ raise
+
+ def get_root(self, drive):
+ """
+ Returns the root directory for the specified drive, creating
+ it if necessary.
+ """
+ drive = _my_normcase(drive)
+ try:
+ return self.Root[drive]
+ except KeyError:
+ root = RootDir(drive, self)
+ self.Root[drive] = root
+ if not drive:
+ self.Root[self.defaultDrive] = root
+ elif drive == self.defaultDrive:
+ self.Root[''] = root
+ return root
+
+ def _lookup(self, p, directory, fsclass, create=1):
+ """
+ The generic entry point for Node lookup with user-supplied data.
+
+ This translates arbitrary input into a canonical Node.FS object
+ of the specified fsclass. The general approach for strings is
+ to turn it into a fully normalized absolute path and then call
+ the root directory's lookup_abs() method for the heavy lifting.
+
+ If the path name begins with '#', it is unconditionally
+ interpreted relative to the top-level directory of this FS. '#'
+ is treated as a synonym for the top-level SConstruct directory,
+ much like '~' is treated as a synonym for the user's home
+ directory in a UNIX shell. So both '#foo' and '#/foo' refer
+ to the 'foo' subdirectory underneath the top-level SConstruct
+ directory.
+
+ If the path name is relative, then the path is looked up relative
+ to the specified directory, or the current directory (self._cwd,
+ typically the SConscript directory) if the specified directory
+ is None.
+ """
+ if isinstance(p, Base):
+ # It's already a Node.FS object. Make sure it's the right
+ # class and return.
+ p.must_be_same(fsclass)
+ return p
+ # str(p) in case it's something like a proxy object
+ p = str(p)
+
+ initial_hash = (p[0:1] == '#')
+ if initial_hash:
+ # There was an initial '#', so we strip it and override
+ # whatever directory they may have specified with the
+ # top-level SConstruct directory.
+ p = p[1:]
+ directory = self.Top
+
+ if directory and not isinstance(directory, Dir):
+ directory = self.Dir(directory)
+
+ if do_splitdrive:
+ drive, p = os.path.splitdrive(p)
+ else:
+ drive = ''
+ if drive and not p:
+ # This causes a naked drive letter to be treated as a synonym
+ # for the root directory on that drive.
+ p = os.sep
+ absolute = os.path.isabs(p)
+
+ needs_normpath = needs_normpath_check.match(p)
+
+ if initial_hash or not absolute:
+ # This is a relative lookup, either to the top-level
+ # SConstruct directory (because of the initial '#') or to
+ # the current directory (the path name is not absolute).
+ # Add the string to the appropriate directory lookup path,
+ # after which the whole thing gets normalized.
+ if not directory:
+ directory = self._cwd
+ if p:
+ p = directory.labspath + '/' + p
+ else:
+ p = directory.labspath
+
+ if needs_normpath:
+ p = os.path.normpath(p)
+
+ if drive or absolute:
+ root = self.get_root(drive)
+ else:
+ if not directory:
+ directory = self._cwd
+ root = directory.root
+
+ if os.sep != '/':
+ p = string.replace(p, os.sep, '/')
+ return root._lookup_abs(p, fsclass, create)
+
+ def Entry(self, name, directory = None, create = 1):
+ """Look up or create a generic Entry node with the specified name.
+ If the name is a relative path (begins with ./, ../, or a file
+ name), then it is looked up relative to the supplied directory
+ node, or to the top level directory of the FS (supplied at
+ construction time) if no directory is supplied.
+ """
+ return self._lookup(name, directory, Entry, create)
+
+ def File(self, name, directory = None, create = 1):
+ """Look up or create a File node with the specified name. If
+ the name is a relative path (begins with ./, ../, or a file name),
+ then it is looked up relative to the supplied directory node,
+ or to the top level directory of the FS (supplied at construction
+ time) if no directory is supplied.
+
+ This method will raise TypeError if a directory is found at the
+ specified path.
+ """
+ return self._lookup(name, directory, File, create)
+
+ def Dir(self, name, directory = None, create = True):
+ """Look up or create a Dir node with the specified name. If
+ the name is a relative path (begins with ./, ../, or a file name),
+ then it is looked up relative to the supplied directory node,
+ or to the top level directory of the FS (supplied at construction
+ time) if no directory is supplied.
+
+ This method will raise TypeError if a normal file is found at the
+ specified path.
+ """
+ return self._lookup(name, directory, Dir, create)
+
+ def VariantDir(self, variant_dir, src_dir, duplicate=1):
+ """Link the supplied variant directory to the source directory
+ for purposes of building files."""
+
+ if not isinstance(src_dir, SCons.Node.Node):
+ src_dir = self.Dir(src_dir)
+ if not isinstance(variant_dir, SCons.Node.Node):
+ variant_dir = self.Dir(variant_dir)
+ if src_dir.is_under(variant_dir):
+ raise SCons.Errors.UserError, "Source directory cannot be under variant directory."
+ if variant_dir.srcdir:
+ if variant_dir.srcdir == src_dir:
+ return # We already did this.
+ raise SCons.Errors.UserError, "'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir)
+ variant_dir.link(src_dir, duplicate)
+
+ def Repository(self, *dirs):
+ """Specify Repository directories to search."""
+ for d in dirs:
+ if not isinstance(d, SCons.Node.Node):
+ d = self.Dir(d)
+ self.Top.addRepository(d)
+
+ def variant_dir_target_climb(self, orig, dir, tail):
+ """Create targets in corresponding variant directories
+
+ Climb the directory tree, and look up path names
+ relative to any linked variant directories we find.
+
+ Even though this loops and walks up the tree, we don't memoize
+ the return value because this is really only used to process
+ the command-line targets.
+ """
+ targets = []
+ message = None
+ fmt = "building associated VariantDir targets: %s"
+ start_dir = dir
+ while dir:
+ for bd in dir.variant_dirs:
+ if start_dir.is_under(bd):
+ # If already in the build-dir location, don't reflect
+ return [orig], fmt % str(orig)
+ p = apply(os.path.join, [bd.path] + tail)
+ targets.append(self.Entry(p))
+ tail = [dir.name] + tail
+ dir = dir.up()
+ if targets:
+ message = fmt % string.join(map(str, targets))
+ return targets, message
+
+ def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None):
+ """
+ Globs
+
+ This is mainly a shim layer
+ """
+ if cwd is None:
+ cwd = self.getcwd()
+ return cwd.glob(pathname, ondisk, source, strings)
+
+class DirNodeInfo(SCons.Node.NodeInfoBase):
+ # This should get reset by the FS initialization.
+ current_version_id = 1
+
+ fs = None
+
+ def str_to_node(self, s):
+ top = self.fs.Top
+ root = top.root
+ if do_splitdrive:
+ drive, s = os.path.splitdrive(s)
+ if drive:
+ root = self.fs.get_root(drive)
+ if not os.path.isabs(s):
+ s = top.labspath + '/' + s
+ return root._lookup_abs(s, Entry)
+
+class DirBuildInfo(SCons.Node.BuildInfoBase):
+ current_version_id = 1
+
+glob_magic_check = re.compile('[*?[]')
+
+def has_glob_magic(s):
+ return glob_magic_check.search(s) is not None
+
+class Dir(Base):
+ """A class for directories in a file system.
+ """
+
+ memoizer_counters = []
+
+ NodeInfo = DirNodeInfo
+ BuildInfo = DirBuildInfo
+
+ def __init__(self, name, directory, fs):
+ if __debug__: logInstanceCreation(self, 'Node.FS.Dir')
+ Base.__init__(self, name, directory, fs)
+ self._morph()
+
+ def _morph(self):
+ """Turn a file system Node (either a freshly initialized directory
+ object or a separate Entry object) into a proper directory object.
+
+ Set up this directory's entries and hook it into the file
+ system tree. Specify that directories (this Node) don't use
+ signatures for calculating whether they're current.
+ """
+
+ self.repositories = []
+ self.srcdir = None
+
+ self.entries = {}
+ self.entries['.'] = self
+ self.entries['..'] = self.dir
+ self.cwd = self
+ self.searched = 0
+ self._sconsign = None
+ self.variant_dirs = []
+ self.root = self.dir.root
+
+ # Don't just reset the executor, replace its action list,
+ # because it might have some pre-or post-actions that need to
+ # be preserved.
+ self.builder = get_MkdirBuilder()
+ self.get_executor().set_action_list(self.builder.action)
+
+ def diskcheck_match(self):
+ diskcheck_match(self, self.isfile,
+ "File %s found where directory expected.")
+
+ def __clearRepositoryCache(self, duplicate=None):
+ """Called when we change the repository(ies) for a directory.
+ This clears any cached information that is invalidated by changing
+ the repository."""
+
+ for node in self.entries.values():
+ if node != self.dir:
+ if node != self and isinstance(node, Dir):
+ node.__clearRepositoryCache(duplicate)
+ else:
+ node.clear()
+ try:
+ del node._srcreps
+ except AttributeError:
+ pass
+ if duplicate is not None:
+ node.duplicate=duplicate
+
+ def __resetDuplicate(self, node):
+ if node != self:
+ node.duplicate = node.get_dir().duplicate
+
+ def Entry(self, name):
+ """
+ Looks up or creates an entry node named 'name' relative to
+ this directory.
+ """
+ return self.fs.Entry(name, self)
+
+ def Dir(self, name, create=True):
+ """
+ Looks up or creates a directory node named 'name' relative to
+ this directory.
+ """
+ return self.fs.Dir(name, self, create)
+
+ def File(self, name):
+ """
+ Looks up or creates a file node named 'name' relative to
+ this directory.
+ """
+ return self.fs.File(name, self)
+
+ def _lookup_rel(self, name, klass, create=1):
+ """
+ Looks up a *normalized* relative path name, relative to this
+ directory.
+
+ This method is intended for use by internal lookups with
+ already-normalized path data. For general-purpose lookups,
+ use the Entry(), Dir() and File() methods above.
+
+ This method does *no* input checking and will die or give
+ incorrect results if it's passed a non-normalized path name (e.g.,
+ a path containing '..'), an absolute path name, a top-relative
+ ('#foo') path name, or any kind of object.
+ """
+ name = self.entry_labspath(name)
+ return self.root._lookup_abs(name, klass, create)
+
+ def link(self, srcdir, duplicate):
+ """Set this directory as the variant directory for the
+ supplied source directory."""
+ self.srcdir = srcdir
+ self.duplicate = duplicate
+ self.__clearRepositoryCache(duplicate)
+ srcdir.variant_dirs.append(self)
+
+ def getRepositories(self):
+ """Returns a list of repositories for this directory.
+ """
+ if self.srcdir and not self.duplicate:
+ return self.srcdir.get_all_rdirs() + self.repositories
+ return self.repositories
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs'))
+
+ def get_all_rdirs(self):
+ try:
+ return list(self._memo['get_all_rdirs'])
+ except KeyError:
+ pass
+
+ result = [self]
+ fname = '.'
+ dir = self
+ while dir:
+ for rep in dir.getRepositories():
+ result.append(rep.Dir(fname))
+ if fname == '.':
+ fname = dir.name
+ else:
+ fname = dir.name + os.sep + fname
+ dir = dir.up()
+
+ self._memo['get_all_rdirs'] = list(result)
+
+ return result
+
+ def addRepository(self, dir):
+ if dir != self and not dir in self.repositories:
+ self.repositories.append(dir)
+ dir.tpath = '.'
+ self.__clearRepositoryCache()
+
+ def up(self):
+ return self.entries['..']
+
+ def _rel_path_key(self, other):
+ return str(other)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key))
+
+ def rel_path(self, other):
+ """Return a path to "other" relative to this directory.
+ """
+
+ # This complicated and expensive method, which constructs relative
+ # paths between arbitrary Node.FS objects, is no longer used
+ # by SCons itself. It was introduced to store dependency paths
+ # in .sconsign files relative to the target, but that ended up
+ # being significantly inefficient.
+ #
+ # We're continuing to support the method because some SConstruct
+ # files out there started using it when it was available, and
+ # we're all about backwards compatibility..
+
+ try:
+ memo_dict = self._memo['rel_path']
+ except KeyError:
+ memo_dict = {}
+ self._memo['rel_path'] = memo_dict
+ else:
+ try:
+ return memo_dict[other]
+ except KeyError:
+ pass
+
+ if self is other:
+ result = '.'
+
+ elif not other in self.path_elements:
+ try:
+ other_dir = other.get_dir()
+ except AttributeError:
+ result = str(other)
+ else:
+ if other_dir is None:
+ result = other.name
+ else:
+ dir_rel_path = self.rel_path(other_dir)
+ if dir_rel_path == '.':
+ result = other.name
+ else:
+ result = dir_rel_path + os.sep + other.name
+ else:
+ i = self.path_elements.index(other) + 1
+
+ path_elems = ['..'] * (len(self.path_elements) - i) \
+ + map(lambda n: n.name, other.path_elements[i:])
+
+ result = string.join(path_elems, os.sep)
+
+ memo_dict[other] = result
+
+ return result
+
+ def get_env_scanner(self, env, kw={}):
+ import SCons.Defaults
+ return SCons.Defaults.DirEntryScanner
+
+ def get_target_scanner(self):
+ import SCons.Defaults
+ return SCons.Defaults.DirEntryScanner
+
+ def get_found_includes(self, env, scanner, path):
+ """Return this directory's implicit dependencies.
+
+ We don't bother caching the results because the scan typically
+ shouldn't be requested more than once (as opposed to scanning
+ .h file contents, which can be requested as many times as the
+ files is #included by other files).
+ """
+ if not scanner:
+ return []
+ # Clear cached info for this Dir. If we already visited this
+ # directory on our walk down the tree (because we didn't know at
+ # that point it was being used as the source for another Node)
+ # then we may have calculated build signature before realizing
+ # we had to scan the disk. Now that we have to, though, we need
+ # to invalidate the old calculated signature so that any node
+ # dependent on our directory structure gets one that includes
+ # info about everything on disk.
+ self.clear()
+ return scanner(self, env, path)
+
+ #
+ # Taskmaster interface subsystem
+ #
+
+ def prepare(self):
+ pass
+
+ def build(self, **kw):
+ """A null "builder" for directories."""
+ global MkdirBuilder
+ if self.builder is not MkdirBuilder:
+ apply(SCons.Node.Node.build, [self,], kw)
+
+ #
+ #
+ #
+
+ def _create(self):
+ """Create this directory, silently and without worrying about
+ whether the builder is the default or not."""
+ listDirs = []
+ parent = self
+ while parent:
+ if parent.exists():
+ break
+ listDirs.append(parent)
+ p = parent.up()
+ if p is None:
+ # Don't use while: - else: for this condition because
+ # if so, then parent is None and has no .path attribute.
+ raise SCons.Errors.StopError, parent.path
+ parent = p
+ listDirs.reverse()
+ for dirnode in listDirs:
+ try:
+ # Don't call dirnode.build(), call the base Node method
+ # directly because we definitely *must* create this
+ # directory. The dirnode.build() method will suppress
+ # the build if it's the default builder.
+ SCons.Node.Node.build(dirnode)
+ dirnode.get_executor().nullify()
+ # The build() action may or may not have actually
+ # created the directory, depending on whether the -n
+ # option was used or not. Delete the _exists and
+ # _rexists attributes so they can be reevaluated.
+ dirnode.clear()
+ except OSError:
+ pass
+
+ def multiple_side_effect_has_builder(self):
+ global MkdirBuilder
+ return self.builder is not MkdirBuilder and self.has_builder()
+
+ def alter_targets(self):
+ """Return any corresponding targets in a variant directory.
+ """
+ return self.fs.variant_dir_target_climb(self, self, [])
+
+ def scanner_key(self):
+ """A directory does not get scanned."""
+ return None
+
+ def get_text_contents(self):
+ """We already emit things in text, so just return the binary
+ version."""
+ return self.get_contents()
+
+ def get_contents(self):
+ """Return content signatures and names of all our children
+ separated by new-lines. Ensure that the nodes are sorted."""
+ contents = []
+ name_cmp = lambda a, b: cmp(a.name, b.name)
+ sorted_children = self.children()[:]
+ sorted_children.sort(name_cmp)
+ for node in sorted_children:
+ contents.append('%s %s\n' % (node.get_csig(), node.name))
+ return string.join(contents, '')
+
+ def get_csig(self):
+ """Compute the content signature for Directory nodes. In
+ general, this is not needed and the content signature is not
+ stored in the DirNodeInfo. However, if get_contents on a Dir
+ node is called which has a child directory, the child
+ directory should return the hash of its contents."""
+ contents = self.get_contents()
+ return SCons.Util.MD5signature(contents)
+
+ def do_duplicate(self, src):
+ pass
+
+ changed_since_last_build = SCons.Node.Node.state_has_changed
+
+ def is_up_to_date(self):
+ """If any child is not up-to-date, then this directory isn't,
+ either."""
+ if self.builder is not MkdirBuilder and not self.exists():
+ return 0
+ up_to_date = SCons.Node.up_to_date
+ for kid in self.children():
+ if kid.get_state() > up_to_date:
+ return 0
+ return 1
+
+ def rdir(self):
+ if not self.exists():
+ norm_name = _my_normcase(self.name)
+ for dir in self.dir.get_all_rdirs():
+ try: node = dir.entries[norm_name]
+ except KeyError: node = dir.dir_on_disk(self.name)
+ if node and node.exists() and \
+ (isinstance(dir, Dir) or isinstance(dir, Entry)):
+ return node
+ return self
+
+ def sconsign(self):
+ """Return the .sconsign file info for this directory,
+ creating it first if necessary."""
+ if not self._sconsign:
+ import SCons.SConsign
+ self._sconsign = SCons.SConsign.ForDirectory(self)
+ return self._sconsign
+
+ def srcnode(self):
+ """Dir has a special need for srcnode()...if we
+ have a srcdir attribute set, then that *is* our srcnode."""
+ if self.srcdir:
+ return self.srcdir
+ return Base.srcnode(self)
+
+ def get_timestamp(self):
+ """Return the latest timestamp from among our children"""
+ stamp = 0
+ for kid in self.children():
+ if kid.get_timestamp() > stamp:
+ stamp = kid.get_timestamp()
+ return stamp
+
+ def entry_abspath(self, name):
+ return self.abspath + os.sep + name
+
+ def entry_labspath(self, name):
+ return self.labspath + '/' + name
+
+ def entry_path(self, name):
+ return self.path + os.sep + name
+
+ def entry_tpath(self, name):
+ return self.tpath + os.sep + name
+
+ def entry_exists_on_disk(self, name):
+ try:
+ d = self.on_disk_entries
+ except AttributeError:
+ d = {}
+ try:
+ entries = os.listdir(self.abspath)
+ except OSError:
+ pass
+ else:
+ for entry in map(_my_normcase, entries):
+ d[entry] = True
+ self.on_disk_entries = d
+ if sys.platform == 'win32':
+ name = _my_normcase(name)
+ result = d.get(name)
+ if result is None:
+ # Belt-and-suspenders for Windows: check directly for
+ # 8.3 file names that don't show up in os.listdir().
+ result = os.path.exists(self.abspath + os.sep + name)
+ d[name] = result
+ return result
+ else:
+ return d.has_key(name)
+
+ memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list'))
+
+ def srcdir_list(self):
+ try:
+ return self._memo['srcdir_list']
+ except KeyError:
+ pass
+
+ result = []
+
+ dirname = '.'
+ dir = self
+ while dir:
+ if dir.srcdir:
+ result.append(dir.srcdir.Dir(dirname))
+ dirname = dir.name + os.sep + dirname
+ dir = dir.up()
+
+ self._memo['srcdir_list'] = result
+
+ return result
+
+ def srcdir_duplicate(self, name):
+ for dir in self.srcdir_list():
+ if self.is_under(dir):
+ # We shouldn't source from something in the build path;
+ # variant_dir is probably under src_dir, in which case
+ # we are reflecting.
+ break
+ if dir.entry_exists_on_disk(name):
+ srcnode = dir.Entry(name).disambiguate()
+ if self.duplicate:
+ node = self.Entry(name).disambiguate()
+ node.do_duplicate(srcnode)
+ return node
+ else:
+ return srcnode
+ return None
+
+ def _srcdir_find_file_key(self, filename):
+ return filename
+
+ memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key))
+
+ def srcdir_find_file(self, filename):
+ try:
+ memo_dict = self._memo['srcdir_find_file']
+ except KeyError:
+ memo_dict = {}
+ self._memo['srcdir_find_file'] = memo_dict
+ else:
+ try:
+ return memo_dict[filename]
+ except KeyError:
+ pass
+
+ def func(node):
+ if (isinstance(node, File) or isinstance(node, Entry)) and \
+ (node.is_derived() or node.exists()):
+ return node
+ return None
+
+ norm_name = _my_normcase(filename)
+
+ for rdir in self.get_all_rdirs():
+ try: node = rdir.entries[norm_name]
+ except KeyError: node = rdir.file_on_disk(filename)
+ else: node = func(node)
+ if node:
+ result = (node, self)
+ memo_dict[filename] = result
+ return result
+
+ for srcdir in self.srcdir_list():
+ for rdir in srcdir.get_all_rdirs():
+ try: node = rdir.entries[norm_name]
+ except KeyError: node = rdir.file_on_disk(filename)
+ else: node = func(node)
+ if node:
+ result = (File(filename, self, self.fs), srcdir)
+ memo_dict[filename] = result
+ return result
+
+ result = (None, None)
+ memo_dict[filename] = result
+ return result
+
+ def dir_on_disk(self, name):
+ if self.entry_exists_on_disk(name):
+ try: return self.Dir(name)
+ except TypeError: pass
+ node = self.srcdir_duplicate(name)
+ if isinstance(node, File):
+ return None
+ return node
+
+ def file_on_disk(self, name):
+ if self.entry_exists_on_disk(name) or \
+ diskcheck_rcs(self, name) or \
+ diskcheck_sccs(self, name):
+ try: return self.File(name)
+ except TypeError: pass
+ node = self.srcdir_duplicate(name)
+ if isinstance(node, Dir):
+ return None
+ return node
+
+ def walk(self, func, arg):
+ """
+ Walk this directory tree by calling the specified function
+ for each directory in the tree.
+
+ This behaves like the os.path.walk() function, but for in-memory
+ Node.FS.Dir objects. The function takes the same arguments as
+ the functions passed to os.path.walk():
+
+ func(arg, dirname, fnames)
+
+ Except that "dirname" will actually be the directory *Node*,
+ not the string. The '.' and '..' entries are excluded from
+ fnames. The fnames list may be modified in-place to filter the
+ subdirectories visited or otherwise impose a specific order.
+ The "arg" argument is always passed to func() and may be used
+ in any way (or ignored, passing None is common).
+ """
+ entries = self.entries
+ names = entries.keys()
+ names.remove('.')
+ names.remove('..')
+ func(arg, self, names)
+ select_dirs = lambda n, e=entries: isinstance(e[n], Dir)
+ for dirname in filter(select_dirs, names):
+ entries[dirname].walk(func, arg)
+
+ def glob(self, pathname, ondisk=True, source=False, strings=False):
+ """
+ Returns a list of Nodes (or strings) matching a specified
+ pathname pattern.
+
+ Pathname patterns follow UNIX shell semantics: * matches
+ any-length strings of any characters, ? matches any character,
+ and [] can enclose lists or ranges of characters. Matches do
+ not span directory separators.
+
+ The matches take into account Repositories, returning local
+ Nodes if a corresponding entry exists in a Repository (either
+ an in-memory Node or something on disk).
+
+ By defafult, the glob() function matches entries that exist
+ on-disk, in addition to in-memory Nodes. Setting the "ondisk"
+ argument to False (or some other non-true value) causes the glob()
+ function to only match in-memory Nodes. The default behavior is
+ to return both the on-disk and in-memory Nodes.
+
+ The "source" argument, when true, specifies that corresponding
+ source Nodes must be returned if you're globbing in a build
+ directory (initialized with VariantDir()). The default behavior
+ is to return Nodes local to the VariantDir().
+
+ The "strings" argument, when true, returns the matches as strings,
+ not Nodes. The strings are path names relative to this directory.
+
+ The underlying algorithm is adapted from the glob.glob() function
+ in the Python library (but heavily modified), and uses fnmatch()
+ under the covers.
+ """
+ dirname, basename = os.path.split(pathname)
+ if not dirname:
+ result = self._glob1(basename, ondisk, source, strings)
+ result.sort(lambda a, b: cmp(str(a), str(b)))
+ return result
+ if has_glob_magic(dirname):
+ list = self.glob(dirname, ondisk, source, strings=False)
+ else:
+ list = [self.Dir(dirname, create=True)]
+ result = []
+ for dir in list:
+ r = dir._glob1(basename, ondisk, source, strings)
+ if strings:
+ r = map(lambda x, d=str(dir): os.path.join(d, x), r)
+ result.extend(r)
+ result.sort(lambda a, b: cmp(str(a), str(b)))
+ return result
+
+ def _glob1(self, pattern, ondisk=True, source=False, strings=False):
+ """
+ Globs for and returns a list of entry names matching a single
+ pattern in this directory.
+
+ This searches any repositories and source directories for
+ corresponding entries and returns a Node (or string) relative
+ to the current directory if an entry is found anywhere.
+
+ TODO: handle pattern with no wildcard
+ """
+ search_dir_list = self.get_all_rdirs()
+ for srcdir in self.srcdir_list():
+ search_dir_list.extend(srcdir.get_all_rdirs())
+
+ selfEntry = self.Entry
+ names = []
+ for dir in search_dir_list:
+ # We use the .name attribute from the Node because the keys of
+ # the dir.entries dictionary are normalized (that is, all upper
+ # case) on case-insensitive systems like Windows.
+ #node_names = [ v.name for k, v in dir.entries.items() if k not in ('.', '..') ]
+ entry_names = filter(lambda n: n not in ('.', '..'), dir.entries.keys())
+ node_names = map(lambda n, e=dir.entries: e[n].name, entry_names)
+ names.extend(node_names)
+ if not strings:
+ # Make sure the working directory (self) actually has
+ # entries for all Nodes in repositories or variant dirs.
+ for name in node_names: selfEntry(name)
+ if ondisk:
+ try:
+ disk_names = os.listdir(dir.abspath)
+ except os.error:
+ continue
+ names.extend(disk_names)
+ if not strings:
+ # We're going to return corresponding Nodes in
+ # the local directory, so we need to make sure
+ # those Nodes exist. We only want to create
+ # Nodes for the entries that will match the
+ # specified pattern, though, which means we
+ # need to filter the list here, even though
+ # the overall list will also be filtered later,
+ # after we exit this loop.
+ if pattern[0] != '.':
+ #disk_names = [ d for d in disk_names if d[0] != '.' ]
+ disk_names = filter(lambda x: x[0] != '.', disk_names)
+ disk_names = fnmatch.filter(disk_names, pattern)
+ dirEntry = dir.Entry
+ for name in disk_names:
+ # Add './' before disk filename so that '#' at
+ # beginning of filename isn't interpreted.
+ name = './' + name
+ node = dirEntry(name).disambiguate()
+ n = selfEntry(name)
+ if n.__class__ != node.__class__:
+ n.__class__ = node.__class__
+ n._morph()
+
+ names = set(names)
+ if pattern[0] != '.':
+ #names = [ n for n in names if n[0] != '.' ]
+ names = filter(lambda x: x[0] != '.', names)
+ names = fnmatch.filter(names, pattern)
+
+ if strings:
+ return names
+
+ #return [ self.entries[_my_normcase(n)] for n in names ]
+ return map(lambda n, e=self.entries: e[_my_normcase(n)], names)
+
+class RootDir(Dir):
+ """A class for the root directory of a file system.
+
+ This is the same as a Dir class, except that the path separator
+ ('/' or '\\') is actually part of the name, so we don't need to
+ add a separator when creating the path names of entries within
+ this directory.
+ """
+ def __init__(self, name, fs):
+ if __debug__: logInstanceCreation(self, 'Node.FS.RootDir')
+ # We're going to be our own parent directory (".." entry and .dir
+ # attribute) so we have to set up some values so Base.__init__()
+ # won't gag won't it calls some of our methods.
+ self.abspath = ''
+ self.labspath = ''
+ self.path = ''
+ self.tpath = ''
+ self.path_elements = []
+ self.duplicate = 0
+ self.root = self
+ Base.__init__(self, name, self, fs)
+
+ # Now set our paths to what we really want them to be: the
+ # initial drive letter (the name) plus the directory separator,
+ # except for the "lookup abspath," which does not have the
+ # drive letter.
+ self.abspath = name + os.sep
+ self.labspath = ''
+ self.path = name + os.sep
+ self.tpath = name + os.sep
+ self._morph()
+
+ self._lookupDict = {}
+
+ # The // and os.sep + os.sep entries are necessary because
+ # os.path.normpath() seems to preserve double slashes at the
+ # beginning of a path (presumably for UNC path names), but
+ # collapses triple slashes to a single slash.
+ self._lookupDict[''] = self
+ self._lookupDict['/'] = self
+ self._lookupDict['//'] = self
+ self._lookupDict[os.sep] = self
+ self._lookupDict[os.sep + os.sep] = self
+
+ def must_be_same(self, klass):
+ if klass is Dir:
+ return
+ Base.must_be_same(self, klass)
+
+ def _lookup_abs(self, p, klass, create=1):
+ """
+ Fast (?) lookup of a *normalized* absolute path.
+
+ This method is intended for use by internal lookups with
+ already-normalized path data. For general-purpose lookups,
+ use the FS.Entry(), FS.Dir() or FS.File() methods.
+
+ The caller is responsible for making sure we're passed a
+ normalized absolute path; we merely let Python's dictionary look
+ up and return the One True Node.FS object for the path.
+
+ If no Node for the specified "p" doesn't already exist, and
+ "create" is specified, the Node may be created after recursive
+ invocation to find or create the parent directory or directories.
+ """
+ k = _my_normcase(p)
+ try:
+ result = self._lookupDict[k]
+ except KeyError:
+ if not create:
+ msg = "No such file or directory: '%s' in '%s' (and create is False)" % (p, str(self))
+ raise SCons.Errors.UserError, msg
+ # There is no Node for this path name, and we're allowed
+ # to create it.
+ dir_name, file_name = os.path.split(p)
+ dir_node = self._lookup_abs(dir_name, Dir)
+ result = klass(file_name, dir_node, self.fs)
+
+ # Double-check on disk (as configured) that the Node we
+ # created matches whatever is out there in the real world.
+ result.diskcheck_match()
+
+ self._lookupDict[k] = result
+ dir_node.entries[_my_normcase(file_name)] = result
+ dir_node.implicit = None
+ else:
+ # There is already a Node for this path name. Allow it to
+ # complain if we were looking for an inappropriate type.
+ result.must_be_same(klass)
+ return result
+
+ def __str__(self):
+ return self.abspath
+
+ def entry_abspath(self, name):
+ return self.abspath + name
+
+ def entry_labspath(self, name):
+ return '/' + name
+
+ def entry_path(self, name):
+ return self.path + name
+
+ def entry_tpath(self, name):
+ return self.tpath + name
+
+ def is_under(self, dir):
+ if self is dir:
+ return 1
+ else:
+ return 0
+
+ def up(self):
+ return None
+
+ def get_dir(self):
+ return None
+
+ def src_builder(self):
+ return _null
+
+class FileNodeInfo(SCons.Node.NodeInfoBase):
+ current_version_id = 1
+
+ field_list = ['csig', 'timestamp', 'size']
+
+ # This should get reset by the FS initialization.
+ fs = None
+
+ def str_to_node(self, s):
+ top = self.fs.Top
+ root = top.root
+ if do_splitdrive:
+ drive, s = os.path.splitdrive(s)
+ if drive:
+ root = self.fs.get_root(drive)
+ if not os.path.isabs(s):
+ s = top.labspath + '/' + s
+ return root._lookup_abs(s, Entry)
+
+class FileBuildInfo(SCons.Node.BuildInfoBase):
+ current_version_id = 1
+
+ def convert_to_sconsign(self):
+ """
+ Converts this FileBuildInfo object for writing to a .sconsign file
+
+ This replaces each Node in our various dependency lists with its
+ usual string representation: relative to the top-level SConstruct
+ directory, or an absolute path if it's outside.
+ """
+ if os.sep == '/':
+ node_to_str = str
+ else:
+ def node_to_str(n):
+ try:
+ s = n.path
+ except AttributeError:
+ s = str(n)
+ else:
+ s = string.replace(s, os.sep, '/')
+ return s
+ for attr in ['bsources', 'bdepends', 'bimplicit']:
+ try:
+ val = getattr(self, attr)
+ except AttributeError:
+ pass
+ else:
+ setattr(self, attr, map(node_to_str, val))
+ def convert_from_sconsign(self, dir, name):
+ """
+ Converts a newly-read FileBuildInfo object for in-SCons use
+
+ For normal up-to-date checking, we don't have any conversion to
+ perform--but we're leaving this method here to make that clear.
+ """
+ pass
+ def prepare_dependencies(self):
+ """
+ Prepares a FileBuildInfo object for explaining what changed
+
+ The bsources, bdepends and bimplicit lists have all been
+ stored on disk as paths relative to the top-level SConstruct
+ directory. Convert the strings to actual Nodes (for use by the
+ --debug=explain code and --implicit-cache).
+ """
+ attrs = [
+ ('bsources', 'bsourcesigs'),
+ ('bdepends', 'bdependsigs'),
+ ('bimplicit', 'bimplicitsigs'),
+ ]
+ for (nattr, sattr) in attrs:
+ try:
+ strings = getattr(self, nattr)
+ nodeinfos = getattr(self, sattr)
+ except AttributeError:
+ continue
+ nodes = []
+ for s, ni in izip(strings, nodeinfos):
+ if not isinstance(s, SCons.Node.Node):
+ s = ni.str_to_node(s)
+ nodes.append(s)
+ setattr(self, nattr, nodes)
+ def format(self, names=0):
+ result = []
+ bkids = self.bsources + self.bdepends + self.bimplicit
+ bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs
+ for bkid, bkidsig in izip(bkids, bkidsigs):
+ result.append(str(bkid) + ': ' +
+ string.join(bkidsig.format(names=names), ' '))
+ result.append('%s [%s]' % (self.bactsig, self.bact))
+ return string.join(result, '\n')
+
+class File(Base):
+ """A class for files in a file system.
+ """
+
+ memoizer_counters = []
+
+ NodeInfo = FileNodeInfo
+ BuildInfo = FileBuildInfo
+
+ md5_chunksize = 64
+
+ def diskcheck_match(self):
+ diskcheck_match(self, self.isdir,
+ "Directory %s found where file expected.")
+
+ def __init__(self, name, directory, fs):
+ if __debug__: logInstanceCreation(self, 'Node.FS.File')
+ Base.__init__(self, name, directory, fs)
+ self._morph()
+
+ def Entry(self, name):
+ """Create an entry node named 'name' relative to
+ the directory of this file."""
+ return self.dir.Entry(name)
+
+ def Dir(self, name, create=True):
+ """Create a directory node named 'name' relative to
+ the directory of this file."""
+ return self.dir.Dir(name, create=create)
+
+ def Dirs(self, pathlist):
+ """Create a list of directories relative to the SConscript
+ directory of this file."""
+ # TODO(1.5)
+ # return [self.Dir(p) for p in pathlist]
+ return map(lambda p, s=self: s.Dir(p), pathlist)
+
+ def File(self, name):
+ """Create a file node named 'name' relative to
+ the directory of this file."""
+ return self.dir.File(name)
+
+ #def generate_build_dict(self):
+ # """Return an appropriate dictionary of values for building
+ # this File."""
+ # return {'Dir' : self.Dir,
+ # 'File' : self.File,
+ # 'RDirs' : self.RDirs}
+
+ def _morph(self):
+ """Turn a file system node into a File object."""
+ self.scanner_paths = {}
+ if not hasattr(self, '_local'):
+ self._local = 0
+
+ # If there was already a Builder set on this entry, then
+ # we need to make sure we call the target-decider function,
+ # not the source-decider. Reaching in and doing this by hand
+ # is a little bogus. We'd prefer to handle this by adding
+ # an Entry.builder_set() method that disambiguates like the
+ # other methods, but that starts running into problems with the
+ # fragile way we initialize Dir Nodes with their Mkdir builders,
+ # yet still allow them to be overridden by the user. Since it's
+ # not clear right now how to fix that, stick with what works
+ # until it becomes clear...
+ if self.has_builder():
+ self.changed_since_last_build = self.decide_target
+
+ def scanner_key(self):
+ return self.get_suffix()
+
+ def get_contents(self):
+ if not self.rexists():
+ return ''
+ fname = self.rfile().abspath
+ try:
+ contents = open(fname, "rb").read()
+ except EnvironmentError, e:
+ if not e.filename:
+ e.filename = fname
+ raise
+ return contents
+
+ try:
+ import codecs
+ except ImportError:
+ get_text_contents = get_contents
+ else:
+ # This attempts to figure out what the encoding of the text is
+ # based upon the BOM bytes, and then decodes the contents so that
+ # it's a valid python string.
+ def get_text_contents(self):
+ contents = self.get_contents()
+ # The behavior of various decode() methods and functions
+ # w.r.t. the initial BOM bytes is different for different
+ # encodings and/or Python versions. ('utf-8' does not strip
+ # them, but has a 'utf-8-sig' which does; 'utf-16' seems to
+ # strip them; etc.) Just side step all the complication by
+ # explicitly stripping the BOM before we decode().
+ if contents.startswith(codecs.BOM_UTF8):
+ contents = contents[len(codecs.BOM_UTF8):]
+ # TODO(2.2): Remove when 2.3 becomes floor.
+ #contents = contents.decode('utf-8')
+ contents = my_decode(contents, 'utf-8')
+ elif contents.startswith(codecs.BOM_UTF16_LE):
+ contents = contents[len(codecs.BOM_UTF16_LE):]
+ # TODO(2.2): Remove when 2.3 becomes floor.
+ #contents = contents.decode('utf-16-le')
+ contents = my_decode(contents, 'utf-16-le')
+ elif contents.startswith(codecs.BOM_UTF16_BE):
+ contents = contents[len(codecs.BOM_UTF16_BE):]
+ # TODO(2.2): Remove when 2.3 becomes floor.
+ #contents = contents.decode('utf-16-be')
+ contents = my_decode(contents, 'utf-16-be')
+ return contents
+
+ def get_content_hash(self):
+ """
+ Compute and return the MD5 hash for this file.
+ """
+ if not self.rexists():
+ return SCons.Util.MD5signature('')
+ fname = self.rfile().abspath
+ try:
+ cs = SCons.Util.MD5filesignature(fname,
+ chunksize=SCons.Node.FS.File.md5_chunksize*1024)
+ except EnvironmentError, e:
+ if not e.filename:
+ e.filename = fname
+ raise
+ return cs
+
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_size'))
+
+ def get_size(self):
+ try:
+ return self._memo['get_size']
+ except KeyError:
+ pass
+
+ if self.rexists():
+ size = self.rfile().getsize()
+ else:
+ size = 0
+
+ self._memo['get_size'] = size
+
+ return size
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp'))
+
+ def get_timestamp(self):
+ try:
+ return self._memo['get_timestamp']
+ except KeyError:
+ pass
+
+ if self.rexists():
+ timestamp = self.rfile().getmtime()
+ else:
+ timestamp = 0
+
+ self._memo['get_timestamp'] = timestamp
+
+ return timestamp
+
+ def store_info(self):
+ # Merge our build information into the already-stored entry.
+ # This accomodates "chained builds" where a file that's a target
+ # in one build (SConstruct file) is a source in a different build.
+ # See test/chained-build.py for the use case.
+ if do_store_info:
+ self.dir.sconsign().store_info(self.name, self)
+
+ convert_copy_attrs = [
+ 'bsources',
+ 'bimplicit',
+ 'bdepends',
+ 'bact',
+ 'bactsig',
+ 'ninfo',
+ ]
+
+
+ convert_sig_attrs = [
+ 'bsourcesigs',
+ 'bimplicitsigs',
+ 'bdependsigs',
+ ]
+
+ def convert_old_entry(self, old_entry):
+ # Convert a .sconsign entry from before the Big Signature
+ # Refactoring, doing what we can to convert its information
+ # to the new .sconsign entry format.
+ #
+ # The old format looked essentially like this:
+ #
+ # BuildInfo
+ # .ninfo (NodeInfo)
+ # .bsig
+ # .csig
+ # .timestamp
+ # .size
+ # .bsources
+ # .bsourcesigs ("signature" list)
+ # .bdepends
+ # .bdependsigs ("signature" list)
+ # .bimplicit
+ # .bimplicitsigs ("signature" list)
+ # .bact
+ # .bactsig
+ #
+ # The new format looks like this:
+ #
+ # .ninfo (NodeInfo)
+ # .bsig
+ # .csig
+ # .timestamp
+ # .size
+ # .binfo (BuildInfo)
+ # .bsources
+ # .bsourcesigs (NodeInfo list)
+ # .bsig
+ # .csig
+ # .timestamp
+ # .size
+ # .bdepends
+ # .bdependsigs (NodeInfo list)
+ # .bsig
+ # .csig
+ # .timestamp
+ # .size
+ # .bimplicit
+ # .bimplicitsigs (NodeInfo list)
+ # .bsig
+ # .csig
+ # .timestamp
+ # .size
+ # .bact
+ # .bactsig
+ #
+ # The basic idea of the new structure is that a NodeInfo always
+ # holds all available information about the state of a given Node
+ # at a certain point in time. The various .b*sigs lists can just
+ # be a list of pointers to the .ninfo attributes of the different
+ # dependent nodes, without any copying of information until it's
+ # time to pickle it for writing out to a .sconsign file.
+ #
+ # The complicating issue is that the *old* format only stored one
+ # "signature" per dependency, based on however the *last* build
+ # was configured. We don't know from just looking at it whether
+ # it was a build signature, a content signature, or a timestamp
+ # "signature". Since we no longer use build signatures, the
+ # best we can do is look at the length and if it's thirty two,
+ # assume that it was (or might have been) a content signature.
+ # If it was actually a build signature, then it will cause a
+ # rebuild anyway when it doesn't match the new content signature,
+ # but that's probably the best we can do.
+ import SCons.SConsign
+ new_entry = SCons.SConsign.SConsignEntry()
+ new_entry.binfo = self.new_binfo()
+ binfo = new_entry.binfo
+ for attr in self.convert_copy_attrs:
+ try:
+ value = getattr(old_entry, attr)
+ except AttributeError:
+ continue
+ setattr(binfo, attr, value)
+ delattr(old_entry, attr)
+ for attr in self.convert_sig_attrs:
+ try:
+ sig_list = getattr(old_entry, attr)
+ except AttributeError:
+ continue
+ value = []
+ for sig in sig_list:
+ ninfo = self.new_ninfo()
+ if len(sig) == 32:
+ ninfo.csig = sig
+ else:
+ ninfo.timestamp = sig
+ value.append(ninfo)
+ setattr(binfo, attr, value)
+ delattr(old_entry, attr)
+ return new_entry
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info'))
+
+ def get_stored_info(self):
+ try:
+ return self._memo['get_stored_info']
+ except KeyError:
+ pass
+
+ try:
+ sconsign_entry = self.dir.sconsign().get_entry(self.name)
+ except (KeyError, EnvironmentError):
+ import SCons.SConsign
+ sconsign_entry = SCons.SConsign.SConsignEntry()
+ sconsign_entry.binfo = self.new_binfo()
+ sconsign_entry.ninfo = self.new_ninfo()
+ else:
+ if isinstance(sconsign_entry, FileBuildInfo):
+ # This is a .sconsign file from before the Big Signature
+ # Refactoring; convert it as best we can.
+ sconsign_entry = self.convert_old_entry(sconsign_entry)
+ try:
+ delattr(sconsign_entry.ninfo, 'bsig')
+ except AttributeError:
+ pass
+
+ self._memo['get_stored_info'] = sconsign_entry
+
+ return sconsign_entry
+
+ def get_stored_implicit(self):
+ binfo = self.get_stored_info().binfo
+ binfo.prepare_dependencies()
+ try: return binfo.bimplicit
+ except AttributeError: return None
+
+ def rel_path(self, other):
+ return self.dir.rel_path(other)
+
+ def _get_found_includes_key(self, env, scanner, path):
+ return (id(env), id(scanner), path)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key))
+
+ def get_found_includes(self, env, scanner, path):
+ """Return the included implicit dependencies in this file.
+ Cache results so we only scan the file once per path
+ regardless of how many times this information is requested.
+ """
+ memo_key = (id(env), id(scanner), path)
+ try:
+ memo_dict = self._memo['get_found_includes']
+ except KeyError:
+ memo_dict = {}
+ self._memo['get_found_includes'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+
+ if scanner:
+ # result = [n.disambiguate() for n in scanner(self, env, path)]
+ result = scanner(self, env, path)
+ result = map(lambda N: N.disambiguate(), result)
+ else:
+ result = []
+
+ memo_dict[memo_key] = result
+
+ return result
+
+ def _createDir(self):
+ # ensure that the directories for this node are
+ # created.
+ self.dir._create()
+
+ def push_to_cache(self):
+ """Try to push the node into a cache
+ """
+ # This should get called before the Nodes' .built() method is
+ # called, which would clear the build signature if the file has
+ # a source scanner.
+ #
+ # We have to clear the local memoized values *before* we push
+ # the node to cache so that the memoization of the self.exists()
+ # return value doesn't interfere.
+ if self.nocache:
+ return
+ self.clear_memoized_values()
+ if self.exists():
+ self.get_build_env().get_CacheDir().push(self)
+
+ def retrieve_from_cache(self):
+ """Try to retrieve the node's content from a cache
+
+ This method is called from multiple threads in a parallel build,
+ so only do thread safe stuff here. Do thread unsafe stuff in
+ built().
+
+ Returns true iff the node was successfully retrieved.
+ """
+ if self.nocache:
+ return None
+ if not self.is_derived():
+ return None
+ return self.get_build_env().get_CacheDir().retrieve(self)
+
+ def visited(self):
+ if self.exists():
+ self.get_build_env().get_CacheDir().push_if_forced(self)
+
+ ninfo = self.get_ninfo()
+
+ csig = self.get_max_drift_csig()
+ if csig:
+ ninfo.csig = csig
+
+ ninfo.timestamp = self.get_timestamp()
+ ninfo.size = self.get_size()
+
+ if not self.has_builder():
+ # This is a source file, but it might have been a target file
+ # in another build that included more of the DAG. Copy
+ # any build information that's stored in the .sconsign file
+ # into our binfo object so it doesn't get lost.
+ old = self.get_stored_info()
+ self.get_binfo().__dict__.update(old.binfo.__dict__)
+
+ self.store_info()
+
+ def find_src_builder(self):
+ if self.rexists():
+ return None
+ scb = self.dir.src_builder()
+ if scb is _null:
+ if diskcheck_sccs(self.dir, self.name):
+ scb = get_DefaultSCCSBuilder()
+ elif diskcheck_rcs(self.dir, self.name):
+ scb = get_DefaultRCSBuilder()
+ else:
+ scb = None
+ if scb is not None:
+ try:
+ b = self.builder
+ except AttributeError:
+ b = None
+ if b is None:
+ self.builder_set(scb)
+ return scb
+
+ def has_src_builder(self):
+ """Return whether this Node has a source builder or not.
+
+ If this Node doesn't have an explicit source code builder, this
+ is where we figure out, on the fly, if there's a transparent
+ source code builder for it.
+
+ Note that if we found a source builder, we also set the
+ self.builder attribute, so that all of the methods that actually
+ *build* this file don't have to do anything different.
+ """
+ try:
+ scb = self.sbuilder
+ except AttributeError:
+ scb = self.sbuilder = self.find_src_builder()
+ return scb is not None
+
+ def alter_targets(self):
+ """Return any corresponding targets in a variant directory.
+ """
+ if self.is_derived():
+ return [], None
+ return self.fs.variant_dir_target_climb(self, self.dir, [self.name])
+
+ def _rmv_existing(self):
+ self.clear_memoized_values()
+ e = Unlink(self, [], None)
+ if isinstance(e, SCons.Errors.BuildError):
+ raise e
+
+ #
+ # Taskmaster interface subsystem
+ #
+
+ def make_ready(self):
+ self.has_src_builder()
+ self.get_binfo()
+
+ def prepare(self):
+ """Prepare for this file to be created."""
+ SCons.Node.Node.prepare(self)
+
+ if self.get_state() != SCons.Node.up_to_date:
+ if self.exists():
+ if self.is_derived() and not self.precious:
+ self._rmv_existing()
+ else:
+ try:
+ self._createDir()
+ except SCons.Errors.StopError, drive:
+ desc = "No drive `%s' for target `%s'." % (drive, self)
+ raise SCons.Errors.StopError, desc
+
+ #
+ #
+ #
+
+ def remove(self):
+ """Remove this file."""
+ if self.exists() or self.islink():
+ self.fs.unlink(self.path)
+ return 1
+ return None
+
+ def do_duplicate(self, src):
+ self._createDir()
+ Unlink(self, None, None)
+ e = Link(self, src, None)
+ if isinstance(e, SCons.Errors.BuildError):
+ desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
+ raise SCons.Errors.StopError, desc
+ self.linked = 1
+ # The Link() action may or may not have actually
+ # created the file, depending on whether the -n
+ # option was used or not. Delete the _exists and
+ # _rexists attributes so they can be reevaluated.
+ self.clear()
+
+ memoizer_counters.append(SCons.Memoize.CountValue('exists'))
+
+ def exists(self):
+ try:
+ return self._memo['exists']
+ except KeyError:
+ pass
+ # Duplicate from source path if we are set up to do this.
+ if self.duplicate and not self.is_derived() and not self.linked:
+ src = self.srcnode()
+ if src is not self:
+ # At this point, src is meant to be copied in a variant directory.
+ src = src.rfile()
+ if src.abspath != self.abspath:
+ if src.exists():
+ self.do_duplicate(src)
+ # Can't return 1 here because the duplication might
+ # not actually occur if the -n option is being used.
+ else:
+ # The source file does not exist. Make sure no old
+ # copy remains in the variant directory.
+ if Base.exists(self) or self.islink():
+ self.fs.unlink(self.path)
+ # Return None explicitly because the Base.exists() call
+ # above will have cached its value if the file existed.
+ self._memo['exists'] = None
+ return None
+ result = Base.exists(self)
+ self._memo['exists'] = result
+ return result
+
+ #
+ # SIGNATURE SUBSYSTEM
+ #
+
+ def get_max_drift_csig(self):
+ """
+ Returns the content signature currently stored for this node
+ if it's been unmodified longer than the max_drift value, or the
+ max_drift value is 0. Returns None otherwise.
+ """
+ old = self.get_stored_info()
+ mtime = self.get_timestamp()
+
+ max_drift = self.fs.max_drift
+ if max_drift > 0:
+ if (time.time() - mtime) > max_drift:
+ try:
+ n = old.ninfo
+ if n.timestamp and n.csig and n.timestamp == mtime:
+ return n.csig
+ except AttributeError:
+ pass
+ elif max_drift == 0:
+ try:
+ return old.ninfo.csig
+ except AttributeError:
+ pass
+
+ return None
+
+ def get_csig(self):
+ """
+ Generate a node's content signature, the digested signature
+ of its content.
+
+ node - the node
+ cache - alternate node to use for the signature cache
+ returns - the content signature
+ """
+ ninfo = self.get_ninfo()
+ try:
+ return ninfo.csig
+ except AttributeError:
+ pass
+
+ csig = self.get_max_drift_csig()
+ if csig is None:
+
+ try:
+ if self.get_size() < SCons.Node.FS.File.md5_chunksize:
+ contents = self.get_contents()
+ else:
+ csig = self.get_content_hash()
+ except IOError:
+ # This can happen if there's actually a directory on-disk,
+ # which can be the case if they've disabled disk checks,
+ # or if an action with a File target actually happens to
+ # create a same-named directory by mistake.
+ csig = ''
+ else:
+ if not csig:
+ csig = SCons.Util.MD5signature(contents)
+
+ ninfo.csig = csig
+
+ return csig
+
+ #
+ # DECISION SUBSYSTEM
+ #
+
+ def builder_set(self, builder):
+ SCons.Node.Node.builder_set(self, builder)
+ self.changed_since_last_build = self.decide_target
+
+ def changed_content(self, target, prev_ni):
+ cur_csig = self.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+ def changed_state(self, target, prev_ni):
+ return self.state != SCons.Node.up_to_date
+
+ def changed_timestamp_then_content(self, target, prev_ni):
+ if not self.changed_timestamp_match(target, prev_ni):
+ try:
+ self.get_ninfo().csig = prev_ni.csig
+ except AttributeError:
+ pass
+ return False
+ return self.changed_content(target, prev_ni)
+
+ def changed_timestamp_newer(self, target, prev_ni):
+ try:
+ return self.get_timestamp() > target.get_timestamp()
+ except AttributeError:
+ return 1
+
+ def changed_timestamp_match(self, target, prev_ni):
+ try:
+ return self.get_timestamp() != prev_ni.timestamp
+ except AttributeError:
+ return 1
+
+ def decide_source(self, target, prev_ni):
+ return target.get_build_env().decide_source(self, target, prev_ni)
+
+ def decide_target(self, target, prev_ni):
+ return target.get_build_env().decide_target(self, target, prev_ni)
+
+ # Initialize this Node's decider function to decide_source() because
+ # every file is a source file until it has a Builder attached...
+ changed_since_last_build = decide_source
+
+ def is_up_to_date(self):
+ T = 0
+ if T: Trace('is_up_to_date(%s):' % self)
+ if not self.exists():
+ if T: Trace(' not self.exists():')
+ # The file doesn't exist locally...
+ r = self.rfile()
+ if r != self:
+ # ...but there is one in a Repository...
+ if not self.changed(r):
+ if T: Trace(' changed(%s):' % r)
+ # ...and it's even up-to-date...
+ if self._local:
+ # ...and they'd like a local copy.
+ e = LocalCopy(self, r, None)
+ if isinstance(e, SCons.Errors.BuildError):
+ raise
+ self.store_info()
+ if T: Trace(' 1\n')
+ return 1
+ self.changed()
+ if T: Trace(' None\n')
+ return None
+ else:
+ r = self.changed()
+ if T: Trace(' self.exists(): %s\n' % r)
+ return not r
+
+ memoizer_counters.append(SCons.Memoize.CountValue('rfile'))
+
+ def rfile(self):
+ try:
+ return self._memo['rfile']
+ except KeyError:
+ pass
+ result = self
+ if not self.exists():
+ norm_name = _my_normcase(self.name)
+ for dir in self.dir.get_all_rdirs():
+ try: node = dir.entries[norm_name]
+ except KeyError: node = dir.file_on_disk(self.name)
+ if node and node.exists() and \
+ (isinstance(node, File) or isinstance(node, Entry) \
+ or not node.is_derived()):
+ result = node
+ # Copy over our local attributes to the repository
+ # Node so we identify shared object files in the
+ # repository and don't assume they're static.
+ #
+ # This isn't perfect; the attribute would ideally
+ # be attached to the object in the repository in
+ # case it was built statically in the repository
+ # and we changed it to shared locally, but that's
+ # rarely the case and would only occur if you
+ # intentionally used the same suffix for both
+ # shared and static objects anyway. So this
+ # should work well in practice.
+ result.attributes = self.attributes
+ break
+ self._memo['rfile'] = result
+ return result
+
+ def rstr(self):
+ return str(self.rfile())
+
+ def get_cachedir_csig(self):
+ """
+ Fetch a Node's content signature for purposes of computing
+ another Node's cachesig.
+
+ This is a wrapper around the normal get_csig() method that handles
+ the somewhat obscure case of using CacheDir with the -n option.
+ Any files that don't exist would normally be "built" by fetching
+ them from the cache, but the normal get_csig() method will try
+ to open up the local file, which doesn't exist because the -n
+ option meant we didn't actually pull the file from cachedir.
+ But since the file *does* actually exist in the cachedir, we
+ can use its contents for the csig.
+ """
+ try:
+ return self.cachedir_csig
+ except AttributeError:
+ pass
+
+ cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self)
+ if not self.exists() and cachefile and os.path.exists(cachefile):
+ self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \
+ SCons.Node.FS.File.md5_chunksize * 1024)
+ else:
+ self.cachedir_csig = self.get_csig()
+ return self.cachedir_csig
+
+ def get_cachedir_bsig(self):
+ try:
+ return self.cachesig
+ except AttributeError:
+ pass
+
+ # Add the path to the cache signature, because multiple
+ # targets built by the same action will all have the same
+ # build signature, and we have to differentiate them somehow.
+ children = self.children()
+ executor = self.get_executor()
+ # sigs = [n.get_cachedir_csig() for n in children]
+ sigs = map(lambda n: n.get_cachedir_csig(), children)
+ sigs.append(SCons.Util.MD5signature(executor.get_contents()))
+ sigs.append(self.path)
+ result = self.cachesig = SCons.Util.MD5collect(sigs)
+ return result
+
+
+default_fs = None
+
+def get_default_fs():
+ global default_fs
+ if not default_fs:
+ default_fs = FS()
+ return default_fs
+
+class FileFinder:
+ """
+ """
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ def __init__(self):
+ self._memo = {}
+
+ def filedir_lookup(self, p, fd=None):
+ """
+ A helper method for find_file() that looks up a directory for
+ a file we're trying to find. This only creates the Dir Node if
+ it exists on-disk, since if the directory doesn't exist we know
+ we won't find any files in it... :-)
+
+ It would be more compact to just use this as a nested function
+ with a default keyword argument (see the commented-out version
+ below), but that doesn't work unless you have nested scopes,
+ so we define it here just so this work under Python 1.5.2.
+ """
+ if fd is None:
+ fd = self.default_filedir
+ dir, name = os.path.split(fd)
+ drive, d = os.path.splitdrive(dir)
+ if not name and d[:1] in ('/', os.sep):
+ #return p.fs.get_root(drive).dir_on_disk(name)
+ return p.fs.get_root(drive)
+ if dir:
+ p = self.filedir_lookup(p, dir)
+ if not p:
+ return None
+ norm_name = _my_normcase(name)
+ try:
+ node = p.entries[norm_name]
+ except KeyError:
+ return p.dir_on_disk(name)
+ if isinstance(node, Dir):
+ return node
+ if isinstance(node, Entry):
+ node.must_be_same(Dir)
+ return node
+ return None
+
+ def _find_file_key(self, filename, paths, verbose=None):
+ return (filename, paths)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key))
+
+ def find_file(self, filename, paths, verbose=None):
+ """
+ find_file(str, [Dir()]) -> [nodes]
+
+ filename - a filename to find
+ paths - a list of directory path *nodes* to search in. Can be
+ represented as a list, a tuple, or a callable that is
+ called with no arguments and returns the list or tuple.
+
+ returns - the node created from the found file.
+
+ Find a node corresponding to either a derived file or a file
+ that exists already.
+
+ Only the first file found is returned, and none is returned
+ if no file is found.
+ """
+ memo_key = self._find_file_key(filename, paths)
+ try:
+ memo_dict = self._memo['find_file']
+ except KeyError:
+ memo_dict = {}
+ self._memo['find_file'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+
+ if verbose and not callable(verbose):
+ if not SCons.Util.is_String(verbose):
+ verbose = "find_file"
+ verbose = ' %s: ' % verbose
+ verbose = lambda s, v=verbose: sys.stdout.write(v + s)
+
+ filedir, filename = os.path.split(filename)
+ if filedir:
+ # More compact code that we can't use until we drop
+ # support for Python 1.5.2:
+ #
+ #def filedir_lookup(p, fd=filedir):
+ # """
+ # A helper function that looks up a directory for a file
+ # we're trying to find. This only creates the Dir Node
+ # if it exists on-disk, since if the directory doesn't
+ # exist we know we won't find any files in it... :-)
+ # """
+ # dir, name = os.path.split(fd)
+ # if dir:
+ # p = filedir_lookup(p, dir)
+ # if not p:
+ # return None
+ # norm_name = _my_normcase(name)
+ # try:
+ # node = p.entries[norm_name]
+ # except KeyError:
+ # return p.dir_on_disk(name)
+ # if isinstance(node, Dir):
+ # return node
+ # if isinstance(node, Entry):
+ # node.must_be_same(Dir)
+ # return node
+ # if isinstance(node, Dir) or isinstance(node, Entry):
+ # return node
+ # return None
+ #paths = filter(None, map(filedir_lookup, paths))
+
+ self.default_filedir = filedir
+ paths = filter(None, map(self.filedir_lookup, paths))
+
+ result = None
+ for dir in paths:
+ if verbose:
+ verbose("looking for '%s' in '%s' ...\n" % (filename, dir))
+ node, d = dir.srcdir_find_file(filename)
+ if node:
+ if verbose:
+ verbose("... FOUND '%s' in '%s'\n" % (filename, d))
+ result = node
+ break
+
+ memo_dict[memo_key] = result
+
+ return result
+
+find_file = FileFinder().find_file
+
+
+def invalidate_node_memos(targets):
+ """
+ Invalidate the memoized values of all Nodes (files or directories)
+ that are associated with the given entries. Has been added to
+ clear the cache of nodes affected by a direct execution of an
+ action (e.g. Delete/Copy/Chmod). Existing Node caches become
+ inconsistent if the action is run through Execute(). The argument
+ `targets` can be a single Node object or filename, or a sequence
+ of Nodes/filenames.
+ """
+ from traceback import extract_stack
+
+ # First check if the cache really needs to be flushed. Only
+ # actions run in the SConscript with Execute() seem to be
+ # affected. XXX The way to check if Execute() is in the stacktrace
+ # is a very dirty hack and should be replaced by a more sensible
+ # solution.
+ for f in extract_stack():
+ if f[2] == 'Execute' and f[0][-14:] == 'Environment.py':
+ break
+ else:
+ # Dont have to invalidate, so return
+ return
+
+ if not SCons.Util.is_List(targets):
+ targets = [targets]
+
+ for entry in targets:
+ # If the target is a Node object, clear the cache. If it is a
+ # filename, look up potentially existing Node object first.
+ try:
+ entry.clear_memoized_values()
+ except AttributeError:
+ # Not a Node object, try to look up Node by filename. XXX
+ # This creates Node objects even for those filenames which
+ # do not correspond to an existing Node object.
+ node = get_default_fs().Entry(entry)
+ if node:
+ node.clear_memoized_values()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
new file mode 100644
index 0000000..bf64ab7
--- /dev/null
+++ b/src/engine/SCons/Node/FSTests.py
@@ -0,0 +1,3520 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/FSTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+import sys
+import time
+import unittest
+from TestCmd import TestCmd
+import shutil
+import stat
+
+import SCons.Errors
+import SCons.Node.FS
+import SCons.Util
+import SCons.Warnings
+
+built_it = None
+
+# This will be built-in in 2.3. For now fake it.
+try :
+ True , False
+except NameError :
+ True = 1 ; False = 0
+
+
+scanner_count = 0
+
+class Scanner:
+ def __init__(self, node=None):
+ global scanner_count
+ scanner_count = scanner_count + 1
+ self.hash = scanner_count
+ self.node = node
+ def path(self, env, dir, target=None, source=None):
+ return ()
+ def __call__(self, node, env, path):
+ return [self.node]
+ def __hash__(self):
+ return self.hash
+ def select(self, node):
+ return self
+ def recurse_nodes(self, nodes):
+ return nodes
+
+class Environment:
+ def __init__(self):
+ self.scanner = Scanner()
+ def Dictionary(self, *args):
+ return {}
+ def autogenerate(self, **kw):
+ return {}
+ def get_scanner(self, skey):
+ return self.scanner
+ def Override(self, overrides):
+ return self
+ def _update(self, dict):
+ pass
+
+class Action:
+ def __call__(self, targets, sources, env, **kw):
+ global built_it
+ if kw.get('execute', 1):
+ built_it = 1
+ return 0
+ def show(self, string):
+ pass
+ def get_contents(self, target, source, env):
+ return ""
+ def genstring(self, target, source, env):
+ return ""
+ def strfunction(self, targets, sources, env):
+ return ""
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class Builder:
+ def __init__(self, factory, action=Action()):
+ self.factory = factory
+ self.env = Environment()
+ self.overrides = {}
+ self.action = action
+ self.target_scanner = None
+ self.source_scanner = None
+
+ def targets(self, t):
+ return [t]
+
+ def source_factory(self, name):
+ return self.factory(name)
+
+class _tempdirTestCase(unittest.TestCase):
+ def setUp(self):
+ self.save_cwd = os.getcwd()
+ self.test = TestCmd(workdir='')
+ # FS doesn't like the cwd to be something other than its root.
+ os.chdir(self.test.workpath(""))
+ self.fs = SCons.Node.FS.FS()
+
+ def tearDown(self):
+ os.chdir(self.save_cwd)
+
+class VariantDirTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test variant dir functionality"""
+ test=TestCmd(workdir='')
+
+ fs = SCons.Node.FS.FS()
+ f1 = fs.File('build/test1')
+ fs.VariantDir('build', 'src')
+ f2 = fs.File('build/test2')
+ d1 = fs.Dir('build')
+ assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
+ assert f2.srcnode().path == os.path.normpath('src/test2'), f2.srcnode().path
+ assert d1.srcnode().path == 'src', d1.srcnode().path
+
+ fs = SCons.Node.FS.FS()
+ f1 = fs.File('build/test1')
+ fs.VariantDir('build', '.')
+ f2 = fs.File('build/test2')
+ d1 = fs.Dir('build')
+ assert f1.srcnode().path == 'test1', f1.srcnode().path
+ assert f2.srcnode().path == 'test2', f2.srcnode().path
+ assert d1.srcnode().path == '.', d1.srcnode().path
+
+ fs = SCons.Node.FS.FS()
+ fs.VariantDir('build/var1', 'src')
+ fs.VariantDir('build/var2', 'src')
+ f1 = fs.File('build/var1/test1')
+ f2 = fs.File('build/var2/test1')
+ assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
+ assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
+
+ fs = SCons.Node.FS.FS()
+ fs.VariantDir('../var1', 'src')
+ fs.VariantDir('../var2', 'src')
+ f1 = fs.File('../var1/test1')
+ f2 = fs.File('../var2/test1')
+ assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
+ assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
+
+ # Set up some files
+ test.subdir('work', ['work', 'src'])
+ test.subdir(['work', 'build'], ['work', 'build', 'var1'])
+ test.subdir(['work', 'build', 'var2'])
+ test.subdir('rep1', ['rep1', 'src'])
+ test.subdir(['rep1', 'build'], ['rep1', 'build', 'var1'])
+ test.subdir(['rep1', 'build', 'var2'])
+
+ # A source file in the source directory
+ test.write([ 'work', 'src', 'test.in' ], 'test.in')
+
+ # A source file in a subdir of the source directory
+ test.subdir([ 'work', 'src', 'new_dir' ])
+ test.write([ 'work', 'src', 'new_dir', 'test9.out' ], 'test9.out\n')
+
+ # A source file in the repository
+ test.write([ 'rep1', 'src', 'test2.in' ], 'test2.in')
+
+ # Some source files in the variant directory
+ test.write([ 'work', 'build', 'var2', 'test.in' ], 'test.old')
+ test.write([ 'work', 'build', 'var2', 'test2.in' ], 'test2.old')
+
+ # An old derived file in the variant directories
+ test.write([ 'work', 'build', 'var1', 'test.out' ], 'test.old')
+ test.write([ 'work', 'build', 'var2', 'test.out' ], 'test.old')
+
+ # And just in case we are weird, a derived file in the source
+ # dir.
+ test.write([ 'work', 'src', 'test.out' ], 'test.out.src')
+
+ # A derived file in the repository
+ test.write([ 'rep1', 'build', 'var1', 'test2.out' ], 'test2.out_rep')
+ test.write([ 'rep1', 'build', 'var2', 'test2.out' ], 'test2.out_rep')
+
+ os.chdir(test.workpath('work'))
+
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.VariantDir('build/var1', 'src', duplicate=0)
+ fs.VariantDir('build/var2', 'src')
+ f1 = fs.File('build/var1/test.in')
+ f1out = fs.File('build/var1/test.out')
+ f1out.builder = 1
+ f1out_2 = fs.File('build/var1/test2.out')
+ f1out_2.builder = 1
+ f2 = fs.File('build/var2/test.in')
+ f2out = fs.File('build/var2/test.out')
+ f2out.builder = 1
+ f2out_2 = fs.File('build/var2/test2.out')
+ f2out_2.builder = 1
+ fs.Repository(test.workpath('rep1'))
+
+ assert f1.srcnode().path == os.path.normpath('src/test.in'),\
+ f1.srcnode().path
+ # str(node) returns source path for duplicate = 0
+ assert str(f1) == os.path.normpath('src/test.in'), str(f1)
+ # Build path does not exist
+ assert not f1.exists()
+ # ...but the actual file is not there...
+ assert not os.path.exists(f1.get_abspath())
+ # And duplicate=0 should also work just like a Repository
+ assert f1.rexists()
+ # rfile() should point to the source path
+ assert f1.rfile().path == os.path.normpath('src/test.in'),\
+ f1.rfile().path
+
+ assert f2.srcnode().path == os.path.normpath('src/test.in'),\
+ f2.srcnode().path
+ # str(node) returns build path for duplicate = 1
+ assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2)
+ # Build path exists
+ assert f2.exists()
+ # ...and exists() should copy the file from src to build path
+ assert test.read(['work', 'build', 'var2', 'test.in']) == 'test.in',\
+ test.read(['work', 'build', 'var2', 'test.in'])
+ # Since exists() is true, so should rexists() be
+ assert f2.rexists()
+
+ f3 = fs.File('build/var1/test2.in')
+ f4 = fs.File('build/var2/test2.in')
+
+ assert f3.srcnode().path == os.path.normpath('src/test2.in'),\
+ f3.srcnode().path
+ # str(node) returns source path for duplicate = 0
+ assert str(f3) == os.path.normpath('src/test2.in'), str(f3)
+ # Build path does not exist
+ assert not f3.exists()
+ # Source path does not either
+ assert not f3.srcnode().exists()
+ # But we do have a file in the Repository
+ assert f3.rexists()
+ # rfile() should point to the source path
+ assert f3.rfile().path == os.path.normpath(test.workpath('rep1/src/test2.in')),\
+ f3.rfile().path
+
+ assert f4.srcnode().path == os.path.normpath('src/test2.in'),\
+ f4.srcnode().path
+ # str(node) returns build path for duplicate = 1
+ assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4)
+ # Build path should exist
+ assert f4.exists()
+ # ...and copy over the file into the local build path
+ assert test.read(['work', 'build', 'var2', 'test2.in']) == 'test2.in'
+ # should exist in repository, since exists() is true
+ assert f4.rexists()
+ # rfile() should point to ourselves
+ assert f4.rfile().path == os.path.normpath('build/var2/test2.in'),\
+ f4.rfile().path
+
+ f5 = fs.File('build/var1/test.out')
+ f6 = fs.File('build/var2/test.out')
+
+ assert f5.exists()
+ # We should not copy the file from the source dir, since this is
+ # a derived file.
+ assert test.read(['work', 'build', 'var1', 'test.out']) == 'test.old'
+
+ assert f6.exists()
+ # We should not copy the file from the source dir, since this is
+ # a derived file.
+ assert test.read(['work', 'build', 'var2', 'test.out']) == 'test.old'
+
+ f7 = fs.File('build/var1/test2.out')
+ f8 = fs.File('build/var2/test2.out')
+
+ assert not f7.exists()
+ assert f7.rexists()
+ r = f7.rfile().path
+ expect = os.path.normpath(test.workpath('rep1/build/var1/test2.out'))
+ assert r == expect, (repr(r), repr(expect))
+
+ assert not f8.exists()
+ assert f8.rexists()
+ assert f8.rfile().path == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\
+ f8.rfile().path
+
+ # Verify the Mkdir and Link actions are called
+ d9 = fs.Dir('build/var2/new_dir')
+ f9 = fs.File('build/var2/new_dir/test9.out')
+
+ class MkdirAction(Action):
+ def __init__(self, dir_made):
+ self.dir_made = dir_made
+ def __call__(self, target, source, env, executor=None):
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ self.dir_made.extend(target)
+
+ save_Link = SCons.Node.FS.Link
+ link_made = []
+ def link_func(target, source, env, link_made=link_made):
+ link_made.append(target)
+ SCons.Node.FS.Link = link_func
+
+ try:
+ dir_made = []
+ d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
+ d9.reset_executor()
+ f9.exists()
+ expect = os.path.join('build', 'var2', 'new_dir')
+ assert dir_made[0].path == expect, dir_made[0].path
+ expect = os.path.join('build', 'var2', 'new_dir', 'test9.out')
+ assert link_made[0].path == expect, link_made[0].path
+ assert f9.linked
+ finally:
+ SCons.Node.FS.Link = save_Link
+
+ # Test for an interesting pathological case...we have a source
+ # file in a build path, but not in a source path. This can
+ # happen if you switch from duplicate=1 to duplicate=0, then
+ # delete a source file. At one time, this would cause exists()
+ # to return a 1 but get_contents() to throw.
+ test.write([ 'work', 'build', 'var1', 'asourcefile' ], 'stuff')
+ f10 = fs.File('build/var1/asourcefile')
+ assert f10.exists()
+ assert f10.get_contents() == 'stuff', f10.get_contents()
+
+ f11 = fs.File('src/file11')
+ t, m = f11.alter_targets()
+ bdt = map(lambda n: n.path, t)
+ var1_file11 = os.path.normpath('build/var1/file11')
+ var2_file11 = os.path.normpath('build/var2/file11')
+ assert bdt == [var1_file11, var2_file11], bdt
+
+ f12 = fs.File('src/file12')
+ f12.builder = 1
+ bdt, m = f12.alter_targets()
+ assert bdt == [], map(lambda n: n.path, bdt)
+
+ d13 = fs.Dir('src/new_dir')
+ t, m = d13.alter_targets()
+ bdt = map(lambda n: n.path, t)
+ var1_new_dir = os.path.normpath('build/var1/new_dir')
+ var2_new_dir = os.path.normpath('build/var2/new_dir')
+ assert bdt == [var1_new_dir, var2_new_dir], bdt
+
+ # Test that an IOError trying to Link a src file
+ # into a VariantDir ends up throwing a StopError.
+ fIO = fs.File("build/var2/IOError")
+
+ save_Link = SCons.Node.FS.Link
+ def Link_IOError(target, source, env):
+ raise IOError, (17, "Link_IOError")
+ SCons.Node.FS.Link = SCons.Action.Action(Link_IOError, None)
+
+ test.write(['work', 'src', 'IOError'], "work/src/IOError\n")
+
+ try:
+ exc_caught = 0
+ try:
+ fIO.exists()
+ except SCons.Errors.StopError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a StopError"
+
+ finally:
+ SCons.Node.FS.Link = save_Link
+
+ # Test to see if Link() works...
+ test.subdir('src','build')
+ test.write('src/foo', 'src/foo\n')
+ os.chmod(test.workpath('src/foo'), stat.S_IRUSR)
+ SCons.Node.FS.Link(fs.File(test.workpath('build/foo')),
+ fs.File(test.workpath('src/foo')),
+ None)
+ os.chmod(test.workpath('src/foo'), stat.S_IRUSR | stat.S_IWRITE)
+ st=os.stat(test.workpath('build/foo'))
+ assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \
+ stat.S_IMODE(st[stat.ST_MODE])
+
+ # This used to generate a UserError when we forbid the source
+ # directory from being outside the top-level SConstruct dir.
+ fs = SCons.Node.FS.FS()
+ fs.VariantDir('build', '/test/foo')
+
+ exc_caught = 0
+ try:
+ try:
+ fs = SCons.Node.FS.FS()
+ fs.VariantDir('build', 'build/src')
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a UserError."
+ finally:
+ test.unlink( "src/foo" )
+ test.unlink( "build/foo" )
+
+ fs = SCons.Node.FS.FS()
+ fs.VariantDir('build', 'src1')
+
+ # Calling the same VariantDir twice should work fine.
+ fs.VariantDir('build', 'src1')
+
+ # Trying to move a variant dir to a second source dir
+ # should blow up
+ try:
+ fs.VariantDir('build', 'src2')
+ except SCons.Errors.UserError:
+ pass
+ else:
+ assert 0, "Should have caught a UserError."
+
+ # Test against a former bug. Make sure we can get a repository
+ # path for the variant directory itself!
+ fs=SCons.Node.FS.FS(test.workpath('work'))
+ test.subdir('work')
+ fs.VariantDir('build/var3', 'src', duplicate=0)
+ d1 = fs.Dir('build/var3')
+ r = d1.rdir()
+ assert r == d1, "%s != %s" % (r, d1)
+
+ # verify the link creation attempts in file_link()
+ class LinkSimulator :
+ """A class to intercept os.[sym]link() calls and track them."""
+
+ def __init__( self, duplicate, link, symlink, copy ) :
+ self.duplicate = duplicate
+ self.have = {}
+ self.have['hard'] = link
+ self.have['soft'] = symlink
+ self.have['copy'] = copy
+
+ self.links_to_be_called = []
+ for link in string.split(self.duplicate, '-'):
+ if self.have[link]:
+ self.links_to_be_called.append(link)
+
+ def link_fail( self , src , dest ) :
+ next_link = self.links_to_be_called.pop(0)
+ assert next_link == "hard", \
+ "Wrong link order: expected %s to be called "\
+ "instead of hard" % next_link
+ raise OSError( "Simulating hard link creation error." )
+
+ def symlink_fail( self , src , dest ) :
+ next_link = self.links_to_be_called.pop(0)
+ assert next_link == "soft", \
+ "Wrong link order: expected %s to be called "\
+ "instead of soft" % next_link
+ raise OSError( "Simulating symlink creation error." )
+
+ def copy( self , src , dest ) :
+ next_link = self.links_to_be_called.pop(0)
+ assert next_link == "copy", \
+ "Wrong link order: expected %s to be called "\
+ "instead of copy" % next_link
+ # copy succeeds, but use the real copy
+ self.have['copy'](src, dest)
+ # end class LinkSimulator
+
+ try:
+ SCons.Node.FS.set_duplicate("no-link-order")
+ assert 0, "Expected exception when passing an invalid duplicate to set_duplicate"
+ except SCons.Errors.InternalError:
+ pass
+
+ for duplicate in SCons.Node.FS.Valid_Duplicates:
+ # save the real functions for later restoration
+ try:
+ real_link = os.link
+ except AttributeError:
+ real_link = None
+ try:
+ real_symlink = os.symlink
+ except AttributeError:
+ real_symlink = None
+ real_copy = shutil.copy2
+
+ simulator = LinkSimulator(duplicate, real_link, real_symlink, real_copy)
+
+ # override the real functions with our simulation
+ os.link = simulator.link_fail
+ os.symlink = simulator.symlink_fail
+ shutil.copy2 = simulator.copy
+
+ try:
+
+ SCons.Node.FS.set_duplicate(duplicate)
+
+ src_foo = test.workpath('src', 'foo')
+ build_foo = test.workpath('build', 'foo')
+
+ test.write(src_foo, 'src/foo\n')
+ os.chmod(src_foo, stat.S_IRUSR)
+ try:
+ SCons.Node.FS.Link(fs.File(build_foo),
+ fs.File(src_foo),
+ None)
+ finally:
+ os.chmod(src_foo, stat.S_IRUSR | stat.S_IWRITE)
+ test.unlink(src_foo)
+ test.unlink(build_foo)
+
+ finally:
+ # restore the real functions
+ if real_link:
+ os.link = real_link
+ else:
+ delattr(os, 'link')
+ if real_symlink:
+ os.symlink = real_symlink
+ else:
+ delattr(os, 'symlink')
+ shutil.copy2 = real_copy
+
+ # Test VariantDir "reflection," where a same-named subdirectory
+ # exists underneath a variant_dir.
+ fs = SCons.Node.FS.FS()
+ fs.VariantDir('work/src/b1/b2', 'work/src')
+
+ dir_list = [
+ 'work/src',
+ 'work/src/b1',
+ 'work/src/b1/b2',
+ 'work/src/b1/b2/b1',
+ 'work/src/b1/b2/b1/b2',
+ 'work/src/b1/b2/b1/b2/b1',
+ 'work/src/b1/b2/b1/b2/b1/b2',
+ ]
+
+ srcnode_map = {
+ 'work/src/b1/b2' : 'work/src',
+ 'work/src/b1/b2/f' : 'work/src/f',
+ 'work/src/b1/b2/b1' : 'work/src/b1/',
+ 'work/src/b1/b2/b1/f' : 'work/src/b1/f',
+ 'work/src/b1/b2/b1/b2' : 'work/src/b1/b2',
+ 'work/src/b1/b2/b1/b2/f' : 'work/src/b1/b2/f',
+ 'work/src/b1/b2/b1/b2/b1' : 'work/src/b1/b2/b1',
+ 'work/src/b1/b2/b1/b2/b1/f' : 'work/src/b1/b2/b1/f',
+ 'work/src/b1/b2/b1/b2/b1/b2' : 'work/src/b1/b2/b1/b2',
+ 'work/src/b1/b2/b1/b2/b1/b2/f' : 'work/src/b1/b2/b1/b2/f',
+ }
+
+ alter_map = {
+ 'work/src' : 'work/src/b1/b2',
+ 'work/src/f' : 'work/src/b1/b2/f',
+ 'work/src/b1' : 'work/src/b1/b2/b1',
+ 'work/src/b1/f' : 'work/src/b1/b2/b1/f',
+ }
+
+ errors = 0
+
+ for dir in dir_list:
+ dnode = fs.Dir(dir)
+ f = dir + '/f'
+ fnode = fs.File(dir + '/f')
+
+ dp = dnode.srcnode().path
+ expect = os.path.normpath(srcnode_map.get(dir, dir))
+ if dp != expect:
+ print "Dir `%s' srcnode() `%s' != expected `%s'" % (dir, dp, expect)
+ errors = errors + 1
+
+ fp = fnode.srcnode().path
+ expect = os.path.normpath(srcnode_map.get(f, f))
+ if fp != expect:
+ print "File `%s' srcnode() `%s' != expected `%s'" % (f, fp, expect)
+ errors = errors + 1
+
+ for dir in dir_list:
+ dnode = fs.Dir(dir)
+ f = dir + '/f'
+ fnode = fs.File(dir + '/f')
+
+ t, m = dnode.alter_targets()
+ tp = t[0].path
+ expect = os.path.normpath(alter_map.get(dir, dir))
+ if tp != expect:
+ print "Dir `%s' alter_targets() `%s' != expected `%s'" % (dir, tp, expect)
+ errors = errors + 1
+
+ t, m = fnode.alter_targets()
+ tp = t[0].path
+ expect = os.path.normpath(alter_map.get(f, f))
+ if tp != expect:
+ print "File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect)
+ errors = errors + 1
+
+ self.failIf(errors)
+
+class BaseTestCase(_tempdirTestCase):
+ def test_stat(self):
+ """Test the Base.stat() method"""
+ test = self.test
+ test.write("e1", "e1\n")
+ fs = SCons.Node.FS.FS()
+
+ e1 = fs.Entry('e1')
+ s = e1.stat()
+ assert s is not None, s
+
+ e2 = fs.Entry('e2')
+ s = e2.stat()
+ assert s is None, s
+
+ def test_getmtime(self):
+ """Test the Base.getmtime() method"""
+ test = self.test
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ file = fs.Entry('file')
+ assert file.getmtime()
+
+ file = fs.Entry('nonexistent')
+ mtime = file.getmtime()
+ assert mtime is None, mtime
+
+ def test_getsize(self):
+ """Test the Base.getsize() method"""
+ test = self.test
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ file = fs.Entry('file')
+ size = file.getsize()
+ assert size == 5, size
+
+ file = fs.Entry('nonexistent')
+ size = file.getsize()
+ assert size is None, size
+
+ def test_isdir(self):
+ """Test the Base.isdir() method"""
+ test = self.test
+ test.subdir('dir')
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ dir = fs.Entry('dir')
+ assert dir.isdir()
+
+ file = fs.Entry('file')
+ assert not file.isdir()
+
+ nonexistent = fs.Entry('nonexistent')
+ assert not nonexistent.isdir()
+
+ def test_isfile(self):
+ """Test the Base.isfile() method"""
+ test = self.test
+ test.subdir('dir')
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ dir = fs.Entry('dir')
+ assert not dir.isfile()
+
+ file = fs.Entry('file')
+ assert file.isfile()
+
+ nonexistent = fs.Entry('nonexistent')
+ assert not nonexistent.isfile()
+
+ if hasattr(os, 'symlink'):
+ def test_islink(self):
+ """Test the Base.islink() method"""
+ test = self.test
+ test.subdir('dir')
+ test.write("file", "file\n")
+ test.symlink("symlink", "symlink")
+ fs = SCons.Node.FS.FS()
+
+ dir = fs.Entry('dir')
+ assert not dir.islink()
+
+ file = fs.Entry('file')
+ assert not file.islink()
+
+ symlink = fs.Entry('symlink')
+ assert symlink.islink()
+
+ nonexistent = fs.Entry('nonexistent')
+ assert not nonexistent.islink()
+
+class DirNodeInfoTestCase(_tempdirTestCase):
+ def test___init__(self):
+ """Test DirNodeInfo initialization"""
+ ddd = self.fs.Dir('ddd')
+ ni = SCons.Node.FS.DirNodeInfo(ddd)
+
+class DirBuildInfoTestCase(_tempdirTestCase):
+ def test___init__(self):
+ """Test DirBuildInfo initialization"""
+ ddd = self.fs.Dir('ddd')
+ bi = SCons.Node.FS.DirBuildInfo(ddd)
+
+class FileNodeInfoTestCase(_tempdirTestCase):
+ def test___init__(self):
+ """Test FileNodeInfo initialization"""
+ fff = self.fs.File('fff')
+ ni = SCons.Node.FS.FileNodeInfo(fff)
+ assert isinstance(ni, SCons.Node.FS.FileNodeInfo)
+
+ def test_update(self):
+ """Test updating a File.NodeInfo with on-disk information"""
+ test = self.test
+ fff = self.fs.File('fff')
+
+ ni = SCons.Node.FS.FileNodeInfo(fff)
+
+ test.write('fff', "fff\n")
+
+ st = os.stat('fff')
+
+ ni.update(fff)
+
+ assert hasattr(ni, 'timestamp')
+ assert hasattr(ni, 'size')
+
+ ni.timestamp = 0
+ ni.size = 0
+
+ ni.update(fff)
+
+ mtime = st[stat.ST_MTIME]
+ assert ni.timestamp == mtime, (ni.timestamp, mtime)
+ size = st[stat.ST_SIZE]
+ assert ni.size == size, (ni.size, size)
+
+ import time
+ time.sleep(2)
+
+ test.write('fff', "fff longer size, different time stamp\n")
+
+ st = os.stat('fff')
+
+ mtime = st[stat.ST_MTIME]
+ assert ni.timestamp != mtime, (ni.timestamp, mtime)
+ size = st[stat.ST_SIZE]
+ assert ni.size != size, (ni.size, size)
+
+ #fff.clear()
+ #ni.update(fff)
+
+ #st = os.stat('fff')
+
+ #mtime = st[stat.ST_MTIME]
+ #assert ni.timestamp == mtime, (ni.timestamp, mtime)
+ #size = st[stat.ST_SIZE]
+ #assert ni.size == size, (ni.size, size)
+
+class FileBuildInfoTestCase(_tempdirTestCase):
+ def test___init__(self):
+ """Test File.BuildInfo initialization"""
+ fff = self.fs.File('fff')
+ bi = SCons.Node.FS.FileBuildInfo(fff)
+ assert bi, bi
+
+ def test_convert_to_sconsign(self):
+ """Test converting to .sconsign file format"""
+ fff = self.fs.File('fff')
+ bi = SCons.Node.FS.FileBuildInfo(fff)
+ assert hasattr(bi, 'convert_to_sconsign')
+
+ def test_convert_from_sconsign(self):
+ """Test converting from .sconsign file format"""
+ fff = self.fs.File('fff')
+ bi = SCons.Node.FS.FileBuildInfo(fff)
+ assert hasattr(bi, 'convert_from_sconsign')
+
+ def test_prepare_dependencies(self):
+ """Test that we have a prepare_dependencies() method"""
+ fff = self.fs.File('fff')
+ bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi.prepare_dependencies()
+
+ def test_format(self):
+ """Test the format() method"""
+ f1 = self.fs.File('f1')
+ bi1 = SCons.Node.FS.FileBuildInfo(f1)
+
+ s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1'))
+ s1sig.csig = 1
+ d1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n2'))
+ d1sig.timestamp = 2
+ i1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n3'))
+ i1sig.size = 3
+
+ bi1.bsources = [self.fs.File('s1')]
+ bi1.bdepends = [self.fs.File('d1')]
+ bi1.bimplicit = [self.fs.File('i1')]
+ bi1.bsourcesigs = [s1sig]
+ bi1.bdependsigs = [d1sig]
+ bi1.bimplicitsigs = [i1sig]
+ bi1.bact = 'action'
+ bi1.bactsig = 'actionsig'
+
+ expect_lines = [
+ 's1: 1 None None',
+ 'd1: None 2 None',
+ 'i1: None None 3',
+ 'actionsig [action]',
+ ]
+
+ expect = string.join(expect_lines, '\n')
+ format = bi1.format()
+ assert format == expect, (repr(expect), repr(format))
+
+class FSTestCase(_tempdirTestCase):
+ def test_runTest(self):
+ """Test FS (file system) Node operations
+
+ This test case handles all of the file system node
+ tests in one environment, so we don't have to set up a
+ complicated directory structure for each test individually.
+ """
+ test = self.test
+
+ test.subdir('sub', ['sub', 'dir'])
+
+ wp = test.workpath('')
+ sub = test.workpath('sub', '')
+ sub_dir = test.workpath('sub', 'dir', '')
+ sub_dir_foo = test.workpath('sub', 'dir', 'foo', '')
+ sub_dir_foo_bar = test.workpath('sub', 'dir', 'foo', 'bar', '')
+ sub_foo = test.workpath('sub', 'foo', '')
+
+ os.chdir(sub_dir)
+
+ fs = SCons.Node.FS.FS()
+
+ e1 = fs.Entry('e1')
+ assert isinstance(e1, SCons.Node.FS.Entry)
+
+ d1 = fs.Dir('d1')
+ assert isinstance(d1, SCons.Node.FS.Dir)
+ assert d1.cwd is d1, d1
+
+ f1 = fs.File('f1', directory = d1)
+ assert isinstance(f1, SCons.Node.FS.File)
+
+ d1_f1 = os.path.join('d1', 'f1')
+ assert f1.path == d1_f1, "f1.path %s != %s" % (f1.path, d1_f1)
+ assert str(f1) == d1_f1, "str(f1) %s != %s" % (str(f1), d1_f1)
+
+ x1 = d1.File('x1')
+ assert isinstance(x1, SCons.Node.FS.File)
+ assert str(x1) == os.path.join('d1', 'x1')
+
+ x2 = d1.Dir('x2')
+ assert isinstance(x2, SCons.Node.FS.Dir)
+ assert str(x2) == os.path.join('d1', 'x2')
+
+ x3 = d1.Entry('x3')
+ assert isinstance(x3, SCons.Node.FS.Entry)
+ assert str(x3) == os.path.join('d1', 'x3')
+
+ assert d1.File(x1) == x1
+ assert d1.Dir(x2) == x2
+ assert d1.Entry(x3) == x3
+
+ x1.cwd = d1
+
+ x4 = x1.File('x4')
+ assert str(x4) == os.path.join('d1', 'x4')
+
+ x5 = x1.Dir('x5')
+ assert str(x5) == os.path.join('d1', 'x5')
+
+ x6 = x1.Entry('x6')
+ assert str(x6) == os.path.join('d1', 'x6')
+ x7 = x1.Entry('x7')
+ assert str(x7) == os.path.join('d1', 'x7')
+
+ assert x1.File(x4) == x4
+ assert x1.Dir(x5) == x5
+ assert x1.Entry(x6) == x6
+ assert x1.Entry(x7) == x7
+
+ assert x1.Entry(x5) == x5
+ try:
+ x1.File(x5)
+ except TypeError:
+ pass
+ else:
+ raise Exception, "did not catch expected TypeError"
+
+ assert x1.Entry(x4) == x4
+ try:
+ x1.Dir(x4)
+ except TypeError:
+ pass
+ else:
+ raise Exception, "did not catch expected TypeError"
+
+ x6 = x1.File(x6)
+ assert isinstance(x6, SCons.Node.FS.File)
+
+ x7 = x1.Dir(x7)
+ assert isinstance(x7, SCons.Node.FS.Dir)
+
+ seps = [os.sep]
+ if os.sep != '/':
+ seps = seps + ['/']
+
+ drive, path = os.path.splitdrive(os.getcwd())
+
+ def _do_Dir_test(lpath, path_, abspath_, up_path_, sep, fileSys=fs, drive=drive):
+ dir = fileSys.Dir(string.replace(lpath, '/', sep))
+
+ if os.sep != '/':
+ path_ = string.replace(path_, '/', os.sep)
+ abspath_ = string.replace(abspath_, '/', os.sep)
+ up_path_ = string.replace(up_path_, '/', os.sep)
+
+ def strip_slash(p, drive=drive):
+ if p[-1] == os.sep and len(p) > 1:
+ p = p[:-1]
+ if p[0] == os.sep:
+ p = drive + p
+ return p
+ path = strip_slash(path_)
+ abspath = strip_slash(abspath_)
+ up_path = strip_slash(up_path_)
+ name = string.split(abspath, os.sep)[-1]
+
+ assert dir.name == name, \
+ "dir.name %s != expected name %s" % \
+ (dir.name, name)
+ assert dir.path == path, \
+ "dir.path %s != expected path %s" % \
+ (dir.path, path)
+ assert str(dir) == path, \
+ "str(dir) %s != expected path %s" % \
+ (str(dir), path)
+ assert dir.get_abspath() == abspath, \
+ "dir.abspath %s != expected absolute path %s" % \
+ (dir.get_abspath(), abspath)
+ assert dir.up().path == up_path, \
+ "dir.up().path %s != expected parent path %s" % \
+ (dir.up().path, up_path)
+
+ for sep in seps:
+
+ def Dir_test(lpath, path_, abspath_, up_path_, sep=sep, func=_do_Dir_test):
+ return func(lpath, path_, abspath_, up_path_, sep)
+
+ Dir_test('', './', sub_dir, sub)
+ Dir_test('foo', 'foo/', sub_dir_foo, './')
+ Dir_test('foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/')
+ Dir_test('/foo', '/foo/', '/foo/', '/')
+ Dir_test('/foo/bar', '/foo/bar/', '/foo/bar/', '/foo/')
+ Dir_test('..', sub, sub, wp)
+ Dir_test('foo/..', './', sub_dir, sub)
+ Dir_test('../foo', sub_foo, sub_foo, sub)
+ Dir_test('.', './', sub_dir, sub)
+ Dir_test('./.', './', sub_dir, sub)
+ Dir_test('foo/./bar', 'foo/bar/', sub_dir_foo_bar, 'foo/')
+ Dir_test('#../foo', sub_foo, sub_foo, sub)
+ Dir_test('#/../foo', sub_foo, sub_foo, sub)
+ Dir_test('#foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/')
+ Dir_test('#/foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/')
+ Dir_test('#', './', sub_dir, sub)
+
+ try:
+ f2 = fs.File(string.join(['f1', 'f2'], sep), directory = d1)
+ except TypeError, x:
+ assert str(x) == ("Tried to lookup File '%s' as a Dir." %
+ d1_f1), x
+ except:
+ raise
+
+ try:
+ dir = fs.Dir(string.join(['d1', 'f1'], sep))
+ except TypeError, x:
+ assert str(x) == ("Tried to lookup File '%s' as a Dir." %
+ d1_f1), x
+ except:
+ raise
+
+ try:
+ f2 = fs.File('d1')
+ except TypeError, x:
+ assert str(x) == ("Tried to lookup Dir '%s' as a File." %
+ 'd1'), x
+ except:
+ raise
+
+ # Test that just specifying the drive works to identify
+ # its root directory.
+ p = os.path.abspath(test.workpath('root_file'))
+ drive, path = os.path.splitdrive(p)
+ if drive:
+ # The assert below probably isn't correct for the general
+ # case, but it works for Windows, which covers a lot
+ # of ground...
+ dir = fs.Dir(drive)
+ assert str(dir) == drive + os.sep, str(dir)
+
+ # Make sure that lookups with and without the drive are
+ # equivalent.
+ p = os.path.abspath(test.workpath('some/file'))
+ drive, path = os.path.splitdrive(p)
+
+ e1 = fs.Entry(p)
+ e2 = fs.Entry(path)
+ assert e1 is e2, (e1, e2)
+ assert str(e1) is str(e2), (str(e1), str(e2))
+
+ # Test for a bug in 0.04 that did not like looking up
+ # dirs with a trailing slash on Windows.
+ d=fs.Dir('./')
+ assert d.path == '.', d.abspath
+ d=fs.Dir('foo/')
+ assert d.path == 'foo', d.abspath
+
+ # Test for sub-classing of node building.
+ global built_it
+
+ built_it = None
+ assert not built_it
+ d1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE
+ d1.builder_set(Builder(fs.File))
+ d1.reset_executor()
+ d1.env_set(Environment())
+ d1.build()
+ assert built_it
+
+ built_it = None
+ assert not built_it
+ f1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE
+ f1.builder_set(Builder(fs.File))
+ f1.reset_executor()
+ f1.env_set(Environment())
+ f1.build()
+ assert built_it
+
+ def match(path, expect):
+ expect = string.replace(expect, '/', os.sep)
+ assert path == expect, "path %s != expected %s" % (path, expect)
+
+ e1 = fs.Entry("d1")
+ assert e1.__class__.__name__ == 'Dir'
+ match(e1.path, "d1")
+ match(e1.dir.path, ".")
+
+ e2 = fs.Entry("d1/f1")
+ assert e2.__class__.__name__ == 'File'
+ match(e2.path, "d1/f1")
+ match(e2.dir.path, "d1")
+
+ e3 = fs.Entry("e3")
+ assert e3.__class__.__name__ == 'Entry'
+ match(e3.path, "e3")
+ match(e3.dir.path, ".")
+
+ e4 = fs.Entry("d1/e4")
+ assert e4.__class__.__name__ == 'Entry'
+ match(e4.path, "d1/e4")
+ match(e4.dir.path, "d1")
+
+ e5 = fs.Entry("e3/e5")
+ assert e3.__class__.__name__ == 'Dir'
+ match(e3.path, "e3")
+ match(e3.dir.path, ".")
+ assert e5.__class__.__name__ == 'Entry'
+ match(e5.path, "e3/e5")
+ match(e5.dir.path, "e3")
+
+ e6 = fs.Dir("d1/e4")
+ assert e6 is e4
+ assert e4.__class__.__name__ == 'Dir'
+ match(e4.path, "d1/e4")
+ match(e4.dir.path, "d1")
+
+ e7 = fs.File("e3/e5")
+ assert e7 is e5
+ assert e5.__class__.__name__ == 'File'
+ match(e5.path, "e3/e5")
+ match(e5.dir.path, "e3")
+
+ fs.chdir(fs.Dir('subdir'))
+ f11 = fs.File("f11")
+ match(f11.path, "subdir/f11")
+ d12 = fs.Dir("d12")
+ e13 = fs.Entry("subdir/e13")
+ match(e13.path, "subdir/subdir/e13")
+ fs.chdir(fs.Dir('..'))
+
+ # Test scanning
+ f1.builder_set(Builder(fs.File))
+ f1.env_set(Environment())
+ xyz = fs.File("xyz")
+ f1.builder.target_scanner = Scanner(xyz)
+
+ f1.scan()
+ assert f1.implicit[0].path == "xyz"
+ f1.implicit = []
+ f1.scan()
+ assert f1.implicit == []
+ f1.implicit = None
+ f1.scan()
+ assert f1.implicit[0].path == "xyz"
+
+ # Test underlying scanning functionality in get_found_includes()
+ env = Environment()
+ f12 = fs.File("f12")
+ t1 = fs.File("t1")
+
+ deps = f12.get_found_includes(env, None, t1)
+ assert deps == [], deps
+
+ class MyScanner(Scanner):
+ call_count = 0
+ def __call__(self, node, env, path):
+ self.call_count = self.call_count + 1
+ return Scanner.__call__(self, node, env, path)
+ s = MyScanner(xyz)
+
+ deps = f12.get_found_includes(env, s, t1)
+ assert deps == [xyz], deps
+ assert s.call_count == 1, s.call_count
+
+ f12.built()
+
+ deps = f12.get_found_includes(env, s, t1)
+ assert deps == [xyz], deps
+ assert s.call_count == 2, s.call_count
+
+ env2 = Environment()
+
+ deps = f12.get_found_includes(env2, s, t1)
+ assert deps == [xyz], deps
+ assert s.call_count == 3, s.call_count
+
+
+
+ # Make sure we can scan this file even if the target isn't
+ # a file that has a scanner (it might be an Alias, e.g.).
+ class DummyNode:
+ pass
+
+ deps = f12.get_found_includes(env, s, DummyNode())
+ assert deps == [xyz], deps
+
+ # Test building a file whose directory is not there yet...
+ f1 = fs.File(test.workpath("foo/bar/baz/ack"))
+ assert not f1.dir.exists()
+ f1.prepare()
+ f1.build()
+ assert f1.dir.exists()
+
+ os.chdir('..')
+
+ # Test getcwd()
+ fs = SCons.Node.FS.FS()
+ assert str(fs.getcwd()) == ".", str(fs.getcwd())
+ fs.chdir(fs.Dir('subdir'))
+ # The cwd's path is always "."
+ assert str(fs.getcwd()) == ".", str(fs.getcwd())
+ assert fs.getcwd().path == 'subdir', fs.getcwd().path
+ fs.chdir(fs.Dir('../..'))
+ assert fs.getcwd().path == test.workdir, fs.getcwd().path
+
+ f1 = fs.File(test.workpath("do_i_exist"))
+ assert not f1.exists()
+ test.write("do_i_exist","\n")
+ assert not f1.exists(), "exists() call not cached"
+ f1.built()
+ assert f1.exists(), "exists() call caching not reset"
+ test.unlink("do_i_exist")
+ assert f1.exists()
+ f1.built()
+ assert not f1.exists()
+
+ # For some reason, in Windows, the \x1a character terminates
+ # the reading of files in text mode. This tests that
+ # get_contents() returns the binary contents.
+ test.write("binary_file", "Foo\x1aBar")
+ f1 = fs.File(test.workpath("binary_file"))
+ assert f1.get_contents() == "Foo\x1aBar", f1.get_contents()
+
+ try:
+ # TODO(1.5)
+ eval('test_string = u"Foo\x1aBar"')
+ except SyntaxError:
+ pass
+ else:
+ # This tests to make sure we can decode UTF-8 text files.
+ test.write("utf8_file", test_string.encode('utf-8'))
+ f1 = fs.File(test.workpath("utf8_file"))
+ assert eval('f1.get_text_contents() == u"Foo\x1aBar"'), \
+ f1.get_text_contents()
+
+ def nonexistent(method, s):
+ try:
+ x = method(s, create = 0)
+ except SCons.Errors.UserError:
+ pass
+ else:
+ raise Exception, "did not catch expected UserError"
+
+ nonexistent(fs.Entry, 'nonexistent')
+ nonexistent(fs.Entry, 'nonexistent/foo')
+
+ nonexistent(fs.File, 'nonexistent')
+ nonexistent(fs.File, 'nonexistent/foo')
+
+ nonexistent(fs.Dir, 'nonexistent')
+ nonexistent(fs.Dir, 'nonexistent/foo')
+
+ test.write("preserve_me", "\n")
+ assert os.path.exists(test.workpath("preserve_me"))
+ f1 = fs.File(test.workpath("preserve_me"))
+ f1.prepare()
+ assert os.path.exists(test.workpath("preserve_me"))
+
+ test.write("remove_me", "\n")
+ assert os.path.exists(test.workpath("remove_me"))
+ f1 = fs.File(test.workpath("remove_me"))
+ f1.builder = Builder(fs.File)
+ f1.env_set(Environment())
+ f1.prepare()
+ assert not os.path.exists(test.workpath("remove_me"))
+
+ e = fs.Entry('e_local')
+ assert not hasattr(e, '_local')
+ e.set_local()
+ assert e._local == 1
+ f = fs.File('e_local')
+ assert f._local == 1
+ f = fs.File('f_local')
+ assert f._local == 0
+
+ #XXX test_is_up_to_date() for directories
+
+ #XXX test_sconsign() for directories
+
+ #XXX test_set_signature() for directories
+
+ #XXX test_build() for directories
+
+ #XXX test_root()
+
+ # test Entry.get_contents()
+ e = fs.Entry('does_not_exist')
+ c = e.get_contents()
+ assert c == "", c
+ assert e.__class__ == SCons.Node.FS.Entry
+
+ test.write("file", "file\n")
+ try:
+ e = fs.Entry('file')
+ c = e.get_contents()
+ assert c == "file\n", c
+ assert e.__class__ == SCons.Node.FS.File
+ finally:
+ test.unlink("file")
+
+ # test Entry.get_text_contents()
+ e = fs.Entry('does_not_exist')
+ c = e.get_text_contents()
+ assert c == "", c
+ assert e.__class__ == SCons.Node.FS.Entry
+
+ test.write("file", "file\n")
+ try:
+ e = fs.Entry('file')
+ c = e.get_text_contents()
+ assert c == "file\n", c
+ assert e.__class__ == SCons.Node.FS.File
+ finally:
+ test.unlink("file")
+
+ test.subdir("dir")
+ e = fs.Entry('dir')
+ c = e.get_contents()
+ assert c == "", c
+ assert e.__class__ == SCons.Node.FS.Dir
+
+ c = e.get_text_contents()
+ try:
+ eval('assert c == u"", c')
+ except SyntaxError:
+ assert c == ""
+
+ if hasattr(os, 'symlink'):
+ os.symlink('nonexistent', test.workpath('dangling_symlink'))
+ e = fs.Entry('dangling_symlink')
+ c = e.get_contents()
+ assert e.__class__ == SCons.Node.FS.Entry, e.__class__
+ assert c == "", c
+ c = e.get_text_contents()
+ try:
+ eval('assert c == u"", c')
+ except SyntaxError:
+ assert c == "", c
+
+ test.write("tstamp", "tstamp\n")
+ try:
+ # Okay, *this* manipulation accomodates Windows FAT file systems
+ # that only have two-second granularity on their timestamps.
+ # We round down the current time to the nearest even integer
+ # value, subtract two to make sure the timestamp is not "now,"
+ # and then convert it back to a float.
+ tstamp = float(int(time.time() / 2) * 2) - 2
+ os.utime(test.workpath("tstamp"), (tstamp - 2.0, tstamp))
+ f = fs.File("tstamp")
+ t = f.get_timestamp()
+ assert t == tstamp, "expected %f, got %f" % (tstamp, t)
+ finally:
+ test.unlink("tstamp")
+
+ test.subdir('tdir1')
+ d = fs.Dir('tdir1')
+ t = d.get_timestamp()
+ assert t == 0, "expected 0, got %s" % str(t)
+
+ test.subdir('tdir2')
+ f1 = test.workpath('tdir2', 'file1')
+ f2 = test.workpath('tdir2', 'file2')
+ test.write(f1, 'file1\n')
+ test.write(f2, 'file2\n')
+ current_time = float(int(time.time() / 2) * 2)
+ t1 = current_time - 4.0
+ t2 = current_time - 2.0
+ os.utime(f1, (t1 - 2.0, t1))
+ os.utime(f2, (t2 - 2.0, t2))
+ d = fs.Dir('tdir2')
+ fs.File(f1)
+ fs.File(f2)
+ t = d.get_timestamp()
+ assert t == t2, "expected %f, got %f" % (t2, t)
+
+ skey = fs.Entry('eee.x').scanner_key()
+ assert skey == '.x', skey
+ skey = fs.Entry('eee.xyz').scanner_key()
+ assert skey == '.xyz', skey
+
+ skey = fs.File('fff.x').scanner_key()
+ assert skey == '.x', skey
+ skey = fs.File('fff.xyz').scanner_key()
+ assert skey == '.xyz', skey
+
+ skey = fs.Dir('ddd.x').scanner_key()
+ assert skey is None, skey
+
+ test.write("i_am_not_a_directory", "\n")
+ try:
+ exc_caught = 0
+ try:
+ fs.Dir(test.workpath("i_am_not_a_directory"))
+ except TypeError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a TypeError"
+ finally:
+ test.unlink("i_am_not_a_directory")
+
+ exc_caught = 0
+ try:
+ fs.File(sub_dir)
+ except TypeError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a TypeError"
+
+ # XXX test_is_up_to_date()
+
+ d = fs.Dir('dir')
+ r = d.remove()
+ assert r is None, r
+
+ f = fs.File('does_not_exist')
+ r = f.remove()
+ assert r is None, r
+
+ test.write('exists', "exists\n")
+ f = fs.File('exists')
+ r = f.remove()
+ assert r, r
+ assert not os.path.exists(test.workpath('exists')), "exists was not removed"
+
+ symlink = test.workpath('symlink')
+ try:
+ os.symlink(test.workpath('does_not_exist'), symlink)
+ assert os.path.islink(symlink)
+ f = fs.File('symlink')
+ r = f.remove()
+ assert r, r
+ assert not os.path.islink(symlink), "symlink was not removed"
+ except AttributeError:
+ pass
+
+ test.write('can_not_remove', "can_not_remove\n")
+ test.writable(test.workpath('.'), 0)
+ fp = open(test.workpath('can_not_remove'))
+
+ f = fs.File('can_not_remove')
+ exc_caught = 0
+ try:
+ r = f.remove()
+ except OSError:
+ exc_caught = 1
+
+ fp.close()
+
+ assert exc_caught, "Should have caught an OSError, r = " + str(r)
+
+ f = fs.Entry('foo/bar/baz')
+ assert f.for_signature() == 'baz', f.for_signature()
+ assert f.get_string(0) == os.path.normpath('foo/bar/baz'), \
+ f.get_string(0)
+ assert f.get_string(1) == 'baz', f.get_string(1)
+
+ def test_drive_letters(self):
+ """Test drive-letter look-ups"""
+
+ test = self.test
+
+ test.subdir('sub', ['sub', 'dir'])
+
+ def drive_workpath(drive, dirs, test=test):
+ x = apply(test.workpath, dirs)
+ drive, path = os.path.splitdrive(x)
+ return 'X:' + path
+
+ wp = drive_workpath('X:', [''])
+
+ if wp[-1] in (os.sep, '/'):
+ tmp = os.path.split(wp[:-1])[0]
+ else:
+ tmp = os.path.split(wp)[0]
+
+ parent_tmp = os.path.split(tmp)[0]
+ if parent_tmp == 'X:':
+ parent_tmp = 'X:' + os.sep
+
+ tmp_foo = os.path.join(tmp, 'foo')
+
+ foo = drive_workpath('X:', ['foo'])
+ foo_bar = drive_workpath('X:', ['foo', 'bar'])
+ sub = drive_workpath('X:', ['sub', ''])
+ sub_dir = drive_workpath('X:', ['sub', 'dir', ''])
+ sub_dir_foo = drive_workpath('X:', ['sub', 'dir', 'foo', ''])
+ sub_dir_foo_bar = drive_workpath('X:', ['sub', 'dir', 'foo', 'bar', ''])
+ sub_foo = drive_workpath('X:', ['sub', 'foo', ''])
+
+ fs = SCons.Node.FS.FS()
+
+ seps = [os.sep]
+ if os.sep != '/':
+ seps = seps + ['/']
+
+ def _do_Dir_test(lpath, path_, up_path_, sep, fileSys=fs):
+ dir = fileSys.Dir(string.replace(lpath, '/', sep))
+
+ if os.sep != '/':
+ path_ = string.replace(path_, '/', os.sep)
+ up_path_ = string.replace(up_path_, '/', os.sep)
+
+ def strip_slash(p):
+ if p[-1] == os.sep and len(p) > 3:
+ p = p[:-1]
+ return p
+ path = strip_slash(path_)
+ up_path = strip_slash(up_path_)
+ name = string.split(path, os.sep)[-1]
+
+ assert dir.name == name, \
+ "dir.name %s != expected name %s" % \
+ (dir.name, name)
+ assert dir.path == path, \
+ "dir.path %s != expected path %s" % \
+ (dir.path, path)
+ assert str(dir) == path, \
+ "str(dir) %s != expected path %s" % \
+ (str(dir), path)
+ assert dir.up().path == up_path, \
+ "dir.up().path %s != expected parent path %s" % \
+ (dir.up().path, up_path)
+
+ save_os_path = os.path
+ save_os_sep = os.sep
+ try:
+ import ntpath
+ os.path = ntpath
+ os.sep = '\\'
+ SCons.Node.FS.initialize_do_splitdrive()
+ SCons.Node.FS.initialize_normpath_check()
+
+ for sep in seps:
+
+ def Dir_test(lpath, path_, up_path_, sep=sep, func=_do_Dir_test):
+ return func(lpath, path_, up_path_, sep)
+
+ Dir_test('#X:', wp, tmp)
+ Dir_test('X:foo', foo, wp)
+ Dir_test('X:foo/bar', foo_bar, foo)
+ Dir_test('X:/foo', 'X:/foo', 'X:/')
+ Dir_test('X:/foo/bar', 'X:/foo/bar/', 'X:/foo/')
+ Dir_test('X:..', tmp, parent_tmp)
+ Dir_test('X:foo/..', wp, tmp)
+ Dir_test('X:../foo', tmp_foo, tmp)
+ Dir_test('X:.', wp, tmp)
+ Dir_test('X:./.', wp, tmp)
+ Dir_test('X:foo/./bar', foo_bar, foo)
+ Dir_test('#X:../foo', tmp_foo, tmp)
+ Dir_test('#X:/../foo', tmp_foo, tmp)
+ Dir_test('#X:foo/bar', foo_bar, foo)
+ Dir_test('#X:/foo/bar', foo_bar, foo)
+ Dir_test('#X:/', wp, tmp)
+ finally:
+ os.path = save_os_path
+ os.sep = save_os_sep
+ SCons.Node.FS.initialize_do_splitdrive()
+ SCons.Node.FS.initialize_normpath_check()
+
+ def test_target_from_source(self):
+ """Test the method for generating target nodes from sources"""
+ fs = self.fs
+
+ x = fs.File('x.c')
+ t = x.target_from_source('pre-', '-suf')
+ assert str(t) == 'pre-x-suf', str(t)
+ assert t.__class__ == SCons.Node.FS.Entry
+
+ y = fs.File('dir/y')
+ t = y.target_from_source('pre-', '-suf')
+ assert str(t) == os.path.join('dir', 'pre-y-suf'), str(t)
+ assert t.__class__ == SCons.Node.FS.Entry
+
+ z = fs.File('zz')
+ t = z.target_from_source('pre-', '-suf', lambda x: x[:-1])
+ assert str(t) == 'pre-z-suf', str(t)
+ assert t.__class__ == SCons.Node.FS.Entry
+
+ d = fs.Dir('ddd')
+ t = d.target_from_source('pre-', '-suf')
+ assert str(t) == 'pre-ddd-suf', str(t)
+ assert t.__class__ == SCons.Node.FS.Entry
+
+ e = fs.Entry('eee')
+ t = e.target_from_source('pre-', '-suf')
+ assert str(t) == 'pre-eee-suf', str(t)
+ assert t.__class__ == SCons.Node.FS.Entry
+
+ def test_same_name(self):
+ """Test that a local same-named file isn't found for a Dir lookup"""
+ test = self.test
+ fs = self.fs
+
+ test.subdir('subdir')
+ test.write(['subdir', 'build'], "subdir/build\n")
+
+ subdir = fs.Dir('subdir')
+ fs.chdir(subdir, change_os_dir=1)
+ self.fs._lookup('#build/file', subdir, SCons.Node.FS.File)
+
+ def test_above_root(self):
+ """Testing looking up a path above the root directory"""
+ test = self.test
+ fs = self.fs
+
+ d1 = fs.Dir('d1')
+ d2 = d1.Dir('d2')
+ dirs = string.split(os.path.normpath(d2.abspath), os.sep)
+ above_path = apply(os.path.join, ['..']*len(dirs) + ['above'])
+ above = d2.Dir(above_path)
+
+ def test_rel_path(self):
+ """Test the rel_path() method"""
+ test = self.test
+ fs = self.fs
+
+ d1 = fs.Dir('d1')
+ d1_f = d1.File('f')
+ d1_d2 = d1.Dir('d2')
+ d1_d2_f = d1_d2.File('f')
+
+ d3 = fs.Dir('d3')
+ d3_f = d3.File('f')
+ d3_d4 = d3.Dir('d4')
+ d3_d4_f = d3_d4.File('f')
+
+ cases = [
+ d1, d1, '.',
+ d1, d1_f, 'f',
+ d1, d1_d2, 'd2',
+ d1, d1_d2_f, 'd2/f',
+ d1, d3, '../d3',
+ d1, d3_f, '../d3/f',
+ d1, d3_d4, '../d3/d4',
+ d1, d3_d4_f, '../d3/d4/f',
+
+ d1_f, d1, '.',
+ d1_f, d1_f, 'f',
+ d1_f, d1_d2, 'd2',
+ d1_f, d1_d2_f, 'd2/f',
+ d1_f, d3, '../d3',
+ d1_f, d3_f, '../d3/f',
+ d1_f, d3_d4, '../d3/d4',
+ d1_f, d3_d4_f, '../d3/d4/f',
+
+ d1_d2, d1, '..',
+ d1_d2, d1_f, '../f',
+ d1_d2, d1_d2, '.',
+ d1_d2, d1_d2_f, 'f',
+ d1_d2, d3, '../../d3',
+ d1_d2, d3_f, '../../d3/f',
+ d1_d2, d3_d4, '../../d3/d4',
+ d1_d2, d3_d4_f, '../../d3/d4/f',
+
+ d1_d2_f, d1, '..',
+ d1_d2_f, d1_f, '../f',
+ d1_d2_f, d1_d2, '.',
+ d1_d2_f, d1_d2_f, 'f',
+ d1_d2_f, d3, '../../d3',
+ d1_d2_f, d3_f, '../../d3/f',
+ d1_d2_f, d3_d4, '../../d3/d4',
+ d1_d2_f, d3_d4_f, '../../d3/d4/f',
+ ]
+
+ if sys.platform in ('win32',):
+ x_d1 = fs.Dir(r'X:\d1')
+ x_d1_d2 = x_d1.Dir('d2')
+ y_d1 = fs.Dir(r'Y:\d1')
+ y_d1_d2 = y_d1.Dir('d2')
+ y_d2 = fs.Dir(r'Y:\d2')
+
+ win32_cases = [
+ x_d1, x_d1, '.',
+ x_d1, x_d1_d2, 'd2',
+ x_d1, y_d1, r'Y:\d1',
+ x_d1, y_d1_d2, r'Y:\d1\d2',
+ x_d1, y_d2, r'Y:\d2',
+ ]
+
+ cases.extend(win32_cases)
+
+ failed = 0
+ while cases:
+ dir, other, expect = cases[:3]
+ expect = os.path.normpath(expect)
+ del cases[:3]
+ result = dir.rel_path(other)
+ if result != expect:
+ if failed == 0: print
+ fmt = " dir_path(%(dir)s, %(other)s) => '%(result)s' did not match '%(expect)s'"
+ print fmt % locals()
+ failed = failed + 1
+ assert failed == 0, "%d rel_path() cases failed" % failed
+
+ def test_proxy(self):
+ """Test a Node.FS object wrapped in a proxy instance"""
+ f1 = self.fs.File('fff')
+ class Proxy:
+ # Simplest possibly Proxy class that works for our test,
+ # this is stripped down from SCons.Util.Proxy.
+ def __init__(self, subject):
+ self.__subject = subject
+ def __getattr__(self, name):
+ return getattr(self.__subject, name)
+ p = Proxy(f1)
+ f2 = self.fs.Entry(p)
+ assert f1 is f2, (f1, f2)
+
+
+
+class DirTestCase(_tempdirTestCase):
+
+ def test__morph(self):
+ """Test handling of actions when morphing an Entry into a Dir"""
+ test = self.test
+ e = self.fs.Entry('eee')
+ x = e.get_executor()
+ x.add_pre_action('pre')
+ x.add_post_action('post')
+ e.must_be_same(SCons.Node.FS.Dir)
+ a = x.get_action_list()
+ assert a[0] == 'pre', a
+ assert a[2] == 'post', a
+
+ def test_subclass(self):
+ """Test looking up subclass of Dir nodes"""
+ class DirSubclass(SCons.Node.FS.Dir):
+ pass
+ sd = self.fs._lookup('special_dir', None, DirSubclass, create=1)
+ sd.must_be_same(SCons.Node.FS.Dir)
+
+ def test_get_env_scanner(self):
+ """Test the Dir.get_env_scanner() method
+ """
+ import SCons.Defaults
+ d = self.fs.Dir('ddd')
+ s = d.get_env_scanner(Environment())
+ assert s is SCons.Defaults.DirEntryScanner, s
+
+ def test_get_target_scanner(self):
+ """Test the Dir.get_target_scanner() method
+ """
+ import SCons.Defaults
+ d = self.fs.Dir('ddd')
+ s = d.get_target_scanner()
+ assert s is SCons.Defaults.DirEntryScanner, s
+
+ def test_scan(self):
+ """Test scanning a directory for in-memory entries
+ """
+ fs = self.fs
+
+ dir = fs.Dir('ddd')
+ fs.File(os.path.join('ddd', 'f1'))
+ fs.File(os.path.join('ddd', 'f2'))
+ fs.File(os.path.join('ddd', 'f3'))
+ fs.Dir(os.path.join('ddd', 'd1'))
+ fs.Dir(os.path.join('ddd', 'd1', 'f4'))
+ fs.Dir(os.path.join('ddd', 'd1', 'f5'))
+ dir.scan()
+ kids = map(lambda x: x.path, dir.children(None))
+ kids.sort()
+ assert kids == [os.path.join('ddd', 'd1'),
+ os.path.join('ddd', 'f1'),
+ os.path.join('ddd', 'f2'),
+ os.path.join('ddd', 'f3')], kids
+
+ def test_get_contents(self):
+ """Test getting the contents for a directory.
+ """
+ test = self.test
+
+ test.subdir('d')
+ test.write(['d', 'g'], "67890\n")
+ test.write(['d', 'f'], "12345\n")
+ test.subdir(['d','sub'])
+ test.write(['d', 'sub','h'], "abcdef\n")
+ test.subdir(['d','empty'])
+
+ d = self.fs.Dir('d')
+ g = self.fs.File(os.path.join('d', 'g'))
+ f = self.fs.File(os.path.join('d', 'f'))
+ h = self.fs.File(os.path.join('d', 'sub', 'h'))
+ e = self.fs.Dir(os.path.join('d', 'empty'))
+ s = self.fs.Dir(os.path.join('d', 'sub'))
+
+ #TODO(1.5) files = d.get_contents().split('\n')
+ files = string.split(d.get_contents(), '\n')
+
+ assert e.get_contents() == '', e.get_contents()
+ assert e.get_text_contents() == '', e.get_text_contents()
+ assert e.get_csig()+" empty" == files[0], files
+ assert f.get_csig()+" f" == files[1], files
+ assert g.get_csig()+" g" == files[2], files
+ assert s.get_csig()+" sub" == files[3], files
+
+ def test_implicit_re_scans(self):
+ """Test that adding entries causes a directory to be re-scanned
+ """
+
+ fs = self.fs
+
+ dir = fs.Dir('ddd')
+
+ fs.File(os.path.join('ddd', 'f1'))
+ dir.scan()
+ kids = map(lambda x: x.path, dir.children())
+ kids.sort()
+ assert kids == [os.path.join('ddd', 'f1')], kids
+
+ fs.File(os.path.join('ddd', 'f2'))
+ dir.scan()
+ kids = map(lambda x: x.path, dir.children())
+ kids.sort()
+ assert kids == [os.path.join('ddd', 'f1'),
+ os.path.join('ddd', 'f2')], kids
+
+ def test_entry_exists_on_disk(self):
+ """Test the Dir.entry_exists_on_disk() method
+ """
+ test = self.test
+
+ does_not_exist = self.fs.Dir('does_not_exist')
+ assert not does_not_exist.entry_exists_on_disk('foo')
+
+ test.subdir('d')
+ test.write(['d', 'exists'], "d/exists\n")
+ test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n")
+
+ d = self.fs.Dir('d')
+ assert d.entry_exists_on_disk('exists')
+ assert not d.entry_exists_on_disk('does_not_exist')
+
+ if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
+ assert d.entry_exists_on_disk('case-insensitive')
+
+ def test_srcdir_list(self):
+ """Test the Dir.srcdir_list() method
+ """
+ src = self.fs.Dir('src')
+ bld = self.fs.Dir('bld')
+ sub1 = bld.Dir('sub')
+ sub2 = sub1.Dir('sub')
+ sub3 = sub2.Dir('sub')
+ self.fs.VariantDir(bld, src, duplicate=0)
+ self.fs.VariantDir(sub2, src, duplicate=0)
+
+ def check(result, expect):
+ result = map(str, result)
+ expect = map(os.path.normpath, expect)
+ assert result == expect, result
+
+ s = src.srcdir_list()
+ check(s, [])
+
+ s = bld.srcdir_list()
+ check(s, ['src'])
+
+ s = sub1.srcdir_list()
+ check(s, ['src/sub'])
+
+ s = sub2.srcdir_list()
+ check(s, ['src', 'src/sub/sub'])
+
+ s = sub3.srcdir_list()
+ check(s, ['src/sub', 'src/sub/sub/sub'])
+
+ self.fs.VariantDir('src/b1/b2', 'src')
+ b1 = src.Dir('b1')
+ b1_b2 = b1.Dir('b2')
+ b1_b2_b1 = b1_b2.Dir('b1')
+ b1_b2_b1_b2 = b1_b2_b1.Dir('b2')
+ b1_b2_b1_b2_sub = b1_b2_b1_b2.Dir('sub')
+
+ s = b1.srcdir_list()
+ check(s, [])
+
+ s = b1_b2.srcdir_list()
+ check(s, ['src'])
+
+ s = b1_b2_b1.srcdir_list()
+ check(s, ['src/b1'])
+
+ s = b1_b2_b1_b2.srcdir_list()
+ check(s, ['src/b1/b2'])
+
+ s = b1_b2_b1_b2_sub.srcdir_list()
+ check(s, ['src/b1/b2/sub'])
+
+ def test_srcdir_duplicate(self):
+ """Test the Dir.srcdir_duplicate() method
+ """
+ test = self.test
+
+ test.subdir('src0')
+ test.write(['src0', 'exists'], "src0/exists\n")
+
+ bld0 = self.fs.Dir('bld0')
+ src0 = self.fs.Dir('src0')
+ self.fs.VariantDir(bld0, src0, duplicate=0)
+
+ n = bld0.srcdir_duplicate('does_not_exist')
+ assert n is None, n
+ assert not os.path.exists(test.workpath('bld0', 'does_not_exist'))
+
+ n = bld0.srcdir_duplicate('exists')
+ assert str(n) == os.path.normpath('src0/exists'), str(n)
+ assert not os.path.exists(test.workpath('bld0', 'exists'))
+
+ test.subdir('src1')
+ test.write(['src1', 'exists'], "src0/exists\n")
+
+ bld1 = self.fs.Dir('bld1')
+ src1 = self.fs.Dir('src1')
+ self.fs.VariantDir(bld1, src1, duplicate=1)
+
+ n = bld1.srcdir_duplicate('does_not_exist')
+ assert n is None, n
+ assert not os.path.exists(test.workpath('bld1', 'does_not_exist'))
+
+ n = bld1.srcdir_duplicate('exists')
+ assert str(n) == os.path.normpath('bld1/exists'), str(n)
+ assert os.path.exists(test.workpath('bld1', 'exists'))
+
+ def test_srcdir_find_file(self):
+ """Test the Dir.srcdir_find_file() method
+ """
+ test = self.test
+
+ return_true = lambda: 1
+
+ test.subdir('src0')
+ test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n")
+ test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n")
+ test.write(['src0', 'on-disk-e1'], "src0/on-disk-e1\n")
+ test.write(['src0', 'on-disk-e2'], "src0/on-disk-e2\n")
+
+ bld0 = self.fs.Dir('bld0')
+ src0 = self.fs.Dir('src0')
+ self.fs.VariantDir(bld0, src0, duplicate=0)
+
+ derived_f = src0.File('derived-f')
+ derived_f.is_derived = return_true
+ exists_f = src0.File('exists-f')
+ exists_f.exists = return_true
+
+ derived_e = src0.Entry('derived-e')
+ derived_e.is_derived = return_true
+ exists_e = src0.Entry('exists-e')
+ exists_e.exists = return_true
+
+ def check(result, expect):
+ result = map(str, result)
+ expect = map(os.path.normpath, expect)
+ assert result == expect, result
+
+ # First check from the source directory.
+ n = src0.srcdir_find_file('does_not_exist')
+ assert n == (None, None), n
+
+ n = src0.srcdir_find_file('derived-f')
+ check(n, ['src0/derived-f', 'src0'])
+ n = src0.srcdir_find_file('exists-f')
+ check(n, ['src0/exists-f', 'src0'])
+ n = src0.srcdir_find_file('on-disk-f1')
+ check(n, ['src0/on-disk-f1', 'src0'])
+
+ n = src0.srcdir_find_file('derived-e')
+ check(n, ['src0/derived-e', 'src0'])
+ n = src0.srcdir_find_file('exists-e')
+ check(n, ['src0/exists-e', 'src0'])
+ n = src0.srcdir_find_file('on-disk-e1')
+ check(n, ['src0/on-disk-e1', 'src0'])
+
+ # Now check from the variant directory.
+ n = bld0.srcdir_find_file('does_not_exist')
+ assert n == (None, None), n
+
+ n = bld0.srcdir_find_file('derived-f')
+ check(n, ['src0/derived-f', 'bld0'])
+ n = bld0.srcdir_find_file('exists-f')
+ check(n, ['src0/exists-f', 'bld0'])
+ n = bld0.srcdir_find_file('on-disk-f2')
+ check(n, ['src0/on-disk-f2', 'bld0'])
+
+ n = bld0.srcdir_find_file('derived-e')
+ check(n, ['src0/derived-e', 'bld0'])
+ n = bld0.srcdir_find_file('exists-e')
+ check(n, ['src0/exists-e', 'bld0'])
+ n = bld0.srcdir_find_file('on-disk-e2')
+ check(n, ['src0/on-disk-e2', 'bld0'])
+
+ test.subdir('src1')
+ test.write(['src1', 'on-disk-f1'], "src1/on-disk-f1\n")
+ test.write(['src1', 'on-disk-f2'], "src1/on-disk-f2\n")
+ test.write(['src1', 'on-disk-e1'], "src1/on-disk-e1\n")
+ test.write(['src1', 'on-disk-e2'], "src1/on-disk-e2\n")
+
+ bld1 = self.fs.Dir('bld1')
+ src1 = self.fs.Dir('src1')
+ self.fs.VariantDir(bld1, src1, duplicate=1)
+
+ derived_f = src1.File('derived-f')
+ derived_f.is_derived = return_true
+ exists_f = src1.File('exists-f')
+ exists_f.exists = return_true
+
+ derived_e = src1.Entry('derived-e')
+ derived_e.is_derived = return_true
+ exists_e = src1.Entry('exists-e')
+ exists_e.exists = return_true
+
+ # First check from the source directory.
+ n = src1.srcdir_find_file('does_not_exist')
+ assert n == (None, None), n
+
+ n = src1.srcdir_find_file('derived-f')
+ check(n, ['src1/derived-f', 'src1'])
+ n = src1.srcdir_find_file('exists-f')
+ check(n, ['src1/exists-f', 'src1'])
+ n = src1.srcdir_find_file('on-disk-f1')
+ check(n, ['src1/on-disk-f1', 'src1'])
+
+ n = src1.srcdir_find_file('derived-e')
+ check(n, ['src1/derived-e', 'src1'])
+ n = src1.srcdir_find_file('exists-e')
+ check(n, ['src1/exists-e', 'src1'])
+ n = src1.srcdir_find_file('on-disk-e1')
+ check(n, ['src1/on-disk-e1', 'src1'])
+
+ # Now check from the variant directory.
+ n = bld1.srcdir_find_file('does_not_exist')
+ assert n == (None, None), n
+
+ n = bld1.srcdir_find_file('derived-f')
+ check(n, ['bld1/derived-f', 'src1'])
+ n = bld1.srcdir_find_file('exists-f')
+ check(n, ['bld1/exists-f', 'src1'])
+ n = bld1.srcdir_find_file('on-disk-f2')
+ check(n, ['bld1/on-disk-f2', 'bld1'])
+
+ n = bld1.srcdir_find_file('derived-e')
+ check(n, ['bld1/derived-e', 'src1'])
+ n = bld1.srcdir_find_file('exists-e')
+ check(n, ['bld1/exists-e', 'src1'])
+ n = bld1.srcdir_find_file('on-disk-e2')
+ check(n, ['bld1/on-disk-e2', 'bld1'])
+
+ def test_dir_on_disk(self):
+ """Test the Dir.dir_on_disk() method"""
+ self.test.subdir('sub', ['sub', 'exists'])
+ self.test.write(['sub', 'file'], "self/file\n")
+ sub = self.fs.Dir('sub')
+
+ r = sub.dir_on_disk('does_not_exist')
+ assert not r, r
+
+ r = sub.dir_on_disk('exists')
+ assert r, r
+
+ r = sub.dir_on_disk('file')
+ assert not r, r
+
+ def test_file_on_disk(self):
+ """Test the Dir.file_on_disk() method"""
+ self.test.subdir('sub', ['sub', 'dir'])
+ self.test.write(['sub', 'exists'], "self/exists\n")
+ sub = self.fs.Dir('sub')
+
+ r = sub.file_on_disk('does_not_exist')
+ assert not r, r
+
+ r = sub.file_on_disk('exists')
+ assert r, r
+
+ r = sub.file_on_disk('dir')
+ assert not r, r
+
+class EntryTestCase(_tempdirTestCase):
+ def test_runTest(self):
+ """Test methods specific to the Entry sub-class.
+ """
+ test = TestCmd(workdir='')
+ # FS doesn't like the cwd to be something other than its root.
+ os.chdir(test.workpath(""))
+
+ fs = SCons.Node.FS.FS()
+
+ e1 = fs.Entry('e1')
+ e1.rfile()
+ assert e1.__class__ is SCons.Node.FS.File, e1.__class__
+
+ test.subdir('e3d')
+ test.write('e3f', "e3f\n")
+
+ e3d = fs.Entry('e3d')
+ e3d.get_contents()
+ assert e3d.__class__ is SCons.Node.FS.Dir, e3d.__class__
+
+ e3f = fs.Entry('e3f')
+ e3f.get_contents()
+ assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__
+
+ e3n = fs.Entry('e3n')
+ e3n.get_contents()
+ assert e3n.__class__ is SCons.Node.FS.Entry, e3n.__class__
+
+ test.subdir('e4d')
+ test.write('e4f', "e4f\n")
+
+ e4d = fs.Entry('e4d')
+ exists = e4d.exists()
+ assert e4d.__class__ is SCons.Node.FS.Dir, e4d.__class__
+ assert exists, "e4d does not exist?"
+
+ e4f = fs.Entry('e4f')
+ exists = e4f.exists()
+ assert e4f.__class__ is SCons.Node.FS.File, e4f.__class__
+ assert exists, "e4f does not exist?"
+
+ e4n = fs.Entry('e4n')
+ exists = e4n.exists()
+ assert e4n.__class__ is SCons.Node.FS.File, e4n.__class__
+ assert not exists, "e4n exists?"
+
+ class MyCalc:
+ def __init__(self, val):
+ self.max_drift = 0
+ class M:
+ def __init__(self, val):
+ self.val = val
+ def collect(self, args):
+ return reduce(lambda x, y: x+y, args)
+ def signature(self, executor):
+ return self.val + 222
+ self.module = M(val)
+
+ test.subdir('e5d')
+ test.write('e5f', "e5f\n")
+
+ def test_Entry_Entry_lookup(self):
+ """Test looking up an Entry within another Entry"""
+ self.fs.Entry('#topdir')
+ self.fs.Entry('#topdir/a/b/c')
+
+
+
+class FileTestCase(_tempdirTestCase):
+
+ def test_subclass(self):
+ """Test looking up subclass of File nodes"""
+ class FileSubclass(SCons.Node.FS.File):
+ pass
+ sd = self.fs._lookup('special_file', None, FileSubclass, create=1)
+ sd.must_be_same(SCons.Node.FS.File)
+
+ def test_Dirs(self):
+ """Test the File.Dirs() method"""
+ fff = self.fs.File('subdir/fff')
+ # This simulates that the SConscript file that defined
+ # fff is in subdir/.
+ fff.cwd = self.fs.Dir('subdir')
+ d1 = self.fs.Dir('subdir/d1')
+ d2 = self.fs.Dir('subdir/d2')
+ dirs = fff.Dirs(['d1', 'd2'])
+ assert dirs == [d1, d2], map(str, dirs)
+
+ def test_exists(self):
+ """Test the File.exists() method"""
+ fs = self.fs
+ test = self.test
+
+ src_f1 = fs.File('src/f1')
+ assert not src_f1.exists(), "%s apparently exists?" % src_f1
+
+ test.subdir('src')
+ test.write(['src', 'f1'], "src/f1\n")
+
+ assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1
+ src_f1.clear()
+ assert src_f1.exists(), "%s apparently does not exist?" % src_f1
+
+ test.subdir('build')
+ fs.VariantDir('build', 'src')
+ build_f1 = fs.File('build/f1')
+
+ assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
+ assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
+
+ test.unlink(['src', 'f1'])
+ src_f1.clear() # so the next exists() call will look on disk again
+
+ assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1
+ build_f1.clear()
+ build_f1.linked = None
+ assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
+ assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
+
+
+
+class GlobTestCase(_tempdirTestCase):
+ def setUp(self):
+ _tempdirTestCase.setUp(self)
+
+ fs = SCons.Node.FS.FS()
+ self.fs = fs
+
+ # Make entries on disk that will not have Nodes, so we can verify
+ # the behavior of looking for things on disk.
+ self.test.write('disk-bbb', "disk-bbb\n")
+ self.test.write('disk-aaa', "disk-aaa\n")
+ self.test.write('disk-ccc', "disk-ccc\n")
+ self.test.write('#disk-hash', "#disk-hash\n")
+ self.test.subdir('disk-sub')
+ self.test.write(['disk-sub', 'disk-ddd'], "disk-sub/disk-ddd\n")
+ self.test.write(['disk-sub', 'disk-eee'], "disk-sub/disk-eee\n")
+ self.test.write(['disk-sub', 'disk-fff'], "disk-sub/disk-fff\n")
+
+ # Make some entries that have both Nodes and on-disk entries,
+ # so we can verify what we do with
+ self.test.write('both-aaa', "both-aaa\n")
+ self.test.write('both-bbb', "both-bbb\n")
+ self.test.write('both-ccc', "both-ccc\n")
+ self.test.write('#both-hash', "#both-hash\n")
+ self.test.subdir('both-sub1')
+ self.test.write(['both-sub1', 'both-ddd'], "both-sub1/both-ddd\n")
+ self.test.write(['both-sub1', 'both-eee'], "both-sub1/both-eee\n")
+ self.test.write(['both-sub1', 'both-fff'], "both-sub1/both-fff\n")
+ self.test.subdir('both-sub2')
+ self.test.write(['both-sub2', 'both-ddd'], "both-sub2/both-ddd\n")
+ self.test.write(['both-sub2', 'both-eee'], "both-sub2/both-eee\n")
+ self.test.write(['both-sub2', 'both-fff'], "both-sub2/both-fff\n")
+
+ self.both_aaa = fs.File('both-aaa')
+ self.both_bbb = fs.File('both-bbb')
+ self.both_ccc = fs.File('both-ccc')
+ self._both_hash = fs.File('./#both-hash')
+ self.both_sub1 = fs.Dir('both-sub1')
+ self.both_sub1_both_ddd = self.both_sub1.File('both-ddd')
+ self.both_sub1_both_eee = self.both_sub1.File('both-eee')
+ self.both_sub1_both_fff = self.both_sub1.File('both-fff')
+ self.both_sub2 = fs.Dir('both-sub2')
+ self.both_sub2_both_ddd = self.both_sub2.File('both-ddd')
+ self.both_sub2_both_eee = self.both_sub2.File('both-eee')
+ self.both_sub2_both_fff = self.both_sub2.File('both-fff')
+
+ # Make various Nodes (that don't have on-disk entries) so we
+ # can verify how we match them.
+ self.ggg = fs.File('ggg')
+ self.hhh = fs.File('hhh')
+ self.iii = fs.File('iii')
+ self._hash = fs.File('./#hash')
+ self.subdir1 = fs.Dir('subdir1')
+ self.subdir1_lll = self.subdir1.File('lll')
+ self.subdir1_jjj = self.subdir1.File('jjj')
+ self.subdir1_kkk = self.subdir1.File('kkk')
+ self.subdir2 = fs.Dir('subdir2')
+ self.subdir2_lll = self.subdir2.File('lll')
+ self.subdir2_kkk = self.subdir2.File('kkk')
+ self.subdir2_jjj = self.subdir2.File('jjj')
+ self.sub = fs.Dir('sub')
+ self.sub_dir3 = self.sub.Dir('dir3')
+ self.sub_dir3_kkk = self.sub_dir3.File('kkk')
+ self.sub_dir3_jjj = self.sub_dir3.File('jjj')
+ self.sub_dir3_lll = self.sub_dir3.File('lll')
+
+
+ def do_cases(self, cases, **kwargs):
+
+ # First, execute all of the cases with string=True and verify
+ # that we get the expected strings returned. We do this first
+ # so the Glob() calls don't add Nodes to the self.fs file system
+ # hierarchy.
+
+ import copy
+ strings_kwargs = copy.copy(kwargs)
+ strings_kwargs['strings'] = True
+ for input, string_expect, node_expect in cases:
+ r = apply(self.fs.Glob, (input,), strings_kwargs)
+ r.sort()
+ assert r == string_expect, "Glob(%s, strings=True) expected %s, got %s" % (input, string_expect, r)
+
+ # Now execute all of the cases without string=True and look for
+ # the expected Nodes to be returned. If we don't have a list of
+ # actual expected Nodes, that means we're expecting a search for
+ # on-disk-only files to have returned some newly-created nodes.
+ # Verify those by running the list through str() before comparing
+ # them with the expected list of strings.
+ for input, string_expect, node_expect in cases:
+ r = apply(self.fs.Glob, (input,), kwargs)
+ if node_expect:
+ r.sort(lambda a,b: cmp(a.path, b.path))
+ result = []
+ for n in node_expect:
+ if type(n) == type(''):
+ n = self.fs.Entry(n)
+ result.append(n)
+ fmt = lambda n: "%s %s" % (repr(n), repr(str(n)))
+ else:
+ r = map(str, r)
+ r.sort()
+ result = string_expect
+ fmt = lambda n: n
+ if r != result:
+ import pprint
+ print "Glob(%s) expected:" % repr(input)
+ pprint.pprint(map(fmt, result))
+ print "Glob(%s) got:" % repr(input)
+ pprint.pprint(map(fmt, r))
+ self.fail()
+
+ def test_exact_match(self):
+ """Test globbing for exact Node matches"""
+ join = os.path.join
+
+ cases = (
+ ('ggg', ['ggg'], [self.ggg]),
+
+ ('subdir1', ['subdir1'], [self.subdir1]),
+
+ ('subdir1/jjj', [join('subdir1', 'jjj')], [self.subdir1_jjj]),
+
+ ('disk-aaa', ['disk-aaa'], None),
+
+ ('disk-sub', ['disk-sub'], None),
+
+ ('both-aaa', ['both-aaa'], []),
+ )
+
+ self.do_cases(cases)
+
+ def test_subdir_matches(self):
+ """Test globbing for exact Node matches in subdirectories"""
+ join = os.path.join
+
+ cases = (
+ ('*/jjj',
+ [join('subdir1', 'jjj'), join('subdir2', 'jjj')],
+ [self.subdir1_jjj, self.subdir2_jjj]),
+
+ ('*/disk-ddd',
+ [join('disk-sub', 'disk-ddd')],
+ None),
+ )
+
+ self.do_cases(cases)
+
+ def test_asterisk1(self):
+ """Test globbing for simple asterisk Node matches (1)"""
+ cases = (
+ ('h*',
+ ['hhh'],
+ [self.hhh]),
+
+ ('*',
+ ['#both-hash', '#hash',
+ 'both-aaa', 'both-bbb', 'both-ccc',
+ 'both-sub1', 'both-sub2',
+ 'ggg', 'hhh', 'iii',
+ 'sub', 'subdir1', 'subdir2'],
+ [self._both_hash, self._hash,
+ self.both_aaa, self.both_bbb, self.both_ccc, 'both-hash',
+ self.both_sub1, self.both_sub2,
+ self.ggg, 'hash', self.hhh, self.iii,
+ self.sub, self.subdir1, self.subdir2]),
+ )
+
+ self.do_cases(cases, ondisk=False)
+
+ def test_asterisk2(self):
+ """Test globbing for simple asterisk Node matches (2)"""
+ cases = (
+ ('disk-b*',
+ ['disk-bbb'],
+ None),
+
+ ('*',
+ ['#both-hash', '#disk-hash', '#hash',
+ 'both-aaa', 'both-bbb', 'both-ccc',
+ 'both-sub1', 'both-sub2',
+ 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub',
+ 'ggg', 'hhh', 'iii',
+ 'sub', 'subdir1', 'subdir2'],
+ ['./#both-hash', './#disk-hash', './#hash',
+ 'both-aaa', 'both-bbb', 'both-ccc', 'both-hash',
+ 'both-sub1', 'both-sub2',
+ 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub',
+ 'ggg', 'hash', 'hhh', 'iii',
+ 'sub', 'subdir1', 'subdir2']),
+ )
+
+ self.do_cases(cases)
+
+ def test_question_mark(self):
+ """Test globbing for simple question-mark Node matches"""
+ join = os.path.join
+
+ cases = (
+ ('ii?',
+ ['iii'],
+ [self.iii]),
+
+ ('both-sub?/both-eee',
+ [join('both-sub1', 'both-eee'), join('both-sub2', 'both-eee')],
+ [self.both_sub1_both_eee, self.both_sub2_both_eee]),
+
+ ('subdir?/jjj',
+ [join('subdir1', 'jjj'), join('subdir2', 'jjj')],
+ [self.subdir1_jjj, self.subdir2_jjj]),
+
+ ('disk-cc?',
+ ['disk-ccc'],
+ None),
+ )
+
+ self.do_cases(cases)
+
+ def test_does_not_exist(self):
+ """Test globbing for things that don't exist"""
+
+ cases = (
+ ('does_not_exist', [], []),
+ ('no_subdir/*', [], []),
+ ('subdir?/no_file', [], []),
+ )
+
+ self.do_cases(cases)
+
+ def test_subdir_asterisk(self):
+ """Test globbing for asterisk Node matches in subdirectories"""
+ join = os.path.join
+
+ cases = (
+ ('*/k*',
+ [join('subdir1', 'kkk'), join('subdir2', 'kkk')],
+ [self.subdir1_kkk, self.subdir2_kkk]),
+
+ ('both-sub?/*',
+ [join('both-sub1', 'both-ddd'),
+ join('both-sub1', 'both-eee'),
+ join('both-sub1', 'both-fff'),
+ join('both-sub2', 'both-ddd'),
+ join('both-sub2', 'both-eee'),
+ join('both-sub2', 'both-fff')],
+ [self.both_sub1_both_ddd,
+ self.both_sub1_both_eee,
+ self.both_sub1_both_fff,
+ self.both_sub2_both_ddd,
+ self.both_sub2_both_eee,
+ self.both_sub2_both_fff],
+ ),
+
+ ('subdir?/*',
+ [join('subdir1', 'jjj'),
+ join('subdir1', 'kkk'),
+ join('subdir1', 'lll'),
+ join('subdir2', 'jjj'),
+ join('subdir2', 'kkk'),
+ join('subdir2', 'lll')],
+ [self.subdir1_jjj, self.subdir1_kkk, self.subdir1_lll,
+ self.subdir2_jjj, self.subdir2_kkk, self.subdir2_lll]),
+
+ ('sub/*/*',
+ [join('sub', 'dir3', 'jjj'),
+ join('sub', 'dir3', 'kkk'),
+ join('sub', 'dir3', 'lll')],
+ [self.sub_dir3_jjj, self.sub_dir3_kkk, self.sub_dir3_lll]),
+
+ ('*/k*',
+ [join('subdir1', 'kkk'), join('subdir2', 'kkk')],
+ None),
+
+ ('subdir?/*',
+ [join('subdir1', 'jjj'),
+ join('subdir1', 'kkk'),
+ join('subdir1', 'lll'),
+ join('subdir2', 'jjj'),
+ join('subdir2', 'kkk'),
+ join('subdir2', 'lll')],
+ None),
+
+ ('sub/*/*',
+ [join('sub', 'dir3', 'jjj'),
+ join('sub', 'dir3', 'kkk'),
+ join('sub', 'dir3', 'lll')],
+ None),
+ )
+
+ self.do_cases(cases)
+
+ def test_subdir_question(self):
+ """Test globbing for question-mark Node matches in subdirectories"""
+ join = os.path.join
+
+ cases = (
+ ('*/?kk',
+ [join('subdir1', 'kkk'), join('subdir2', 'kkk')],
+ [self.subdir1_kkk, self.subdir2_kkk]),
+
+ ('subdir?/l?l',
+ [join('subdir1', 'lll'), join('subdir2', 'lll')],
+ [self.subdir1_lll, self.subdir2_lll]),
+
+ ('*/disk-?ff',
+ [join('disk-sub', 'disk-fff')],
+ None),
+
+ ('subdir?/l?l',
+ [join('subdir1', 'lll'), join('subdir2', 'lll')],
+ None),
+ )
+
+ self.do_cases(cases)
+
+ def test_sort(self):
+ """Test whether globbing sorts"""
+ join = os.path.join
+ # At least sometimes this should return out-of-order items
+ # if Glob doesn't sort.
+ # It's not a very good test though since it depends on the
+ # order returned by glob, which might already be sorted.
+ g = self.fs.Glob('disk-sub/*', strings=True)
+ expect = [
+ os.path.join('disk-sub', 'disk-ddd'),
+ os.path.join('disk-sub', 'disk-eee'),
+ os.path.join('disk-sub', 'disk-fff'),
+ ]
+ assert g == expect, str(g) + " is not sorted, but should be!"
+
+ g = self.fs.Glob('disk-*', strings=True)
+ expect = [ 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub' ]
+ assert g == expect, str(g) + " is not sorted, but should be!"
+
+
+class RepositoryTestCase(_tempdirTestCase):
+
+ def setUp(self):
+ _tempdirTestCase.setUp(self)
+
+ self.test.subdir('rep1', 'rep2', 'rep3', 'work')
+
+ self.rep1 = self.test.workpath('rep1')
+ self.rep2 = self.test.workpath('rep2')
+ self.rep3 = self.test.workpath('rep3')
+
+ os.chdir(self.test.workpath('work'))
+
+ self.fs = SCons.Node.FS.FS()
+ self.fs.Repository(self.rep1, self.rep2, self.rep3)
+
+ def test_getRepositories(self):
+ """Test the Dir.getRepositories() method"""
+ self.fs.Repository('foo')
+ self.fs.Repository(os.path.join('foo', 'bar'))
+ self.fs.Repository('bar/foo')
+ self.fs.Repository('bar')
+
+ expect = [
+ self.rep1,
+ self.rep2,
+ self.rep3,
+ 'foo',
+ os.path.join('foo', 'bar'),
+ os.path.join('bar', 'foo'),
+ 'bar'
+ ]
+
+ rep = self.fs.Dir('#').getRepositories()
+ r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
+ assert r == expect, r
+
+ def test_get_all_rdirs(self):
+ """Test the Dir.get_all_rdirs() method"""
+ self.fs.Repository('foo')
+ self.fs.Repository(os.path.join('foo', 'bar'))
+ self.fs.Repository('bar/foo')
+ self.fs.Repository('bar')
+
+ expect = [
+ '.',
+ self.rep1,
+ self.rep2,
+ self.rep3,
+ 'foo',
+ os.path.join('foo', 'bar'),
+ os.path.join('bar', 'foo'),
+ 'bar'
+ ]
+
+ rep = self.fs.Dir('#').get_all_rdirs()
+ r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
+ assert r == expect, r
+
+ def test_rentry(self):
+ """Test the Base.entry() method"""
+ return_true = lambda: 1
+ return_false = lambda: 0
+
+ d1 = self.fs.Dir('d1')
+ d2 = self.fs.Dir('d2')
+ d3 = self.fs.Dir('d3')
+
+ e1 = self.fs.Entry('e1')
+ e2 = self.fs.Entry('e2')
+ e3 = self.fs.Entry('e3')
+
+ f1 = self.fs.File('f1')
+ f2 = self.fs.File('f2')
+ f3 = self.fs.File('f3')
+
+ self.test.write([self.rep1, 'd2'], "")
+ self.test.subdir([self.rep2, 'd3'])
+ self.test.write([self.rep3, 'd3'], "")
+
+ self.test.write([self.rep1, 'e2'], "")
+ self.test.subdir([self.rep2, 'e3'])
+ self.test.write([self.rep3, 'e3'], "")
+
+ self.test.write([self.rep1, 'f2'], "")
+ self.test.subdir([self.rep2, 'f3'])
+ self.test.write([self.rep3, 'f3'], "")
+
+ r = d1.rentry()
+ assert r is d1, r
+
+ r = d2.rentry()
+ assert not r is d2, r
+ r = str(r)
+ assert r == os.path.join(self.rep1, 'd2'), r
+
+ r = d3.rentry()
+ assert not r is d3, r
+ r = str(r)
+ assert r == os.path.join(self.rep2, 'd3'), r
+
+ r = e1.rentry()
+ assert r is e1, r
+
+ r = e2.rentry()
+ assert not r is e2, r
+ r = str(r)
+ assert r == os.path.join(self.rep1, 'e2'), r
+
+ r = e3.rentry()
+ assert not r is e3, r
+ r = str(r)
+ assert r == os.path.join(self.rep2, 'e3'), r
+
+ r = f1.rentry()
+ assert r is f1, r
+
+ r = f2.rentry()
+ assert not r is f2, r
+ r = str(r)
+ assert r == os.path.join(self.rep1, 'f2'), r
+
+ r = f3.rentry()
+ assert not r is f3, r
+ r = str(r)
+ assert r == os.path.join(self.rep2, 'f3'), r
+
+ def test_rdir(self):
+ """Test the Dir.rdir() method"""
+ return_true = lambda: 1
+ return_false = lambda: 0
+
+ d1 = self.fs.Dir('d1')
+ d2 = self.fs.Dir('d2')
+ d3 = self.fs.Dir('d3')
+
+ self.test.subdir([self.rep1, 'd2'])
+ self.test.write([self.rep2, 'd3'], "")
+ self.test.subdir([self.rep3, 'd3'])
+
+ r = d1.rdir()
+ assert r is d1, r
+
+ r = d2.rdir()
+ assert not r is d2, r
+ r = str(r)
+ assert r == os.path.join(self.rep1, 'd2'), r
+
+ r = d3.rdir()
+ assert not r is d3, r
+ r = str(r)
+ assert r == os.path.join(self.rep3, 'd3'), r
+
+ e1 = self.fs.Dir('e1')
+ e1.exists = return_false
+ e2 = self.fs.Dir('e2')
+ e2.exists = return_false
+
+ # Make sure we match entries in repositories,
+ # regardless of whether they're derived or not.
+
+ re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
+ re1.exists = return_true
+ re1.is_derived = return_true
+ re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
+ re2.exists = return_true
+ re2.is_derived = return_false
+
+ r = e1.rdir()
+ assert r is re1, r
+
+ r = e2.rdir()
+ assert r is re2, r
+
+ def test_rfile(self):
+ """Test the File.rfile() method"""
+ return_true = lambda: 1
+ return_false = lambda: 0
+
+ f1 = self.fs.File('f1')
+ f2 = self.fs.File('f2')
+ f3 = self.fs.File('f3')
+
+ self.test.write([self.rep1, 'f2'], "")
+ self.test.subdir([self.rep2, 'f3'])
+ self.test.write([self.rep3, 'f3'], "")
+
+ r = f1.rfile()
+ assert r is f1, r
+
+ r = f2.rfile()
+ assert not r is f2, r
+ r = str(r)
+ assert r == os.path.join(self.rep1, 'f2'), r
+
+ r = f3.rfile()
+ assert not r is f3, r
+ r = f3.rstr()
+ assert r == os.path.join(self.rep3, 'f3'), r
+
+ e1 = self.fs.File('e1')
+ e1.exists = return_false
+ e2 = self.fs.File('e2')
+ e2.exists = return_false
+
+ # Make sure we match entries in repositories,
+ # regardless of whether they're derived or not.
+
+ re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
+ re1.exists = return_true
+ re1.is_derived = return_true
+ re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
+ re2.exists = return_true
+ re2.is_derived = return_false
+
+ r = e1.rfile()
+ assert r is re1, r
+
+ r = e2.rfile()
+ assert r is re2, r
+
+ def test_Rfindalldirs(self):
+ """Test the Rfindalldirs() methods"""
+ fs = self.fs
+ test = self.test
+
+ d1 = fs.Dir('d1')
+ d2 = fs.Dir('d2')
+ rep1_d1 = fs.Dir(test.workpath('rep1', 'd1'))
+ rep2_d1 = fs.Dir(test.workpath('rep2', 'd1'))
+ rep3_d1 = fs.Dir(test.workpath('rep3', 'd1'))
+ sub = fs.Dir('sub')
+ sub_d1 = sub.Dir('d1')
+ rep1_sub_d1 = fs.Dir(test.workpath('rep1', 'sub', 'd1'))
+ rep2_sub_d1 = fs.Dir(test.workpath('rep2', 'sub', 'd1'))
+ rep3_sub_d1 = fs.Dir(test.workpath('rep3', 'sub', 'd1'))
+
+ r = fs.Top.Rfindalldirs((d1,))
+ assert r == [d1], map(str, r)
+
+ r = fs.Top.Rfindalldirs((d1, d2))
+ assert r == [d1, d2], map(str, r)
+
+ r = fs.Top.Rfindalldirs(('d1',))
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
+
+ r = fs.Top.Rfindalldirs(('#d1',))
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
+
+ r = sub.Rfindalldirs(('d1',))
+ assert r == [sub_d1, rep1_sub_d1, rep2_sub_d1, rep3_sub_d1], map(str, r)
+
+ r = sub.Rfindalldirs(('#d1',))
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
+
+ r = fs.Top.Rfindalldirs(('d1', d2))
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1, d2], map(str, r)
+
+ def test_rexists(self):
+ """Test the Entry.rexists() method"""
+ fs = self.fs
+ test = self.test
+
+ test.write([self.rep1, 'f2'], "")
+ test.write([self.rep2, "i_exist"], "\n")
+ test.write(["work", "i_exist_too"], "\n")
+
+ fs.VariantDir('build', '.')
+
+ f = fs.File(test.workpath("work", "i_do_not_exist"))
+ assert not f.rexists()
+
+ f = fs.File(test.workpath("work", "i_exist"))
+ assert f.rexists()
+
+ f = fs.File(test.workpath("work", "i_exist_too"))
+ assert f.rexists()
+
+ f1 = fs.File(os.path.join('build', 'f1'))
+ assert not f1.rexists()
+
+ f2 = fs.File(os.path.join('build', 'f2'))
+ assert f2.rexists()
+
+ def test_FAT_timestamps(self):
+ """Test repository timestamps on FAT file systems"""
+ fs = self.fs
+ test = self.test
+
+ test.write(["rep2", "tstamp"], "tstamp\n")
+ try:
+ # Okay, *this* manipulation accomodates Windows FAT file systems
+ # that only have two-second granularity on their timestamps.
+ # We round down the current time to the nearest even integer
+ # value, subtract two to make sure the timestamp is not "now,"
+ # and then convert it back to a float.
+ tstamp = float(int(time.time() / 2) * 2) - 2
+ os.utime(test.workpath("rep2", "tstamp"), (tstamp - 2.0, tstamp))
+ f = fs.File("tstamp")
+ t = f.get_timestamp()
+ assert t == tstamp, "expected %f, got %f" % (tstamp, t)
+ finally:
+ test.unlink(["rep2", "tstamp"])
+
+ def test_get_contents(self):
+ """Ensure get_contents() returns binary contents from Repositories"""
+ fs = self.fs
+ test = self.test
+
+ test.write(["rep3", "contents"], "Con\x1aTents\n")
+ try:
+ c = fs.File("contents").get_contents()
+ assert c == "Con\x1aTents\n", "got '%s'" % c
+ finally:
+ test.unlink(["rep3", "contents"])
+
+ def test_get_text_contents(self):
+ """Ensure get_text_contents() returns text contents from
+ Repositories"""
+ fs = self.fs
+ test = self.test
+
+ # Use a test string that has a file terminator in it to make
+ # sure we read the entire file, regardless of its contents.
+ try:
+ eval('test_string = u"Con\x1aTents\n"')
+ except SyntaxError:
+ import UserString
+ class FakeUnicodeString(UserString.UserString):
+ def encode(self, encoding):
+ return str(self)
+ test_string = FakeUnicodeString("Con\x1aTents\n")
+
+
+ # Test with ASCII.
+ test.write(["rep3", "contents"], test_string.encode('ascii'))
+ try:
+ c = fs.File("contents").get_text_contents()
+ assert test_string == c, "got %s" % repr(c)
+ finally:
+ test.unlink(["rep3", "contents"])
+
+ # Test with utf-8
+ test.write(["rep3", "contents"], test_string.encode('utf-8'))
+ try:
+ c = fs.File("contents").get_text_contents()
+ assert test_string == c, "got %s" % repr(c)
+ finally:
+ test.unlink(["rep3", "contents"])
+
+ # Test with utf-16
+ test.write(["rep3", "contents"], test_string.encode('utf-16'))
+ try:
+ c = fs.File("contents").get_text_contents()
+ assert test_string == c, "got %s" % repr(c)
+ finally:
+ test.unlink(["rep3", "contents"])
+
+ #def test_is_up_to_date(self):
+
+
+
+class find_fileTestCase(unittest.TestCase):
+ def runTest(self):
+ """Testing find_file function"""
+ test = TestCmd(workdir = '')
+ test.write('./foo', 'Some file\n')
+ test.write('./foo2', 'Another file\n')
+ test.subdir('same')
+ test.subdir('bar')
+ test.write(['bar', 'on_disk'], 'Another file\n')
+ test.write(['bar', 'same'], 'bar/same\n')
+
+ fs = SCons.Node.FS.FS(test.workpath(""))
+ # FS doesn't like the cwd to be something other than its root.
+ os.chdir(test.workpath(""))
+
+ node_derived = fs.File(test.workpath('bar/baz'))
+ node_derived.builder_set(1) # Any non-zero value.
+ node_pseudo = fs.File(test.workpath('pseudo'))
+ node_pseudo.set_src_builder(1) # Any non-zero value.
+
+ paths = tuple(map(fs.Dir, ['.', 'same', './bar']))
+ nodes = [SCons.Node.FS.find_file('foo', paths)]
+ nodes.append(SCons.Node.FS.find_file('baz', paths))
+ nodes.append(SCons.Node.FS.find_file('pseudo', paths))
+ nodes.append(SCons.Node.FS.find_file('same', paths))
+
+ file_names = map(str, nodes)
+ file_names = map(os.path.normpath, file_names)
+ expect = ['./foo', './bar/baz', './pseudo', './bar/same']
+ expect = map(os.path.normpath, expect)
+ assert file_names == expect, file_names
+
+ # Make sure we don't blow up if there's already a File in place
+ # of a directory that we'd otherwise try to search. If this
+ # is broken, we'll see an exception like "Tried to lookup File
+ # 'bar/baz' as a Dir.
+ SCons.Node.FS.find_file('baz/no_file_here', paths)
+
+ import StringIO
+ save_sys_stdout = sys.stdout
+
+ try:
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ SCons.Node.FS.find_file('foo2', paths, verbose="xyz")
+ expect = " xyz: looking for 'foo2' in '.' ...\n" + \
+ " xyz: ... FOUND 'foo2' in '.'\n"
+ c = sio.getvalue()
+ assert c == expect, c
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ SCons.Node.FS.find_file('baz2', paths, verbose=1)
+ expect = " find_file: looking for 'baz2' in '.' ...\n" + \
+ " find_file: looking for 'baz2' in 'same' ...\n" + \
+ " find_file: looking for 'baz2' in 'bar' ...\n"
+ c = sio.getvalue()
+ assert c == expect, c
+
+ sio = StringIO.StringIO()
+ sys.stdout = sio
+ SCons.Node.FS.find_file('on_disk', paths, verbose=1)
+ expect = " find_file: looking for 'on_disk' in '.' ...\n" + \
+ " find_file: looking for 'on_disk' in 'same' ...\n" + \
+ " find_file: looking for 'on_disk' in 'bar' ...\n" + \
+ " find_file: ... FOUND 'on_disk' in 'bar'\n"
+ c = sio.getvalue()
+ assert c == expect, c
+ finally:
+ sys.stdout = save_sys_stdout
+
+class StringDirTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test using a string as the second argument of
+ File() and Dir()"""
+
+ test = TestCmd(workdir = '')
+ test.subdir('sub')
+ fs = SCons.Node.FS.FS(test.workpath(''))
+
+ d = fs.Dir('sub', '.')
+ assert str(d) == 'sub', str(d)
+ assert d.exists()
+ f = fs.File('file', 'sub')
+ assert str(f) == os.path.join('sub', 'file')
+ assert not f.exists()
+
+class stored_infoTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test how we store build information"""
+ test = TestCmd(workdir = '')
+ test.subdir('sub')
+ fs = SCons.Node.FS.FS(test.workpath(''))
+
+ d = fs.Dir('sub')
+ f = fs.File('file1', d)
+ bi = f.get_stored_info()
+ assert hasattr(bi, 'ninfo')
+
+ class MySConsign:
+ class Null:
+ def __init__(self):
+ self.xyzzy = 7
+ def get_entry(self, name):
+ return self.Null()
+
+ f = fs.File('file2', d)
+ f.dir.sconsign = MySConsign
+ bi = f.get_stored_info()
+ assert bi.xyzzy == 7, bi
+
+class has_src_builderTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test the has_src_builder() method"""
+ test = TestCmd(workdir = '')
+ fs = SCons.Node.FS.FS(test.workpath(''))
+ os.chdir(test.workpath(''))
+ test.subdir('sub1')
+ test.subdir('sub2', ['sub2', 'SCCS'], ['sub2', 'RCS'])
+
+ sub1 = fs.Dir('sub1', '.')
+ f1 = fs.File('f1', sub1)
+ f2 = fs.File('f2', sub1)
+ f3 = fs.File('f3', sub1)
+ sub2 = fs.Dir('sub2', '.')
+ f4 = fs.File('f4', sub2)
+ f5 = fs.File('f5', sub2)
+ f6 = fs.File('f6', sub2)
+ f7 = fs.File('f7', sub2)
+ f8 = fs.File('f8', sub2)
+
+ h = f1.has_src_builder()
+ assert not h, h
+ h = f1.has_builder()
+ assert not h, h
+
+ b1 = Builder(fs.File)
+ sub1.set_src_builder(b1)
+
+ test.write(['sub1', 'f2'], "sub1/f2\n")
+ h = f1.has_src_builder() # cached from previous call
+ assert not h, h
+ h = f1.has_builder() # cached from previous call
+ assert not h, h
+ h = f2.has_src_builder()
+ assert not h, h
+ h = f2.has_builder()
+ assert not h, h
+ h = f3.has_src_builder()
+ assert h, h
+ h = f3.has_builder()
+ assert h, h
+ assert f3.builder is b1, f3.builder
+
+ f7.set_src_builder(b1)
+ f8.builder_set(b1)
+
+ test.write(['sub2', 'SCCS', 's.f5'], "sub2/SCCS/s.f5\n")
+ test.write(['sub2', 'RCS', 'f6,v'], "sub2/RCS/f6,v\n")
+ h = f4.has_src_builder()
+ assert not h, h
+ h = f4.has_builder()
+ assert not h, h
+ h = f5.has_src_builder()
+ assert h, h
+ h = f5.has_builder()
+ assert h, h
+ h = f6.has_src_builder()
+ assert h, h
+ h = f6.has_builder()
+ assert h, h
+ h = f7.has_src_builder()
+ assert h, h
+ h = f7.has_builder()
+ assert h, h
+ h = f8.has_src_builder()
+ assert not h, h
+ h = f8.has_builder()
+ assert h, h
+
+class prepareTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test the prepare() method"""
+
+ class MyFile(SCons.Node.FS.File):
+ def _createDir(self, update=None):
+ raise SCons.Errors.StopError
+ def exists(self):
+ return None
+
+ fs = SCons.Node.FS.FS()
+ file = MyFile('foo', fs.Dir('.'), fs)
+
+ exc_caught = 0
+ try:
+ file.prepare()
+ except SCons.Errors.StopError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a StopError."
+
+ class MkdirAction(Action):
+ def __init__(self, dir_made):
+ self.dir_made = dir_made
+ def __call__(self, target, source, env, executor=None):
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ self.dir_made.extend(target)
+
+ dir_made = []
+ new_dir = fs.Dir("new_dir")
+ new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
+ new_dir.reset_executor()
+ xyz = fs.File(os.path.join("new_dir", "xyz"))
+
+ xyz.set_state(SCons.Node.up_to_date)
+ xyz.prepare()
+ assert dir_made == [], dir_made
+
+ xyz.set_state(0)
+ xyz.prepare()
+ assert dir_made[0].path == "new_dir", dir_made[0]
+
+ dir = fs.Dir("dir")
+ dir.prepare()
+
+
+
+class SConstruct_dirTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test setting the SConstruct directory"""
+
+ fs = SCons.Node.FS.FS()
+ fs.set_SConstruct_dir(fs.Dir('xxx'))
+ assert fs.SConstruct_dir.path == 'xxx'
+
+
+
+class CacheDirTestCase(unittest.TestCase):
+
+ def test_get_cachedir_csig(self):
+ fs = SCons.Node.FS.FS()
+
+ f9 = fs.File('f9')
+ r = f9.get_cachedir_csig()
+ assert r == 'd41d8cd98f00b204e9800998ecf8427e', r
+
+
+
+class clearTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test clearing FS nodes of cached data."""
+ fs = SCons.Node.FS.FS()
+ test = TestCmd(workdir='')
+
+ e = fs.Entry('e')
+ assert not e.exists()
+ assert not e.rexists()
+ assert str(e) == 'e', str(d)
+ e.clear()
+ assert not e.exists()
+ assert not e.rexists()
+ assert str(e) == 'e', str(d)
+
+ d = fs.Dir(test.workpath('d'))
+ test.subdir('d')
+ assert d.exists()
+ assert d.rexists()
+ assert str(d) == test.workpath('d'), str(d)
+ fs.rename(test.workpath('d'), test.workpath('gone'))
+ # Verify caching is active
+ assert d.exists(), 'caching not active'
+ assert d.rexists()
+ assert str(d) == test.workpath('d'), str(d)
+ # Now verify clear() resets the cache
+ d.clear()
+ assert not d.exists()
+ assert not d.rexists()
+ assert str(d) == test.workpath('d'), str(d)
+
+ f = fs.File(test.workpath('f'))
+ test.write(test.workpath('f'), 'file f')
+ assert f.exists()
+ assert f.rexists()
+ assert str(f) == test.workpath('f'), str(f)
+ # Verify caching is active
+ test.unlink(test.workpath('f'))
+ assert f.exists()
+ assert f.rexists()
+ assert str(f) == test.workpath('f'), str(f)
+ # Now verify clear() resets the cache
+ f.clear()
+ assert not f.exists()
+ assert not f.rexists()
+ assert str(f) == test.workpath('f'), str(f)
+
+
+
+class disambiguateTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test calling the disambiguate() method."""
+ test = TestCmd(workdir='')
+
+ fs = SCons.Node.FS.FS()
+
+ ddd = fs.Dir('ddd')
+ d = ddd.disambiguate()
+ assert d is ddd, d
+
+ fff = fs.File('fff')
+ f = fff.disambiguate()
+ assert f is fff, f
+
+ test.subdir('edir')
+ test.write('efile', "efile\n")
+
+ edir = fs.Entry(test.workpath('edir'))
+ d = edir.disambiguate()
+ assert d.__class__ is ddd.__class__, d.__class__
+
+ efile = fs.Entry(test.workpath('efile'))
+ f = efile.disambiguate()
+ assert f.__class__ is fff.__class__, f.__class__
+
+ test.subdir('build')
+ test.subdir(['build', 'bdir'])
+ test.write(['build', 'bfile'], "build/bfile\n")
+
+ test.subdir('src')
+ test.write(['src', 'bdir'], "src/bdir\n")
+ test.subdir(['src', 'bfile'])
+
+ test.subdir(['src', 'edir'])
+ test.write(['src', 'efile'], "src/efile\n")
+
+ fs.VariantDir(test.workpath('build'), test.workpath('src'))
+
+ build_bdir = fs.Entry(test.workpath('build/bdir'))
+ d = build_bdir.disambiguate()
+ assert d is build_bdir, d
+ assert d.__class__ is ddd.__class__, d.__class__
+
+ build_bfile = fs.Entry(test.workpath('build/bfile'))
+ f = build_bfile.disambiguate()
+ assert f is build_bfile, f
+ assert f.__class__ is fff.__class__, f.__class__
+
+ build_edir = fs.Entry(test.workpath('build/edir'))
+ d = build_edir.disambiguate()
+ assert d.__class__ is ddd.__class__, d.__class__
+
+ build_efile = fs.Entry(test.workpath('build/efile'))
+ f = build_efile.disambiguate()
+ assert f.__class__ is fff.__class__, f.__class__
+
+ build_nonexistant = fs.Entry(test.workpath('build/nonexistant'))
+ f = build_nonexistant.disambiguate()
+ assert f.__class__ is fff.__class__, f.__class__
+
+class postprocessTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test calling the postprocess() method."""
+ fs = SCons.Node.FS.FS()
+
+ e = fs.Entry('e')
+ e.postprocess()
+
+ d = fs.Dir('d')
+ d.postprocess()
+
+ f = fs.File('f')
+ f.postprocess()
+
+
+
+class SpecialAttrTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test special attributes of file nodes."""
+ test=TestCmd(workdir='')
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+
+ f = fs.Entry('foo/bar/baz.blat').get_subst_proxy()
+
+ s = str(f.dir)
+ assert s == os.path.normpath('foo/bar'), s
+ assert f.dir.is_literal(), f.dir
+ for_sig = f.dir.for_signature()
+ assert for_sig == 'bar', for_sig
+
+ s = str(f.file)
+ assert s == 'baz.blat', s
+ assert f.file.is_literal(), f.file
+ for_sig = f.file.for_signature()
+ assert for_sig == 'baz.blat_file', for_sig
+
+ s = str(f.base)
+ assert s == os.path.normpath('foo/bar/baz'), s
+ assert f.base.is_literal(), f.base
+ for_sig = f.base.for_signature()
+ assert for_sig == 'baz.blat_base', for_sig
+
+ s = str(f.filebase)
+ assert s == 'baz', s
+ assert f.filebase.is_literal(), f.filebase
+ for_sig = f.filebase.for_signature()
+ assert for_sig == 'baz.blat_filebase', for_sig
+
+ s = str(f.suffix)
+ assert s == '.blat', s
+ assert f.suffix.is_literal(), f.suffix
+ for_sig = f.suffix.for_signature()
+ assert for_sig == 'baz.blat_suffix', for_sig
+
+ s = str(f.abspath)
+ assert s == test.workpath('work', 'foo', 'bar', 'baz.blat'), s
+ assert f.abspath.is_literal(), f.abspath
+ for_sig = f.abspath.for_signature()
+ assert for_sig == 'baz.blat_abspath', for_sig
+
+ s = str(f.posix)
+ assert s == 'foo/bar/baz.blat', s
+ assert f.posix.is_literal(), f.posix
+ if f.posix != f:
+ for_sig = f.posix.for_signature()
+ assert for_sig == 'baz.blat_posix', for_sig
+
+ s = str(f.windows)
+ assert s == 'foo\\bar\\baz.blat', repr(s)
+ assert f.windows.is_literal(), f.windows
+ if f.windows != f:
+ for_sig = f.windows.for_signature()
+ assert for_sig == 'baz.blat_windows', for_sig
+
+ # Deprecated synonym for the .windows suffix.
+ s = str(f.win32)
+ assert s == 'foo\\bar\\baz.blat', repr(s)
+ assert f.win32.is_literal(), f.win32
+ if f.win32 != f:
+ for_sig = f.win32.for_signature()
+ assert for_sig == 'baz.blat_windows', for_sig
+
+ # And now, combinations!!!
+ s = str(f.srcpath.base)
+ assert s == os.path.normpath('foo/bar/baz'), s
+ s = str(f.srcpath.dir)
+ assert s == str(f.srcdir), s
+ s = str(f.srcpath.posix)
+ assert s == 'foo/bar/baz.blat', s
+ s = str(f.srcpath.windows)
+ assert s == 'foo\\bar\\baz.blat', s
+ s = str(f.srcpath.win32)
+ assert s == 'foo\\bar\\baz.blat', s
+
+ # Test what happens with VariantDir()
+ fs.VariantDir('foo', 'baz')
+
+ s = str(f.srcpath)
+ assert s == os.path.normpath('baz/bar/baz.blat'), s
+ assert f.srcpath.is_literal(), f.srcpath
+ g = f.srcpath.get()
+ assert isinstance(g, SCons.Node.FS.File), g.__class__
+
+ s = str(f.srcdir)
+ assert s == os.path.normpath('baz/bar'), s
+ assert f.srcdir.is_literal(), f.srcdir
+ g = f.srcdir.get()
+ assert isinstance(g, SCons.Node.FS.Dir), g.__class__
+
+ # And now what happens with VariantDir() + Repository()
+ fs.Repository(test.workpath('repository'))
+
+ f = fs.Entry('foo/sub/file.suffix').get_subst_proxy()
+ test.subdir('repository',
+ ['repository', 'baz'],
+ ['repository', 'baz', 'sub'])
+
+ rd = test.workpath('repository', 'baz', 'sub')
+ rf = test.workpath('repository', 'baz', 'sub', 'file.suffix')
+ test.write(rf, "\n")
+
+ s = str(f.srcpath)
+ assert s == os.path.normpath('baz/sub/file.suffix'), s
+ assert f.srcpath.is_literal(), f.srcpath
+ g = f.srcpath.get()
+ # Gets disambiguated to SCons.Node.FS.File by get_subst_proxy().
+ assert isinstance(g, SCons.Node.FS.File), g.__class__
+
+ s = str(f.srcdir)
+ assert s == os.path.normpath('baz/sub'), s
+ assert f.srcdir.is_literal(), f.srcdir
+ g = f.srcdir.get()
+ assert isinstance(g, SCons.Node.FS.Dir), g.__class__
+
+ s = str(f.rsrcpath)
+ assert s == rf, s
+ assert f.rsrcpath.is_literal(), f.rsrcpath
+ g = f.rsrcpath.get()
+ assert isinstance(g, SCons.Node.FS.File), g.__class__
+
+ s = str(f.rsrcdir)
+ assert s == rd, s
+ assert f.rsrcdir.is_literal(), f.rsrcdir
+ g = f.rsrcdir.get()
+ assert isinstance(g, SCons.Node.FS.Dir), g.__class__
+
+ # Check that attempts to access non-existent attributes of the
+ # subst proxy generate the right exceptions and messages.
+ caught = None
+ try:
+ fs.Dir('ddd').get_subst_proxy().no_such_attr
+ except AttributeError, e:
+ assert str(e) == "Dir instance 'ddd' has no attribute 'no_such_attr'", e
+ caught = 1
+ assert caught, "did not catch expected AttributeError"
+
+ caught = None
+ try:
+ fs.Entry('eee').get_subst_proxy().no_such_attr
+ except AttributeError, e:
+ # Gets disambiguated to File instance by get_subst_proxy().
+ assert str(e) == "File instance 'eee' has no attribute 'no_such_attr'", e
+ caught = 1
+ assert caught, "did not catch expected AttributeError"
+
+ caught = None
+ try:
+ fs.File('fff').get_subst_proxy().no_such_attr
+ except AttributeError, e:
+ assert str(e) == "File instance 'fff' has no attribute 'no_such_attr'", e
+ caught = 1
+ assert caught, "did not catch expected AttributeError"
+
+
+
+class SaveStringsTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test caching string values of nodes."""
+ test=TestCmd(workdir='')
+
+ def setup(fs):
+ fs.Dir('src')
+ fs.Dir('d0')
+ fs.Dir('d1')
+
+ d0_f = fs.File('d0/f')
+ d1_f = fs.File('d1/f')
+ d0_b = fs.File('d0/b')
+ d1_b = fs.File('d1/b')
+ d1_f.duplicate = 1
+ d1_b.duplicate = 1
+ d0_b.builder = 1
+ d1_b.builder = 1
+
+ return [d0_f, d1_f, d0_b, d1_b]
+
+ def modify(nodes):
+ d0_f, d1_f, d0_b, d1_b = nodes
+ d1_f.duplicate = 0
+ d1_b.duplicate = 0
+ d0_b.builder = 0
+ d1_b.builder = 0
+
+ fs1 = SCons.Node.FS.FS(test.workpath('fs1'))
+ nodes = setup(fs1)
+ fs1.VariantDir('d0', 'src', duplicate=0)
+ fs1.VariantDir('d1', 'src', duplicate=1)
+
+ s = map(str, nodes)
+ expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])
+ assert s == expect, s
+
+ modify(nodes)
+
+ s = map(str, nodes)
+ expect = map(os.path.normpath, ['src/f', 'src/f', 'd0/b', 'd1/b'])
+ assert s == expect, s
+
+ SCons.Node.FS.save_strings(1)
+ fs2 = SCons.Node.FS.FS(test.workpath('fs2'))
+ nodes = setup(fs2)
+ fs2.VariantDir('d0', 'src', duplicate=0)
+ fs2.VariantDir('d1', 'src', duplicate=1)
+
+ s = map(str, nodes)
+ expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])
+ assert s == expect, s
+
+ modify(nodes)
+
+ s = map(str, nodes)
+ expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])
+ assert s == expect, 'node str() not cached: %s'%s
+
+
+class AbsolutePathTestCase(unittest.TestCase):
+ def test_root_lookup_equivalence(self):
+ """Test looking up /fff vs. fff in the / directory"""
+ test=TestCmd(workdir='')
+
+ fs = SCons.Node.FS.FS('/')
+
+ save_cwd = os.getcwd()
+ try:
+ os.chdir('/')
+ fff1 = fs.File('fff')
+ fff2 = fs.File('/fff')
+ assert fff1 is fff2, "fff and /fff returned different Nodes!"
+ finally:
+ os.chdir(save_cwd)
+
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ suite.addTest(VariantDirTestCase())
+ suite.addTest(find_fileTestCase())
+ suite.addTest(StringDirTestCase())
+ suite.addTest(stored_infoTestCase())
+ suite.addTest(has_src_builderTestCase())
+ suite.addTest(prepareTestCase())
+ suite.addTest(SConstruct_dirTestCase())
+ suite.addTest(clearTestCase())
+ suite.addTest(disambiguateTestCase())
+ suite.addTest(postprocessTestCase())
+ suite.addTest(SpecialAttrTestCase())
+ suite.addTest(SaveStringsTestCase())
+ tclasses = [
+ AbsolutePathTestCase,
+ BaseTestCase,
+ CacheDirTestCase,
+ DirTestCase,
+ DirBuildInfoTestCase,
+ DirNodeInfoTestCase,
+ EntryTestCase,
+ FileTestCase,
+ FileBuildInfoTestCase,
+ FileNodeInfoTestCase,
+ FSTestCase,
+ GlobTestCase,
+ RepositoryTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
new file mode 100644
index 0000000..7920dcb
--- /dev/null
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -0,0 +1,1317 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/NodeTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import re
+import string
+import sys
+import types
+import unittest
+import UserList
+
+import SCons.Errors
+import SCons.Node
+import SCons.Util
+
+
+
+built_it = None
+built_target = None
+built_source = None
+cycle_detected = None
+built_order = 0
+
+def _actionAppend(a1, a2):
+ all = []
+ for curr_a in [a1, a2]:
+ if isinstance(curr_a, MyAction):
+ all.append(curr_a)
+ elif isinstance(curr_a, MyListAction):
+ all.extend(curr_a.list)
+ elif type(curr_a) == type([1,2]):
+ all.extend(curr_a)
+ else:
+ raise 'Cannot Combine Actions'
+ return MyListAction(all)
+
+class MyActionBase:
+ def __add__(self, other):
+ return _actionAppend(self, other)
+
+ def __radd__(self, other):
+ return _actionAppend(other, self)
+
+class MyAction(MyActionBase):
+ def __init__(self):
+ self.order = 0
+
+ def __call__(self, target, source, env, executor=None):
+ global built_it, built_target, built_source, built_args, built_order
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ built_it = 1
+ built_target = target
+ built_source = source
+ built_args = env
+ built_order = built_order + 1
+ self.order = built_order
+ return 0
+
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class MyExecutor:
+ def __init__(self, env=None, targets=[], sources=[]):
+ self.env = env
+ self.targets = targets
+ self.sources = sources
+ def get_build_env(self):
+ return self.env
+ def get_build_scanner_path(self, scanner):
+ return 'executor would call %s' % scanner
+ def cleanup(self):
+ self.cleaned_up = 1
+ def scan_targets(self, scanner):
+ if not scanner:
+ return
+ d = scanner(self.targets)
+ for t in self.targets:
+ t.implicit.extend(d)
+ def scan_sources(self, scanner):
+ if not scanner:
+ return
+ d = scanner(self.sources)
+ for t in self.targets:
+ t.implicit.extend(d)
+
+class MyListAction(MyActionBase):
+ def __init__(self, list):
+ self.list = list
+ def __call__(self, target, source, env):
+ for A in self.list:
+ A(target, source, env)
+
+class Environment:
+ def __init__(self, **kw):
+ self._dict = {}
+ self._dict.update(kw)
+ def __getitem__(self, key):
+ return self._dict[key]
+ def Dictionary(self, *args):
+ return {}
+ def Override(self, overrides):
+ d = self._dict.copy()
+ d.update(overrides)
+ return apply(Environment, (), d)
+ def _update(self, dict):
+ self._dict.update(dict)
+ def get_factory(self, factory):
+ return factory or MyNode
+ def get_scanner(self, scanner_key):
+ return self._dict['SCANNERS'][0]
+
+class Builder:
+ def __init__(self, env=None, is_explicit=1):
+ if env is None: env = Environment()
+ self.env = env
+ self.overrides = {}
+ self.action = MyAction()
+ self.source_factory = MyNode
+ self.is_explicit = is_explicit
+ self.target_scanner = None
+ self.source_scanner = None
+ def targets(self, t):
+ return [t]
+ def get_actions(self):
+ return [self.action]
+ def get_contents(self, target, source, env):
+ return 7
+
+class NoneBuilder(Builder):
+ def execute(self, target, source, env):
+ Builder.execute(self, target, source, env)
+ return None
+
+class ListBuilder(Builder):
+ def __init__(self, *nodes):
+ Builder.__init__(self)
+ self.nodes = nodes
+ def execute(self, target, source, env):
+ if hasattr(self, 'status'):
+ return self.status
+ for n in self.nodes:
+ n.prepare()
+ target = self.nodes[0]
+ self.status = Builder.execute(self, target, source, env)
+
+class FailBuilder:
+ def execute(self, target, source, env):
+ return 1
+
+class ExceptBuilder:
+ def execute(self, target, source, env):
+ raise SCons.Errors.BuildError
+
+class ExceptBuilder2:
+ def execute(self, target, source, env):
+ raise "foo"
+
+class Scanner:
+ called = None
+ def __call__(self, node):
+ self.called = 1
+ return node.found_includes
+ def path(self, env, dir, target=None, source=None):
+ return ()
+ def select(self, node):
+ return self
+ def recurse_nodes(self, nodes):
+ return nodes
+
+class MyNode(SCons.Node.Node):
+ """The base Node class contains a number of do-nothing methods that
+ we expect to be overridden by real, functional Node subclasses. So
+ simulate a real, functional Node subclass.
+ """
+ def __init__(self, name):
+ SCons.Node.Node.__init__(self)
+ self.name = name
+ self.found_includes = []
+ def __str__(self):
+ return self.name
+ def get_found_includes(self, env, scanner, target):
+ return scanner(self)
+
+class Calculator:
+ def __init__(self, val):
+ self.max_drift = 0
+ class M:
+ def __init__(self, val):
+ self.val = val
+ def signature(self, args):
+ return self.val
+ def collect(self, args):
+ return reduce(lambda x, y: x+y, args, self.val)
+ self.module = M(val)
+
+
+
+class NodeInfoBaseTestCase(unittest.TestCase):
+
+ def test_merge(self):
+ """Test merging NodeInfoBase attributes"""
+ ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni2 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+
+ ni1.a1 = 1
+ ni1.a2 = 2
+
+ ni2.a2 = 222
+ ni2.a3 = 333
+
+ ni1.merge(ni2)
+ expect = {'a1':1, 'a2':222, 'a3':333, '_version_id':1}
+ assert ni1.__dict__ == expect, ni1.__dict__
+
+ def test_update(self):
+ """Test the update() method"""
+ ni = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni.update(SCons.Node.Node())
+
+ def test_format(self):
+ """Test the NodeInfoBase.format() method"""
+ ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni1.xxx = 'x'
+ ni1.yyy = 'y'
+ ni1.zzz = 'z'
+
+ f = ni1.format()
+ assert f == ['1', 'x', 'y', 'z'], f
+
+ ni1.field_list = ['xxx', 'zzz', 'aaa']
+
+ f = ni1.format()
+ assert f == ['x', 'z', 'None'], f
+
+
+
+class BuildInfoBaseTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test BuildInfoBase initialization"""
+ n = SCons.Node.Node()
+ bi = SCons.Node.BuildInfoBase(n)
+ assert bi
+
+ def test_merge(self):
+ """Test merging BuildInfoBase attributes"""
+ n1 = SCons.Node.Node()
+ bi1 = SCons.Node.BuildInfoBase(n1)
+ n2 = SCons.Node.Node()
+ bi2 = SCons.Node.BuildInfoBase(n2)
+
+ bi1.a1 = 1
+ bi1.a2 = 2
+
+ bi2.a2 = 222
+ bi2.a3 = 333
+
+ bi1.merge(bi2)
+ assert bi1.a1 == 1, bi1.a1
+ assert bi1.a2 == 222, bi1.a2
+ assert bi1.a3 == 333, bi1.a3
+
+
+class NodeTestCase(unittest.TestCase):
+
+ def test_build(self):
+ """Test building a node
+ """
+ global built_it, built_order
+
+ # Make sure it doesn't blow up if no builder is set.
+ node = MyNode("www")
+ node.build()
+ assert built_it is None
+ node.build(extra_kw_argument = 1)
+ assert built_it is None
+
+ node = MyNode("xxx")
+ node.builder_set(Builder())
+ node.env_set(Environment())
+ node.path = "xxx"
+ node.sources = ["yyy", "zzz"]
+ node.build()
+ assert built_it
+ assert built_target == [node], built_target
+ assert built_source == ["yyy", "zzz"], built_source
+
+ built_it = None
+ node = MyNode("qqq")
+ node.builder_set(NoneBuilder())
+ node.env_set(Environment())
+ node.path = "qqq"
+ node.sources = ["rrr", "sss"]
+ node.builder.overrides = { "foo" : 1, "bar" : 2 }
+ node.build()
+ assert built_it
+ assert built_target == [node], built_target
+ assert built_source == ["rrr", "sss"], built_source
+ assert built_args["foo"] == 1, built_args
+ assert built_args["bar"] == 2, built_args
+
+ fff = MyNode("fff")
+ ggg = MyNode("ggg")
+ lb = ListBuilder(fff, ggg)
+ e = Environment()
+ fff.builder_set(lb)
+ fff.env_set(e)
+ fff.path = "fff"
+ ggg.builder_set(lb)
+ ggg.env_set(e)
+ ggg.path = "ggg"
+ fff.sources = ["hhh", "iii"]
+ ggg.sources = ["hhh", "iii"]
+ # [Charles C. 1/7/2002] Uhhh, why are there no asserts here?
+ # [SK, 15 May 2003] I dunno, let's add some...
+ built_it = None
+ fff.build()
+ assert built_it
+ assert built_target == [fff], built_target
+ assert built_source == ["hhh", "iii"], built_source
+ built_it = None
+ ggg.build()
+ assert built_it
+ assert built_target == [ggg], built_target
+ assert built_source == ["hhh", "iii"], built_source
+
+ built_it = None
+ jjj = MyNode("jjj")
+ b = Builder()
+ jjj.builder_set(b)
+ # NOTE: No env_set()! We should pull the environment from the builder.
+ b.env = Environment()
+ b.overrides = { "on" : 3, "off" : 4 }
+ e.builder = b
+ jjj.build()
+ assert built_it
+ assert built_target[0] == jjj, built_target[0]
+ assert built_source == [], built_source
+ assert built_args["on"] == 3, built_args
+ assert built_args["off"] == 4, built_args
+
+ def test_get_build_scanner_path(self):
+ """Test the get_build_scanner_path() method"""
+ n = SCons.Node.Node()
+ x = MyExecutor()
+ n.set_executor(x)
+ p = n.get_build_scanner_path('fake_scanner')
+ assert p == "executor would call fake_scanner", p
+
+ def test_get_executor(self):
+ """Test the get_executor() method"""
+ n = SCons.Node.Node()
+
+ try:
+ n.get_executor(0)
+ except AttributeError:
+ pass
+ else:
+ self.fail("did not catch expected AttributeError")
+
+ class Builder:
+ action = 'act'
+ env = 'env1'
+ overrides = {}
+
+ n = SCons.Node.Node()
+ n.builder_set(Builder())
+ x = n.get_executor()
+ assert x.env == 'env1', x.env
+
+ n = SCons.Node.Node()
+ n.builder_set(Builder())
+ n.env_set('env2')
+ x = n.get_executor()
+ assert x.env == 'env2', x.env
+
+ def test_set_executor(self):
+ """Test the set_executor() method"""
+ n = SCons.Node.Node()
+ n.set_executor(1)
+ assert n.executor == 1, n.executor
+
+ def test_executor_cleanup(self):
+ """Test letting the executor cleanup its cache"""
+ n = SCons.Node.Node()
+ x = MyExecutor()
+ n.set_executor(x)
+ n.executor_cleanup()
+ assert x.cleaned_up
+
+ def test_reset_executor(self):
+ """Test the reset_executor() method"""
+ n = SCons.Node.Node()
+ n.set_executor(1)
+ assert n.executor == 1, n.executor
+ n.reset_executor()
+ assert not hasattr(n, 'executor'), "unexpected executor attribute"
+
+ def test_built(self):
+ """Test the built() method"""
+ class SubNodeInfo(SCons.Node.NodeInfoBase):
+ def update(self, node):
+ self.updated = 1
+ class SubNode(SCons.Node.Node):
+ def clear(self):
+ self.cleared = 1
+
+ n = SubNode()
+ n.ninfo = SubNodeInfo(n)
+ n.built()
+ assert n.cleared, n.cleared
+ assert n.ninfo.updated, n.ninfo.cleared
+
+ def test_push_to_cache(self):
+ """Test the base push_to_cache() method"""
+ n = SCons.Node.Node()
+ r = n.push_to_cache()
+ assert r is None, r
+
+ def test_retrieve_from_cache(self):
+ """Test the base retrieve_from_cache() method"""
+ n = SCons.Node.Node()
+ r = n.retrieve_from_cache()
+ assert r == 0, r
+
+ def test_visited(self):
+ """Test the base visited() method
+
+ Just make sure it's there and we can call it.
+ """
+ n = SCons.Node.Node()
+ n.visited()
+
+ def test_builder_set(self):
+ """Test setting a Node's Builder
+ """
+ node = SCons.Node.Node()
+ b = Builder()
+ node.builder_set(b)
+ assert node.builder == b
+
+ def test_has_builder(self):
+ """Test the has_builder() method
+ """
+ n1 = SCons.Node.Node()
+ assert n1.has_builder() == 0
+ n1.builder_set(Builder())
+ assert n1.has_builder() == 1
+
+ def test_has_explicit_builder(self):
+ """Test the has_explicit_builder() method
+ """
+ n1 = SCons.Node.Node()
+ assert not n1.has_explicit_builder()
+ n1.set_explicit(1)
+ assert n1.has_explicit_builder()
+ n1.set_explicit(None)
+ assert not n1.has_explicit_builder()
+
+ def test_get_builder(self):
+ """Test the get_builder() method"""
+ n1 = SCons.Node.Node()
+ b = n1.get_builder()
+ assert b is None, b
+ b = n1.get_builder(777)
+ assert b == 777, b
+ n1.builder_set(888)
+ b = n1.get_builder()
+ assert b == 888, b
+ b = n1.get_builder(999)
+ assert b == 888, b
+
+ def test_multiple_side_effect_has_builder(self):
+ """Test the multiple_side_effect_has_builder() method
+ """
+ n1 = SCons.Node.Node()
+ assert n1.multiple_side_effect_has_builder() == 0
+ n1.builder_set(Builder())
+ assert n1.multiple_side_effect_has_builder() == 1
+
+ def test_is_derived(self):
+ """Test the is_derived() method
+ """
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+ n3 = SCons.Node.Node()
+
+ n2.builder_set(Builder())
+ n3.side_effect = 1
+
+ assert n1.is_derived() == 0
+ assert n2.is_derived() == 1
+ assert n3.is_derived() == 1
+
+ def test_alter_targets(self):
+ """Test the alter_targets() method
+ """
+ n = SCons.Node.Node()
+ t, m = n.alter_targets()
+ assert t == [], t
+ assert m is None, m
+
+ def test_is_up_to_date(self):
+ """Test the default is_up_to_date() method
+ """
+ node = SCons.Node.Node()
+ assert node.is_up_to_date() is None
+
+ def test_children_are_up_to_date(self):
+ """Test the children_are_up_to_date() method used by subclasses
+ """
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+
+ n1.add_source([n2])
+ assert n1.children_are_up_to_date(), "expected up to date"
+ n2.set_state(SCons.Node.executed)
+ assert not n1.children_are_up_to_date(), "expected not up to date"
+ n2.set_state(SCons.Node.up_to_date)
+ assert n1.children_are_up_to_date(), "expected up to date"
+ n1.always_build = 1
+ assert not n1.children_are_up_to_date(), "expected not up to date"
+
+ def test_env_set(self):
+ """Test setting a Node's Environment
+ """
+ node = SCons.Node.Node()
+ e = Environment()
+ node.env_set(e)
+ assert node.env == e
+
+ def test_get_actions(self):
+ """Test fetching a Node's action list
+ """
+ node = SCons.Node.Node()
+ node.builder_set(Builder())
+ a = node.builder.get_actions()
+ assert isinstance(a[0], MyAction), a[0]
+
+ def test_get_csig(self):
+ """Test generic content signature calculation
+ """
+ node = SCons.Node.Node()
+ node.get_contents = lambda: 444
+ result = node.get_csig()
+ assert result == '550a141f12de6341fba65b0ad0433500', result
+
+ def test_get_cachedir_csig(self):
+ """Test content signature calculation for CacheDir
+ """
+ node = SCons.Node.Node()
+ node.get_contents = lambda: 555
+ result = node.get_cachedir_csig()
+ assert result == '15de21c670ae7c3f6f3f1f37029303c9', result
+
+ def test_get_binfo(self):
+ """Test fetching/creating a build information structure
+ """
+ node = SCons.Node.Node()
+
+ binfo = node.get_binfo()
+ assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
+
+ node = SCons.Node.Node()
+ d = SCons.Node.Node()
+ d.get_ninfo().csig = 777
+ i = SCons.Node.Node()
+ i.get_ninfo().csig = 888
+ node.depends = [d]
+ node.implicit = [i]
+
+ binfo = node.get_binfo()
+ assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
+ assert hasattr(binfo, 'bsources')
+ assert hasattr(binfo, 'bsourcesigs')
+ assert binfo.bdepends == [d]
+ assert hasattr(binfo, 'bdependsigs')
+ assert binfo.bimplicit == [i]
+ assert hasattr(binfo, 'bimplicitsigs')
+
+ def test_explain(self):
+ """Test explaining why a Node must be rebuilt
+ """
+ class testNode(SCons.Node.Node):
+ def __str__(self): return 'xyzzy'
+ node = testNode()
+ node.exists = lambda: None
+ # Can't do this with new-style classes (python bug #1066490)
+ #node.__str__ = lambda: 'xyzzy'
+ result = node.explain()
+ assert result == "building `xyzzy' because it doesn't exist\n", result
+
+ class testNode2(SCons.Node.Node):
+ def __str__(self): return 'null_binfo'
+ class FS:
+ pass
+ node = testNode2()
+ node.fs = FS()
+ node.fs.Top = SCons.Node.Node()
+ result = node.explain()
+ assert result is None, result
+
+ def get_null_info():
+ class Null_SConsignEntry:
+ class Null_BuildInfo:
+ def prepare_dependencies(self):
+ pass
+ binfo = Null_BuildInfo()
+ return Null_SConsignEntry()
+
+ node.get_stored_info = get_null_info
+ #see above: node.__str__ = lambda: 'null_binfo'
+ result = node.explain()
+ assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
+
+ # XXX additional tests for the guts of the functionality some day
+
+ #def test_del_binfo(self):
+ # """Test deleting the build information from a Node
+ # """
+ # node = SCons.Node.Node()
+ # node.binfo = None
+ # node.del_binfo()
+ # assert not hasattr(node, 'binfo'), node
+
+ def test_store_info(self):
+ """Test calling the method to store build information
+ """
+ node = SCons.Node.Node()
+ node.store_info()
+
+ def test_get_stored_info(self):
+ """Test calling the method to fetch stored build information
+ """
+ node = SCons.Node.Node()
+ result = node.get_stored_info()
+ assert result is None, result
+
+ def test_set_always_build(self):
+ """Test setting a Node's always_build value
+ """
+ node = SCons.Node.Node()
+ node.set_always_build()
+ assert node.always_build
+ node.set_always_build(3)
+ assert node.always_build == 3
+
+ def test_set_noclean(self):
+ """Test setting a Node's noclean value
+ """
+ node = SCons.Node.Node()
+ node.set_noclean()
+ assert node.noclean == 1, node.noclean
+ node.set_noclean(7)
+ assert node.noclean == 1, node.noclean
+ node.set_noclean(0)
+ assert node.noclean == 0, node.noclean
+ node.set_noclean(None)
+ assert node.noclean == 0, node.noclean
+
+ def test_set_precious(self):
+ """Test setting a Node's precious value
+ """
+ node = SCons.Node.Node()
+ node.set_precious()
+ assert node.precious
+ node.set_precious(7)
+ assert node.precious == 7
+
+ def test_exists(self):
+ """Test evaluating whether a Node exists.
+ """
+ node = SCons.Node.Node()
+ e = node.exists()
+ assert e == 1, e
+
+ def test_exists(self):
+ """Test evaluating whether a Node exists locally or in a repository.
+ """
+ node = SCons.Node.Node()
+ e = node.rexists()
+ assert e == 1, e
+
+ class MyNode(SCons.Node.Node):
+ def exists(self):
+ return 'xyz'
+
+ node = MyNode()
+ e = node.rexists()
+ assert e == 'xyz', e
+
+ def test_prepare(self):
+ """Test preparing a node to be built
+
+ By extension, this also tests the missing() method.
+ """
+ node = SCons.Node.Node()
+
+ n1 = SCons.Node.Node()
+ n1.builder_set(Builder())
+ node.implicit = []
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n1])
+
+ node.prepare() # should not throw an exception
+
+ n2 = SCons.Node.Node()
+ n2.linked = 1
+ node.implicit = []
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n2])
+
+ node.prepare() # should not throw an exception
+
+ n3 = SCons.Node.Node()
+ node.implicit = []
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n3])
+
+ node.prepare() # should not throw an exception
+
+ class MyNode(SCons.Node.Node):
+ def rexists(self):
+ return None
+ n4 = MyNode()
+ node.implicit = []
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n4])
+ exc_caught = 0
+ try:
+ node.prepare()
+ except SCons.Errors.StopError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected StopError"
+
+ def test_add_dependency(self):
+ """Test adding dependencies to a Node's list.
+ """
+ node = SCons.Node.Node()
+ assert node.depends == []
+
+ zero = SCons.Node.Node()
+
+ one = SCons.Node.Node()
+ two = SCons.Node.Node()
+ three = SCons.Node.Node()
+ four = SCons.Node.Node()
+ five = SCons.Node.Node()
+ six = SCons.Node.Node()
+
+ node.add_dependency([zero])
+ assert node.depends == [zero]
+ node.add_dependency([one])
+ assert node.depends == [zero, one]
+ node.add_dependency([two, three])
+ assert node.depends == [zero, one, two, three]
+ node.add_dependency([three, four, one])
+ assert node.depends == [zero, one, two, three, four]
+
+ try:
+ node.add_depends([[five, six]])
+ except:
+ pass
+ else:
+ raise "did not catch expected exception"
+ assert node.depends == [zero, one, two, three, four]
+
+
+ def test_add_source(self):
+ """Test adding sources to a Node's list.
+ """
+ node = SCons.Node.Node()
+ assert node.sources == []
+
+ zero = SCons.Node.Node()
+ one = SCons.Node.Node()
+ two = SCons.Node.Node()
+ three = SCons.Node.Node()
+ four = SCons.Node.Node()
+ five = SCons.Node.Node()
+ six = SCons.Node.Node()
+
+ node.add_source([zero])
+ assert node.sources == [zero]
+ node.add_source([one])
+ assert node.sources == [zero, one]
+ node.add_source([two, three])
+ assert node.sources == [zero, one, two, three]
+ node.add_source([three, four, one])
+ assert node.sources == [zero, one, two, three, four]
+
+ try:
+ node.add_source([[five, six]])
+ except:
+ pass
+ else:
+ raise "did not catch expected exception"
+ assert node.sources == [zero, one, two, three, four], node.sources
+
+ def test_add_ignore(self):
+ """Test adding files whose dependencies should be ignored.
+ """
+ node = SCons.Node.Node()
+ assert node.ignore == []
+
+ zero = SCons.Node.Node()
+ one = SCons.Node.Node()
+ two = SCons.Node.Node()
+ three = SCons.Node.Node()
+ four = SCons.Node.Node()
+ five = SCons.Node.Node()
+ six = SCons.Node.Node()
+
+ node.add_ignore([zero])
+ assert node.ignore == [zero]
+ node.add_ignore([one])
+ assert node.ignore == [zero, one]
+ node.add_ignore([two, three])
+ assert node.ignore == [zero, one, two, three]
+ node.add_ignore([three, four, one])
+ assert node.ignore == [zero, one, two, three, four]
+
+ try:
+ node.add_ignore([[five, six]])
+ except:
+ pass
+ else:
+ raise "did not catch expected exception"
+ assert node.ignore == [zero, one, two, three, four]
+
+ def test_get_found_includes(self):
+ """Test the default get_found_includes() method
+ """
+ node = SCons.Node.Node()
+ target = SCons.Node.Node()
+ e = Environment()
+ deps = node.get_found_includes(e, None, target)
+ assert deps == [], deps
+
+ def test_get_implicit_deps(self):
+ """Test get_implicit_deps()
+ """
+ node = MyNode("nnn")
+ target = MyNode("ttt")
+ env = Environment()
+
+ # No scanner at all returns []
+ deps = node.get_implicit_deps(env, None, target)
+ assert deps == [], deps
+
+ s = Scanner()
+ d1 = MyNode("d1")
+ d2 = MyNode("d2")
+ node.found_includes = [d1, d2]
+
+ # Simple return of the found includes
+ deps = node.get_implicit_deps(env, s, target)
+ assert deps == [d1, d2], deps
+
+ # By default, our fake scanner recurses
+ e = MyNode("eee")
+ f = MyNode("fff")
+ g = MyNode("ggg")
+ d1.found_includes = [e, f]
+ d2.found_includes = [e, f]
+ f.found_includes = [g]
+ deps = node.get_implicit_deps(env, s, target)
+ assert deps == [d1, d2, e, f, g], map(str, deps)
+
+ # Recursive scanning eliminates duplicates
+ e.found_includes = [f]
+ deps = node.get_implicit_deps(env, s, target)
+ assert deps == [d1, d2, e, f, g], map(str, deps)
+
+ # Scanner method can select specific nodes to recurse
+ def no_fff(nodes):
+ return filter(lambda n: str(n)[0] != 'f', nodes)
+ s.recurse_nodes = no_fff
+ deps = node.get_implicit_deps(env, s, target)
+ assert deps == [d1, d2, e, f], map(str, deps)
+
+ # Scanner method can short-circuit recursing entirely
+ s.recurse_nodes = lambda nodes: []
+ deps = node.get_implicit_deps(env, s, target)
+ assert deps == [d1, d2], map(str, deps)
+
+ def test_get_env_scanner(self):
+ """Test fetching the environment scanner for a Node
+ """
+ node = SCons.Node.Node()
+ scanner = Scanner()
+ env = Environment(SCANNERS = [scanner])
+ s = node.get_env_scanner(env)
+ assert s == scanner, s
+ s = node.get_env_scanner(env, {'X':1})
+ assert s == scanner, s
+
+ def test_get_target_scanner(self):
+ """Test fetching the target scanner for a Node
+ """
+ s = Scanner()
+ b = Builder()
+ b.target_scanner = s
+ n = SCons.Node.Node()
+ n.builder = b
+ x = n.get_target_scanner()
+ assert x is s, x
+
+ def test_get_source_scanner(self):
+ """Test fetching the source scanner for a Node
+ """
+ target = SCons.Node.Node()
+ source = SCons.Node.Node()
+ s = target.get_source_scanner(source)
+ assert isinstance(s, SCons.Util.Null), s
+
+ ts1 = Scanner()
+ ts2 = Scanner()
+ ts3 = Scanner()
+
+ class Builder1(Builder):
+ def __call__(self, source):
+ r = SCons.Node.Node()
+ r.builder = self
+ return [r]
+ class Builder2(Builder1):
+ def __init__(self, scanner):
+ self.source_scanner = scanner
+
+ builder = Builder2(ts1)
+
+ targets = builder([source])
+ s = targets[0].get_source_scanner(source)
+ assert s is ts1, s
+
+ target.builder_set(Builder2(ts1))
+ target.builder.source_scanner = ts2
+ s = target.get_source_scanner(source)
+ assert s is ts2, s
+
+ builder = Builder1(env=Environment(SCANNERS = [ts3]))
+
+ targets = builder([source])
+
+ s = targets[0].get_source_scanner(source)
+ assert s is ts3, s
+
+
+ def test_scan(self):
+ """Test Scanner functionality
+ """
+ env = Environment()
+ node = MyNode("nnn")
+ node.builder = Builder()
+ node.env_set(env)
+ x = MyExecutor(env, [node])
+
+ s = Scanner()
+ d = MyNode("ddd")
+ node.found_includes = [d]
+
+ node.builder.target_scanner = s
+ assert node.implicit is None
+
+ node.scan()
+ assert s.called
+ assert node.implicit == [d], node.implicit
+
+ # Check that scanning a node with some stored implicit
+ # dependencies resets internal attributes appropriately
+ # if the stored dependencies need recalculation.
+ class StoredNode(MyNode):
+ def get_stored_implicit(self):
+ return [MyNode('implicit1'), MyNode('implicit2')]
+
+ save_implicit_cache = SCons.Node.implicit_cache
+ save_implicit_deps_changed = SCons.Node.implicit_deps_changed
+ save_implicit_deps_unchanged = SCons.Node.implicit_deps_unchanged
+ SCons.Node.implicit_cache = 1
+ SCons.Node.implicit_deps_changed = None
+ SCons.Node.implicit_deps_unchanged = None
+ try:
+ sn = StoredNode("eee")
+ sn.builder_set(Builder())
+ sn.builder.target_scanner = s
+
+ sn.scan()
+
+ assert sn.implicit == [], sn.implicit
+ assert sn.children() == [], sn.children()
+
+ finally:
+ SCons.Node.implicit_cache = save_implicit_cache
+ SCons.Node.implicit_deps_changed = save_implicit_deps_changed
+ SCons.Node.implicit_deps_unchanged = save_implicit_deps_unchanged
+
+ def test_scanner_key(self):
+ """Test that a scanner_key() method exists"""
+ assert SCons.Node.Node().scanner_key() is None
+
+ def test_children(self):
+ """Test fetching the non-ignored "children" of a Node.
+ """
+ node = SCons.Node.Node()
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+ n3 = SCons.Node.Node()
+ n4 = SCons.Node.Node()
+ n5 = SCons.Node.Node()
+ n6 = SCons.Node.Node()
+ n7 = SCons.Node.Node()
+ n8 = SCons.Node.Node()
+ n9 = SCons.Node.Node()
+ n10 = SCons.Node.Node()
+ n11 = SCons.Node.Node()
+ n12 = SCons.Node.Node()
+
+ node.add_source([n1, n2, n3])
+ node.add_dependency([n4, n5, n6])
+ node.implicit = []
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
+ node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
+ node.add_ignore([n2, n5, n8, n11])
+
+ kids = node.children()
+ for kid in [n1, n3, n4, n6, n7, n9, n10, n12]:
+ assert kid in kids, kid
+ for kid in [n2, n5, n8, n11]:
+ assert not kid in kids, kid
+
+ def test_all_children(self):
+ """Test fetching all the "children" of a Node.
+ """
+ node = SCons.Node.Node()
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+ n3 = SCons.Node.Node()
+ n4 = SCons.Node.Node()
+ n5 = SCons.Node.Node()
+ n6 = SCons.Node.Node()
+ n7 = SCons.Node.Node()
+ n8 = SCons.Node.Node()
+ n9 = SCons.Node.Node()
+ n10 = SCons.Node.Node()
+ n11 = SCons.Node.Node()
+ n12 = SCons.Node.Node()
+
+ node.add_source([n1, n2, n3])
+ node.add_dependency([n4, n5, n6])
+ node.implicit = []
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
+ node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
+ node.add_ignore([n2, n5, n8, n11])
+
+ kids = node.all_children()
+ for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]:
+ assert kid in kids, kid
+
+ def test_state(self):
+ """Test setting and getting the state of a node
+ """
+ node = SCons.Node.Node()
+ assert node.get_state() == SCons.Node.no_state
+ node.set_state(SCons.Node.executing)
+ assert node.get_state() == SCons.Node.executing
+ assert SCons.Node.pending < SCons.Node.executing
+ assert SCons.Node.executing < SCons.Node.up_to_date
+ assert SCons.Node.up_to_date < SCons.Node.executed
+ assert SCons.Node.executed < SCons.Node.failed
+
+ def test_walker(self):
+ """Test walking a Node tree.
+ """
+
+ n1 = MyNode("n1")
+
+ nw = SCons.Node.Walker(n1)
+ assert not nw.is_done()
+ assert nw.next().name == "n1"
+ assert nw.is_done()
+ assert nw.next() is None
+
+ n2 = MyNode("n2")
+ n3 = MyNode("n3")
+ n1.add_source([n2, n3])
+
+ nw = SCons.Node.Walker(n1)
+ n = nw.next()
+ assert n.name == "n2", n.name
+ n = nw.next()
+ assert n.name == "n3", n.name
+ n = nw.next()
+ assert n.name == "n1", n.name
+ n = nw.next()
+ assert n is None, n
+
+ n4 = MyNode("n4")
+ n5 = MyNode("n5")
+ n6 = MyNode("n6")
+ n7 = MyNode("n7")
+ n2.add_source([n4, n5])
+ n3.add_dependency([n6, n7])
+
+ nw = SCons.Node.Walker(n1)
+ assert nw.next().name == "n4"
+ assert nw.next().name == "n5"
+ assert nw.history.has_key(n2)
+ assert nw.next().name == "n2"
+ assert nw.next().name == "n6"
+ assert nw.next().name == "n7"
+ assert nw.history.has_key(n3)
+ assert nw.next().name == "n3"
+ assert nw.history.has_key(n1)
+ assert nw.next().name == "n1"
+ assert nw.next() is None
+
+ n8 = MyNode("n8")
+ n8.add_dependency([n3])
+ n7.add_dependency([n8])
+
+ def cycle(node, stack):
+ global cycle_detected
+ cycle_detected = 1
+
+ global cycle_detected
+
+ nw = SCons.Node.Walker(n3, cycle_func = cycle)
+ n = nw.next()
+ assert n.name == "n6", n.name
+ n = nw.next()
+ assert n.name == "n8", n.name
+ assert cycle_detected
+ cycle_detected = None
+ n = nw.next()
+ assert n.name == "n7", n.name
+ n = nw.next()
+ assert nw.next() is None
+
+ def test_abspath(self):
+ """Test the get_abspath() method."""
+ n = MyNode("foo")
+ assert n.get_abspath() == str(n), n.get_abspath()
+
+ def test_for_signature(self):
+ """Test the for_signature() method."""
+ n = MyNode("foo")
+ assert n.for_signature() == str(n), n.get_abspath()
+
+ def test_get_string(self):
+ """Test the get_string() method."""
+ class TestNode(MyNode):
+ def __init__(self, name, sig):
+ MyNode.__init__(self, name)
+ self.sig = sig
+
+ def for_signature(self):
+ return self.sig
+
+ n = TestNode("foo", "bar")
+ assert n.get_string(0) == "foo", n.get_string(0)
+ assert n.get_string(1) == "bar", n.get_string(1)
+
+ def test_literal(self):
+ """Test the is_literal() function."""
+ n=SCons.Node.Node()
+ assert n.is_literal()
+
+ def test_Annotate(self):
+ """Test using an interface-specific Annotate function."""
+ def my_annotate(node, self=self):
+ node.annotation = self.node_string
+
+ save_Annotate = SCons.Node.Annotate
+ SCons.Node.Annotate = my_annotate
+
+ try:
+ self.node_string = '#1'
+ n = SCons.Node.Node()
+ assert n.annotation == '#1', n.annotation
+
+ self.node_string = '#2'
+ n = SCons.Node.Node()
+ assert n.annotation == '#2', n.annotation
+ finally:
+ SCons.Node.Annotate = save_Annotate
+
+ def test_clear(self):
+ """Test clearing all cached state information."""
+ n = SCons.Node.Node()
+
+ n.set_state(3)
+ n.binfo = 'xyz'
+ n.includes = 'testincludes'
+ n.found_include = {'testkey':'testvalue'}
+ n.implicit = 'testimplicit'
+
+ x = MyExecutor()
+ n.set_executor(x)
+
+ n.clear()
+
+ assert n.includes is None, n.includes
+ assert x.cleaned_up
+
+ def test_get_subst_proxy(self):
+ """Test the get_subst_proxy method."""
+ n = MyNode("test")
+
+ assert n.get_subst_proxy() == n, n.get_subst_proxy()
+
+ def test_new_binfo(self):
+ """Test the new_binfo() method"""
+ n = SCons.Node.Node()
+ result = n.new_binfo()
+ assert isinstance(result, SCons.Node.BuildInfoBase), result
+
+ def test_get_suffix(self):
+ """Test the base Node get_suffix() method"""
+ n = SCons.Node.Node()
+ s = n.get_suffix()
+ assert s == '', s
+
+ def test_postprocess(self):
+ """Test calling the base Node postprocess() method"""
+ n = SCons.Node.Node()
+ n.waiting_parents = set( ['foo','bar'] )
+
+ n.postprocess()
+ assert n.waiting_parents == set(), n.waiting_parents
+
+ def test_add_to_waiting_parents(self):
+ """Test the add_to_waiting_parents() method"""
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+ assert n1.waiting_parents == set(), n1.waiting_parents
+ r = n1.add_to_waiting_parents(n2)
+ assert r == 1, r
+ assert n1.waiting_parents == set((n2,)), n1.waiting_parents
+ r = n1.add_to_waiting_parents(n2)
+ assert r == 0, r
+
+
+class NodeListTestCase(unittest.TestCase):
+ def test___str__(self):
+ """Test"""
+ n1 = MyNode("n1")
+ n2 = MyNode("n2")
+ n3 = MyNode("n3")
+ nl = SCons.Node.NodeList([n3, n2, n1])
+
+ l = [1]
+ ul = UserList.UserList([2])
+ try:
+ l.extend(ul)
+ except TypeError:
+ # An older version of Python (*cough* 1.5.2 *cough*)
+ # that doesn't allow UserList objects to extend lists.
+ pass
+ else:
+ s = str(nl)
+ assert s == "['n3', 'n2', 'n1']", s
+
+ r = repr(nl)
+ r = re.sub('at (0[xX])?[0-9a-fA-F]+', 'at 0x', r)
+ # Don't care about ancestry: just leaf value of MyNode
+ r = re.sub('<.*?\.MyNode', '<MyNode', r)
+ # New-style classes report as "object"; classic classes report
+ # as "instance"...
+ r = re.sub("object", "instance", r)
+ l = string.join(["<MyNode instance at 0x>"]*3, ", ")
+ assert r == '[%s]' % l, r
+
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ BuildInfoBaseTestCase,
+ NodeInfoBaseTestCase,
+ NodeTestCase,
+ NodeListTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py
new file mode 100644
index 0000000..941b4ed
--- /dev/null
+++ b/src/engine/SCons/Node/Python.py
@@ -0,0 +1,128 @@
+"""scons.Node.Python
+
+Python nodes.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/Python.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Node
+
+class ValueNodeInfo(SCons.Node.NodeInfoBase):
+ current_version_id = 1
+
+ field_list = ['csig']
+
+ def str_to_node(self, s):
+ return Value(s)
+
+class ValueBuildInfo(SCons.Node.BuildInfoBase):
+ current_version_id = 1
+
+class Value(SCons.Node.Node):
+ """A class for Python variables, typically passed on the command line
+ or generated by a script, but not from a file or some other source.
+ """
+
+ NodeInfo = ValueNodeInfo
+ BuildInfo = ValueBuildInfo
+
+ def __init__(self, value, built_value=None):
+ SCons.Node.Node.__init__(self)
+ self.value = value
+ if built_value is not None:
+ self.built_value = built_value
+
+ def str_for_display(self):
+ return repr(self.value)
+
+ def __str__(self):
+ return str(self.value)
+
+ def make_ready(self):
+ self.get_csig()
+
+ def build(self, **kw):
+ if not hasattr(self, 'built_value'):
+ apply (SCons.Node.Node.build, (self,), kw)
+
+ is_up_to_date = SCons.Node.Node.children_are_up_to_date
+
+ def is_under(self, dir):
+ # Make Value nodes get built regardless of
+ # what directory scons was run from. Value nodes
+ # are outside the filesystem:
+ return 1
+
+ def write(self, built_value):
+ """Set the value of the node."""
+ self.built_value = built_value
+
+ def read(self):
+ """Return the value. If necessary, the value is built."""
+ self.build()
+ if not hasattr(self, 'built_value'):
+ self.built_value = self.value
+ return self.built_value
+
+ def get_text_contents(self):
+ """By the assumption that the node.built_value is a
+ deterministic product of the sources, the contents of a Value
+ are the concatenation of all the contents of its sources. As
+ the value need not be built when get_contents() is called, we
+ cannot use the actual node.built_value."""
+ ###TODO: something reasonable about universal newlines
+ contents = str(self.value)
+ for kid in self.children(None):
+ contents = contents + kid.get_contents()
+ return contents
+
+ get_contents = get_text_contents ###TODO should return 'bytes' value
+
+ def changed_since_last_build(self, target, prev_ni):
+ cur_csig = self.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+ def get_csig(self, calc=None):
+ """Because we're a Python value node and don't have a real
+ timestamp, we get to ignore the calculator and just use the
+ value contents."""
+ try:
+ return self.ninfo.csig
+ except AttributeError:
+ pass
+ contents = self.get_contents()
+ self.get_ninfo().csig = contents
+ return contents
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py
new file mode 100644
index 0000000..01844ac
--- /dev/null
+++ b/src/engine/SCons/Node/PythonTests.py
@@ -0,0 +1,130 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/PythonTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Node.Python
+
+class ValueTestCase(unittest.TestCase):
+
+ def test_Value(self):
+ """Test creating a Value() object
+ """
+ v1 = SCons.Node.Python.Value('a')
+ assert v1.value == 'a', v1.value
+
+ value2 = 'a'
+ v2 = SCons.Node.Python.Value(value2)
+ assert v2.value == value2, v2.value
+ assert v2.value is value2, v2.value
+
+ assert not v1 is v2
+ assert v1.value == v2.value
+
+ v3 = SCons.Node.Python.Value('c', 'cb')
+ assert v3.built_value == 'cb'
+
+ def test_build(self):
+ """Test "building" a Value Node
+ """
+ class fake_executor:
+ def __call__(self, node):
+ node.write('faked')
+
+ v1 = SCons.Node.Python.Value('b', 'built')
+ v1.executor = fake_executor()
+ v1.build()
+ assert v1.built_value == 'built', v1.built_value
+
+ v2 = SCons.Node.Python.Value('b')
+ v2.executor = fake_executor()
+ v2.build()
+ assert v2.built_value == 'faked', v2.built_value
+
+ def test_read(self):
+ """Test the Value.read() method
+ """
+ v1 = SCons.Node.Python.Value('a')
+ x = v1.read()
+ assert x == 'a', x
+
+ def test_write(self):
+ """Test the Value.write() method
+ """
+ v1 = SCons.Node.Python.Value('a')
+ assert v1.value == 'a', v1.value
+ assert not hasattr(v1, 'built_value')
+
+ v1.write('new')
+ assert v1.value == 'a', v1.value
+ assert v1.built_value == 'new', v1.built_value
+
+ def test_get_csig(self):
+ """Test calculating the content signature of a Value() object
+ """
+ v1 = SCons.Node.Python.Value('aaa')
+ csig = v1.get_csig(None)
+ assert csig == 'aaa', csig
+
+ v2 = SCons.Node.Python.Value(7)
+ csig = v2.get_csig(None)
+ assert csig == '7', csig
+
+ v3 = SCons.Node.Python.Value(None)
+ csig = v3.get_csig(None)
+ assert csig == 'None', csig
+
+class ValueNodeInfoTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test ValueNodeInfo initialization"""
+ vvv = SCons.Node.Python.Value('vvv')
+ ni = SCons.Node.Python.ValueNodeInfo(vvv)
+
+class ValueBuildInfoTestCase(unittest.TestCase):
+ def test___init__(self):
+ """Test ValueBuildInfo initialization"""
+ vvv = SCons.Node.Python.Value('vvv')
+ bi = SCons.Node.Python.ValueBuildInfo(vvv)
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ ValueTestCase,
+ ValueBuildInfoTestCase,
+ ValueNodeInfoTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
new file mode 100644
index 0000000..94fd4df
--- /dev/null
+++ b/src/engine/SCons/Node/__init__.py
@@ -0,0 +1,1341 @@
+"""SCons.Node
+
+The Node package for the SCons software construction utility.
+
+This is, in many ways, the heart of SCons.
+
+A Node is where we encapsulate all of the dependency information about
+any thing that SCons can build, or about any thing which SCons can use
+to build some other thing. The canonical "thing," of course, is a file,
+but a Node can also represent something remote (like a web page) or
+something completely abstract (like an Alias).
+
+Each specific type of "thing" is specifically represented by a subclass
+of the Node base class: Node.FS.File for files, Node.Alias for aliases,
+etc. Dependency information is kept here in the base class, and
+information specific to files/aliases/etc. is in the subclass. The
+goal, if we've done this correctly, is that any type of "thing" should
+be able to depend on any other type of "thing."
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Node/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import copy
+from itertools import chain, izip
+import string
+import UserList
+
+from SCons.Debug import logInstanceCreation
+import SCons.Executor
+import SCons.Memoize
+import SCons.Util
+
+from SCons.Debug import Trace
+
+def classname(obj):
+ return string.split(str(obj.__class__), '.')[-1]
+
+# Node states
+#
+# These are in "priority" order, so that the maximum value for any
+# child/dependency of a node represents the state of that node if
+# it has no builder of its own. The canonical example is a file
+# system directory, which is only up to date if all of its children
+# were up to date.
+no_state = 0
+pending = 1
+executing = 2
+up_to_date = 3
+executed = 4
+failed = 5
+
+StateString = {
+ 0 : "no_state",
+ 1 : "pending",
+ 2 : "executing",
+ 3 : "up_to_date",
+ 4 : "executed",
+ 5 : "failed",
+}
+
+# controls whether implicit dependencies are cached:
+implicit_cache = 0
+
+# controls whether implicit dep changes are ignored:
+implicit_deps_unchanged = 0
+
+# controls whether the cached implicit deps are ignored:
+implicit_deps_changed = 0
+
+# A variable that can be set to an interface-specific function be called
+# to annotate a Node with information about its creation.
+def do_nothing(node): pass
+
+Annotate = do_nothing
+
+# Classes for signature info for Nodes.
+
+class NodeInfoBase:
+ """
+ The generic base class for signature information for a Node.
+
+ Node subclasses should subclass NodeInfoBase to provide their own
+ logic for dealing with their own Node-specific signature information.
+ """
+ current_version_id = 1
+ def __init__(self, node):
+ # Create an object attribute from the class attribute so it ends up
+ # in the pickled data in the .sconsign file.
+ self._version_id = self.current_version_id
+ def update(self, node):
+ try:
+ field_list = self.field_list
+ except AttributeError:
+ return
+ for f in field_list:
+ try:
+ delattr(self, f)
+ except AttributeError:
+ pass
+ try:
+ func = getattr(node, 'get_' + f)
+ except AttributeError:
+ pass
+ else:
+ setattr(self, f, func())
+ def convert(self, node, val):
+ pass
+ def merge(self, other):
+ self.__dict__.update(other.__dict__)
+ def format(self, field_list=None, names=0):
+ if field_list is None:
+ try:
+ field_list = self.field_list
+ except AttributeError:
+ field_list = self.__dict__.keys()
+ field_list.sort()
+ fields = []
+ for field in field_list:
+ try:
+ f = getattr(self, field)
+ except AttributeError:
+ f = None
+ f = str(f)
+ if names:
+ f = field + ': ' + f
+ fields.append(f)
+ return fields
+
+class BuildInfoBase:
+ """
+ The generic base class for build information for a Node.
+
+ This is what gets stored in a .sconsign file for each target file.
+ It contains a NodeInfo instance for this node (signature information
+ that's specific to the type of Node) and direct attributes for the
+ generic build stuff we have to track: sources, explicit dependencies,
+ implicit dependencies, and action information.
+ """
+ current_version_id = 1
+ def __init__(self, node):
+ # Create an object attribute from the class attribute so it ends up
+ # in the pickled data in the .sconsign file.
+ self._version_id = self.current_version_id
+ self.bsourcesigs = []
+ self.bdependsigs = []
+ self.bimplicitsigs = []
+ self.bactsig = None
+ def merge(self, other):
+ self.__dict__.update(other.__dict__)
+
+class Node:
+ """The base Node class, for entities that we know how to
+ build, or use to build other Nodes.
+ """
+
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ class Attrs:
+ pass
+
+ def __init__(self):
+ if __debug__: logInstanceCreation(self, 'Node.Node')
+ # Note that we no longer explicitly initialize a self.builder
+ # attribute to None here. That's because the self.builder
+ # attribute may be created on-the-fly later by a subclass (the
+ # canonical example being a builder to fetch a file from a
+ # source code system like CVS or Subversion).
+
+ # Each list of children that we maintain is accompanied by a
+ # dictionary used to look up quickly whether a node is already
+ # present in the list. Empirical tests showed that it was
+ # fastest to maintain them as side-by-side Node attributes in
+ # this way, instead of wrapping up each list+dictionary pair in
+ # a class. (Of course, we could always still do that in the
+ # future if we had a good reason to...).
+ self.sources = [] # source files used to build node
+ self.sources_set = set()
+ self._specific_sources = False
+ self.depends = [] # explicit dependencies (from Depends)
+ self.depends_set = set()
+ self.ignore = [] # dependencies to ignore
+ self.ignore_set = set()
+ self.prerequisites = SCons.Util.UniqueList()
+ self.implicit = None # implicit (scanned) dependencies (None means not scanned yet)
+ self.waiting_parents = set()
+ self.waiting_s_e = set()
+ self.ref_count = 0
+ self.wkids = None # Kids yet to walk, when it's an array
+
+ self.env = None
+ self.state = no_state
+ self.precious = None
+ self.noclean = 0
+ self.nocache = 0
+ self.always_build = None
+ self.includes = None
+ self.attributes = self.Attrs() # Generic place to stick information about the Node.
+ self.side_effect = 0 # true iff this node is a side effect
+ self.side_effects = [] # the side effects of building this target
+ self.linked = 0 # is this node linked to the variant directory?
+
+ self.clear_memoized_values()
+
+ # Let the interface in which the build engine is embedded
+ # annotate this Node with its own info (like a description of
+ # what line in what file created the node, for example).
+ Annotate(self)
+
+ def disambiguate(self, must_exist=None):
+ return self
+
+ def get_suffix(self):
+ return ''
+
+ memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
+
+ def get_build_env(self):
+ """Fetch the appropriate Environment to build this node.
+ """
+ try:
+ return self._memo['get_build_env']
+ except KeyError:
+ pass
+ result = self.get_executor().get_build_env()
+ self._memo['get_build_env'] = result
+ return result
+
+ def get_build_scanner_path(self, scanner):
+ """Fetch the appropriate scanner path for this node."""
+ return self.get_executor().get_build_scanner_path(scanner)
+
+ def set_executor(self, executor):
+ """Set the action executor for this node."""
+ self.executor = executor
+
+ def get_executor(self, create=1):
+ """Fetch the action executor for this node. Create one if
+ there isn't already one, and requested to do so."""
+ try:
+ executor = self.executor
+ except AttributeError:
+ if not create:
+ raise
+ try:
+ act = self.builder.action
+ except AttributeError:
+ executor = SCons.Executor.Null(targets=[self])
+ else:
+ executor = SCons.Executor.Executor(act,
+ self.env or self.builder.env,
+ [self.builder.overrides],
+ [self],
+ self.sources)
+ self.executor = executor
+ return executor
+
+ def executor_cleanup(self):
+ """Let the executor clean up any cached information."""
+ try:
+ executor = self.get_executor(create=None)
+ except AttributeError:
+ pass
+ else:
+ executor.cleanup()
+
+ def reset_executor(self):
+ "Remove cached executor; forces recompute when needed."
+ try:
+ delattr(self, 'executor')
+ except AttributeError:
+ pass
+
+ def push_to_cache(self):
+ """Try to push a node into a cache
+ """
+ pass
+
+ def retrieve_from_cache(self):
+ """Try to retrieve the node's content from a cache
+
+ This method is called from multiple threads in a parallel build,
+ so only do thread safe stuff here. Do thread unsafe stuff in
+ built().
+
+ Returns true iff the node was successfully retrieved.
+ """
+ return 0
+
+ #
+ # Taskmaster interface subsystem
+ #
+
+ def make_ready(self):
+ """Get a Node ready for evaluation.
+
+ This is called before the Taskmaster decides if the Node is
+ up-to-date or not. Overriding this method allows for a Node
+ subclass to be disambiguated if necessary, or for an implicit
+ source builder to be attached.
+ """
+ pass
+
+ def prepare(self):
+ """Prepare for this Node to be built.
+
+ This is called after the Taskmaster has decided that the Node
+ is out-of-date and must be rebuilt, but before actually calling
+ the method to build the Node.
+
+ This default implementation checks that explicit or implicit
+ dependencies either exist or are derived, and initializes the
+ BuildInfo structure that will hold the information about how
+ this node is, uh, built.
+
+ (The existence of source files is checked separately by the
+ Executor, which aggregates checks for all of the targets built
+ by a specific action.)
+
+ Overriding this method allows for for a Node subclass to remove
+ the underlying file from the file system. Note that subclass
+ methods should call this base class method to get the child
+ check and the BuildInfo structure.
+ """
+ for d in self.depends:
+ if d.missing():
+ msg = "Explicit dependency `%s' not found, needed by target `%s'."
+ raise SCons.Errors.StopError, msg % (d, self)
+ if self.implicit is not None:
+ for i in self.implicit:
+ if i.missing():
+ msg = "Implicit dependency `%s' not found, needed by target `%s'."
+ raise SCons.Errors.StopError, msg % (i, self)
+ self.binfo = self.get_binfo()
+
+ def build(self, **kw):
+ """Actually build the node.
+
+ This is called by the Taskmaster after it's decided that the
+ Node is out-of-date and must be rebuilt, and after the prepare()
+ method has gotten everything, uh, prepared.
+
+ This method is called from multiple threads in a parallel build,
+ so only do thread safe stuff here. Do thread unsafe stuff
+ in built().
+
+ """
+ try:
+ apply(self.get_executor(), (self,), kw)
+ except SCons.Errors.BuildError, e:
+ e.node = self
+ raise
+
+ def built(self):
+ """Called just after this node is successfully built."""
+
+ # Clear the implicit dependency caches of any Nodes
+ # waiting for this Node to be built.
+ for parent in self.waiting_parents:
+ parent.implicit = None
+
+ self.clear()
+
+ self.ninfo.update(self)
+
+ def visited(self):
+ """Called just after this node has been visited (with or
+ without a build)."""
+ try:
+ binfo = self.binfo
+ except AttributeError:
+ # Apparently this node doesn't need build info, so
+ # don't bother calculating or storing it.
+ pass
+ else:
+ self.ninfo.update(self)
+ self.store_info()
+
+ #
+ #
+ #
+
+ def add_to_waiting_s_e(self, node):
+ self.waiting_s_e.add(node)
+
+ def add_to_waiting_parents(self, node):
+ """
+ Returns the number of nodes added to our waiting parents list:
+ 1 if we add a unique waiting parent, 0 if not. (Note that the
+ returned values are intended to be used to increment a reference
+ count, so don't think you can "clean up" this function by using
+ True and False instead...)
+ """
+ wp = self.waiting_parents
+ if node in wp:
+ return 0
+ wp.add(node)
+ return 1
+
+ def postprocess(self):
+ """Clean up anything we don't need to hang onto after we've
+ been built."""
+ self.executor_cleanup()
+ self.waiting_parents = set()
+
+ def clear(self):
+ """Completely clear a Node of all its cached state (so that it
+ can be re-evaluated by interfaces that do continuous integration
+ builds).
+ """
+ # The del_binfo() call here isn't necessary for normal execution,
+ # but is for interactive mode, where we might rebuild the same
+ # target and need to start from scratch.
+ self.del_binfo()
+ self.clear_memoized_values()
+ self.ninfo = self.new_ninfo()
+ self.executor_cleanup()
+ try:
+ delattr(self, '_calculated_sig')
+ except AttributeError:
+ pass
+ self.includes = None
+
+ def clear_memoized_values(self):
+ self._memo = {}
+
+ def builder_set(self, builder):
+ self.builder = builder
+ try:
+ del self.executor
+ except AttributeError:
+ pass
+
+ def has_builder(self):
+ """Return whether this Node has a builder or not.
+
+ In Boolean tests, this turns out to be a *lot* more efficient
+ than simply examining the builder attribute directly ("if
+ node.builder: ..."). When the builder attribute is examined
+ directly, it ends up calling __getattr__ for both the __len__
+ and __nonzero__ attributes on instances of our Builder Proxy
+ class(es), generating a bazillion extra calls and slowing
+ things down immensely.
+ """
+ try:
+ b = self.builder
+ except AttributeError:
+ # There was no explicit builder for this Node, so initialize
+ # the self.builder attribute to None now.
+ b = self.builder = None
+ return b is not None
+
+ def set_explicit(self, is_explicit):
+ self.is_explicit = is_explicit
+
+ def has_explicit_builder(self):
+ """Return whether this Node has an explicit builder
+
+ This allows an internal Builder created by SCons to be marked
+ non-explicit, so that it can be overridden by an explicit
+ builder that the user supplies (the canonical example being
+ directories)."""
+ try:
+ return self.is_explicit
+ except AttributeError:
+ self.is_explicit = None
+ return self.is_explicit
+
+ def get_builder(self, default_builder=None):
+ """Return the set builder, or a specified default value"""
+ try:
+ return self.builder
+ except AttributeError:
+ return default_builder
+
+ multiple_side_effect_has_builder = has_builder
+
+ def is_derived(self):
+ """
+ Returns true iff this node is derived (i.e. built).
+
+ This should return true only for nodes whose path should be in
+ the variant directory when duplicate=0 and should contribute their build
+ signatures when they are used as source files to other derived files. For
+ example: source with source builders are not derived in this sense,
+ and hence should not return true.
+ """
+ return self.has_builder() or self.side_effect
+
+ def alter_targets(self):
+ """Return a list of alternate targets for this Node.
+ """
+ return [], None
+
+ def get_found_includes(self, env, scanner, path):
+ """Return the scanned include lines (implicit dependencies)
+ found in this node.
+
+ The default is no implicit dependencies. We expect this method
+ to be overridden by any subclass that can be scanned for
+ implicit dependencies.
+ """
+ return []
+
+ def get_implicit_deps(self, env, scanner, path):
+ """Return a list of implicit dependencies for this node.
+
+ This method exists to handle recursive invocation of the scanner
+ on the implicit dependencies returned by the scanner, if the
+ scanner's recursive flag says that we should.
+ """
+ if not scanner:
+ return []
+
+ # Give the scanner a chance to select a more specific scanner
+ # for this Node.
+ #scanner = scanner.select(self)
+
+ nodes = [self]
+ seen = {}
+ seen[self] = 1
+ deps = []
+ while nodes:
+ n = nodes.pop(0)
+ d = filter(lambda x, seen=seen: not seen.has_key(x),
+ n.get_found_includes(env, scanner, path))
+ if d:
+ deps.extend(d)
+ for n in d:
+ seen[n] = 1
+ nodes.extend(scanner.recurse_nodes(d))
+
+ return deps
+
+ def get_env_scanner(self, env, kw={}):
+ return env.get_scanner(self.scanner_key())
+
+ def get_target_scanner(self):
+ return self.builder.target_scanner
+
+ def get_source_scanner(self, node):
+ """Fetch the source scanner for the specified node
+
+ NOTE: "self" is the target being built, "node" is
+ the source file for which we want to fetch the scanner.
+
+ Implies self.has_builder() is true; again, expect to only be
+ called from locations where this is already verified.
+
+ This function may be called very often; it attempts to cache
+ the scanner found to improve performance.
+ """
+ scanner = None
+ try:
+ scanner = self.builder.source_scanner
+ except AttributeError:
+ pass
+ if not scanner:
+ # The builder didn't have an explicit scanner, so go look up
+ # a scanner from env['SCANNERS'] based on the node's scanner
+ # key (usually the file extension).
+ scanner = self.get_env_scanner(self.get_build_env())
+ if scanner:
+ scanner = scanner.select(node)
+ return scanner
+
+ def add_to_implicit(self, deps):
+ if not hasattr(self, 'implicit') or self.implicit is None:
+ self.implicit = []
+ self.implicit_set = set()
+ self._children_reset()
+ self._add_child(self.implicit, self.implicit_set, deps)
+
+ def scan(self):
+ """Scan this node's dependents for implicit dependencies."""
+ # Don't bother scanning non-derived files, because we don't
+ # care what their dependencies are.
+ # Don't scan again, if we already have scanned.
+ if self.implicit is not None:
+ return
+ self.implicit = []
+ self.implicit_set = set()
+ self._children_reset()
+ if not self.has_builder():
+ return
+
+ build_env = self.get_build_env()
+ executor = self.get_executor()
+
+ # Here's where we implement --implicit-cache.
+ if implicit_cache and not implicit_deps_changed:
+ implicit = self.get_stored_implicit()
+ if implicit is not None:
+ # We now add the implicit dependencies returned from the
+ # stored .sconsign entry to have already been converted
+ # to Nodes for us. (We used to run them through a
+ # source_factory function here.)
+
+ # Update all of the targets with them. This
+ # essentially short-circuits an N*M scan of the
+ # sources for each individual target, which is a hell
+ # of a lot more efficient.
+ for tgt in executor.get_all_targets():
+ tgt.add_to_implicit(implicit)
+
+ if implicit_deps_unchanged or self.is_up_to_date():
+ return
+ # one of this node's sources has changed,
+ # so we must recalculate the implicit deps:
+ self.implicit = []
+ self.implicit_set = set()
+
+ # Have the executor scan the sources.
+ executor.scan_sources(self.builder.source_scanner)
+
+ # If there's a target scanner, have the executor scan the target
+ # node itself and associated targets that might be built.
+ scanner = self.get_target_scanner()
+ if scanner:
+ executor.scan_targets(scanner)
+
+ def scanner_key(self):
+ return None
+
+ def select_scanner(self, scanner):
+ """Selects a scanner for this Node.
+
+ This is a separate method so it can be overridden by Node
+ subclasses (specifically, Node.FS.Dir) that *must* use their
+ own Scanner and don't select one the Scanner.Selector that's
+ configured for the target.
+ """
+ return scanner.select(self)
+
+ def env_set(self, env, safe=0):
+ if safe and self.env:
+ return
+ self.env = env
+
+ #
+ # SIGNATURE SUBSYSTEM
+ #
+
+ NodeInfo = NodeInfoBase
+ BuildInfo = BuildInfoBase
+
+ def new_ninfo(self):
+ ninfo = self.NodeInfo(self)
+ return ninfo
+
+ def get_ninfo(self):
+ try:
+ return self.ninfo
+ except AttributeError:
+ self.ninfo = self.new_ninfo()
+ return self.ninfo
+
+ def new_binfo(self):
+ binfo = self.BuildInfo(self)
+ return binfo
+
+ def get_binfo(self):
+ """
+ Fetch a node's build information.
+
+ node - the node whose sources will be collected
+ cache - alternate node to use for the signature cache
+ returns - the build signature
+
+ This no longer handles the recursive descent of the
+ node's children's signatures. We expect that they're
+ already built and updated by someone else, if that's
+ what's wanted.
+ """
+ try:
+ return self.binfo
+ except AttributeError:
+ pass
+
+ binfo = self.new_binfo()
+ self.binfo = binfo
+
+ executor = self.get_executor()
+ ignore_set = self.ignore_set
+
+ if self.has_builder():
+ binfo.bact = str(executor)
+ binfo.bactsig = SCons.Util.MD5signature(executor.get_contents())
+
+ if self._specific_sources:
+ sources = []
+ for s in self.sources:
+ if s not in ignore_set:
+ sources.append(s)
+ else:
+ sources = executor.get_unignored_sources(self, self.ignore)
+ seen = set()
+ bsources = []
+ bsourcesigs = []
+ for s in sources:
+ if not s in seen:
+ seen.add(s)
+ bsources.append(s)
+ bsourcesigs.append(s.get_ninfo())
+ binfo.bsources = bsources
+ binfo.bsourcesigs = bsourcesigs
+
+ depends = self.depends
+ dependsigs = []
+ for d in depends:
+ if d not in ignore_set:
+ dependsigs.append(d.get_ninfo())
+ binfo.bdepends = depends
+ binfo.bdependsigs = dependsigs
+
+ implicit = self.implicit or []
+ implicitsigs = []
+ for i in implicit:
+ if i not in ignore_set:
+ implicitsigs.append(i.get_ninfo())
+ binfo.bimplicit = implicit
+ binfo.bimplicitsigs = implicitsigs
+
+ return binfo
+
+ def del_binfo(self):
+ """Delete the build info from this node."""
+ try:
+ delattr(self, 'binfo')
+ except AttributeError:
+ pass
+
+ def get_csig(self):
+ try:
+ return self.ninfo.csig
+ except AttributeError:
+ ninfo = self.get_ninfo()
+ ninfo.csig = SCons.Util.MD5signature(self.get_contents())
+ return self.ninfo.csig
+
+ def get_cachedir_csig(self):
+ return self.get_csig()
+
+ def store_info(self):
+ """Make the build signature permanent (that is, store it in the
+ .sconsign file or equivalent)."""
+ pass
+
+ def do_not_store_info(self):
+ pass
+
+ def get_stored_info(self):
+ return None
+
+ def get_stored_implicit(self):
+ """Fetch the stored implicit dependencies"""
+ return None
+
+ #
+ #
+ #
+
+ def set_precious(self, precious = 1):
+ """Set the Node's precious value."""
+ self.precious = precious
+
+ def set_noclean(self, noclean = 1):
+ """Set the Node's noclean value."""
+ # Make sure noclean is an integer so the --debug=stree
+ # output in Util.py can use it as an index.
+ self.noclean = noclean and 1 or 0
+
+ def set_nocache(self, nocache = 1):
+ """Set the Node's nocache value."""
+ # Make sure nocache is an integer so the --debug=stree
+ # output in Util.py can use it as an index.
+ self.nocache = nocache and 1 or 0
+
+ def set_always_build(self, always_build = 1):
+ """Set the Node's always_build value."""
+ self.always_build = always_build
+
+ def exists(self):
+ """Does this node exists?"""
+ # All node exist by default:
+ return 1
+
+ def rexists(self):
+ """Does this node exist locally or in a repositiory?"""
+ # There are no repositories by default:
+ return self.exists()
+
+ def missing(self):
+ return not self.is_derived() and \
+ not self.linked and \
+ not self.rexists()
+
+ def remove(self):
+ """Remove this Node: no-op by default."""
+ return None
+
+ def add_dependency(self, depend):
+ """Adds dependencies."""
+ try:
+ self._add_child(self.depends, self.depends_set, depend)
+ except TypeError, e:
+ e = e.args[0]
+ if SCons.Util.is_List(e):
+ s = map(str, e)
+ else:
+ s = str(e)
+ raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
+
+ def add_prerequisite(self, prerequisite):
+ """Adds prerequisites"""
+ self.prerequisites.extend(prerequisite)
+ self._children_reset()
+
+ def add_ignore(self, depend):
+ """Adds dependencies to ignore."""
+ try:
+ self._add_child(self.ignore, self.ignore_set, depend)
+ except TypeError, e:
+ e = e.args[0]
+ if SCons.Util.is_List(e):
+ s = map(str, e)
+ else:
+ s = str(e)
+ raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
+
+ def add_source(self, source):
+ """Adds sources."""
+ if self._specific_sources:
+ return
+ try:
+ self._add_child(self.sources, self.sources_set, source)
+ except TypeError, e:
+ e = e.args[0]
+ if SCons.Util.is_List(e):
+ s = map(str, e)
+ else:
+ s = str(e)
+ raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
+
+ def _add_child(self, collection, set, child):
+ """Adds 'child' to 'collection', first checking 'set' to see if it's
+ already present."""
+ #if type(child) is not type([]):
+ # child = [child]
+ #for c in child:
+ # if not isinstance(c, Node):
+ # raise TypeError, c
+ added = None
+ for c in child:
+ if c not in set:
+ set.add(c)
+ collection.append(c)
+ added = 1
+ if added:
+ self._children_reset()
+
+ def set_specific_source(self, source):
+ self.add_source(source)
+ self._specific_sources = True
+
+ def add_wkid(self, wkid):
+ """Add a node to the list of kids waiting to be evaluated"""
+ if self.wkids is not None:
+ self.wkids.append(wkid)
+
+ def _children_reset(self):
+ self.clear_memoized_values()
+ # We need to let the Executor clear out any calculated
+ # build info that it's cached so we can re-calculate it.
+ self.executor_cleanup()
+
+ memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
+
+ def _children_get(self):
+ try:
+ return self._memo['children_get']
+ except KeyError:
+ pass
+
+ # The return list may contain duplicate Nodes, especially in
+ # source trees where there are a lot of repeated #includes
+ # of a tangle of .h files. Profiling shows, however, that
+ # eliminating the duplicates with a brute-force approach that
+ # preserves the order (that is, something like:
+ #
+ # u = []
+ # for n in list:
+ # if n not in u:
+ # u.append(n)"
+ #
+ # takes more cycles than just letting the underlying methods
+ # hand back cached values if a Node's information is requested
+ # multiple times. (Other methods of removing duplicates, like
+ # using dictionary keys, lose the order, and the only ordered
+ # dictionary patterns I found all ended up using "not in"
+ # internally anyway...)
+ if self.ignore_set:
+ if self.implicit is None:
+ iter = chain(self.sources,self.depends)
+ else:
+ iter = chain(self.sources, self.depends, self.implicit)
+
+ children = []
+ for i in iter:
+ if i not in self.ignore_set:
+ children.append(i)
+ else:
+ if self.implicit is None:
+ children = self.sources + self.depends
+ else:
+ children = self.sources + self.depends + self.implicit
+
+ self._memo['children_get'] = children
+ return children
+
+ def all_children(self, scan=1):
+ """Return a list of all the node's direct children."""
+ if scan:
+ self.scan()
+
+ # The return list may contain duplicate Nodes, especially in
+ # source trees where there are a lot of repeated #includes
+ # of a tangle of .h files. Profiling shows, however, that
+ # eliminating the duplicates with a brute-force approach that
+ # preserves the order (that is, something like:
+ #
+ # u = []
+ # for n in list:
+ # if n not in u:
+ # u.append(n)"
+ #
+ # takes more cycles than just letting the underlying methods
+ # hand back cached values if a Node's information is requested
+ # multiple times. (Other methods of removing duplicates, like
+ # using dictionary keys, lose the order, and the only ordered
+ # dictionary patterns I found all ended up using "not in"
+ # internally anyway...)
+ if self.implicit is None:
+ return self.sources + self.depends
+ else:
+ return self.sources + self.depends + self.implicit
+
+ def children(self, scan=1):
+ """Return a list of the node's direct children, minus those
+ that are ignored by this node."""
+ if scan:
+ self.scan()
+ return self._children_get()
+
+ def set_state(self, state):
+ self.state = state
+
+ def get_state(self):
+ return self.state
+
+ def state_has_changed(self, target, prev_ni):
+ return (self.state != SCons.Node.up_to_date)
+
+ def get_env(self):
+ env = self.env
+ if not env:
+ import SCons.Defaults
+ env = SCons.Defaults.DefaultEnvironment()
+ return env
+
+ def changed_since_last_build(self, target, prev_ni):
+ """
+
+ Must be overridden in a specific subclass to return True if this
+ Node (a dependency) has changed since the last time it was used
+ to build the specified target. prev_ni is this Node's state (for
+ example, its file timestamp, length, maybe content signature)
+ as of the last time the target was built.
+
+ Note that this method is called through the dependency, not the
+ target, because a dependency Node must be able to use its own
+ logic to decide if it changed. For example, File Nodes need to
+ obey if we're configured to use timestamps, but Python Value Nodes
+ never use timestamps and always use the content. If this method
+ were called through the target, then each Node's implementation
+ of this method would have to have more complicated logic to
+ handle all the different Node types on which it might depend.
+ """
+ raise NotImplementedError
+
+ def Decider(self, function):
+ SCons.Util.AddMethod(self, function, 'changed_since_last_build')
+
+ def changed(self, node=None):
+ """
+ Returns if the node is up-to-date with respect to the BuildInfo
+ stored last time it was built. The default behavior is to compare
+ it against our own previously stored BuildInfo, but the stored
+ BuildInfo from another Node (typically one in a Repository)
+ can be used instead.
+
+ Note that we now *always* check every dependency. We used to
+ short-circuit the check by returning as soon as we detected
+ any difference, but we now rely on checking every dependency
+ to make sure that any necessary Node information (for example,
+ the content signature of an #included .h file) is updated.
+ """
+ t = 0
+ if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
+ if node is None:
+ node = self
+
+ result = False
+
+ bi = node.get_stored_info().binfo
+ then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs
+ children = self.children()
+
+ diff = len(children) - len(then)
+ if diff:
+ # The old and new dependency lists are different lengths.
+ # This always indicates that the Node must be rebuilt.
+ # We also extend the old dependency list with enough None
+ # entries to equal the new dependency list, for the benefit
+ # of the loop below that updates node information.
+ then.extend([None] * diff)
+ if t: Trace(': old %s new %s' % (len(then), len(children)))
+ result = True
+
+ for child, prev_ni in izip(children, then):
+ if child.changed_since_last_build(self, prev_ni):
+ if t: Trace(': %s changed' % child)
+ result = True
+
+ contents = self.get_executor().get_contents()
+ if self.has_builder():
+ import SCons.Util
+ newsig = SCons.Util.MD5signature(contents)
+ if bi.bactsig != newsig:
+ if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig))
+ result = True
+
+ if not result:
+ if t: Trace(': up to date')
+
+ if t: Trace('\n')
+
+ return result
+
+ def is_up_to_date(self):
+ """Default check for whether the Node is current: unknown Node
+ subtypes are always out of date, so they will always get built."""
+ return None
+
+ def children_are_up_to_date(self):
+ """Alternate check for whether the Node is current: If all of
+ our children were up-to-date, then this Node was up-to-date, too.
+
+ The SCons.Node.Alias and SCons.Node.Python.Value subclasses
+ rebind their current() method to this method."""
+ # Allow the children to calculate their signatures.
+ self.binfo = self.get_binfo()
+ if self.always_build:
+ return None
+ state = 0
+ for kid in self.children(None):
+ s = kid.get_state()
+ if s and (not state or s > state):
+ state = s
+ return (state == 0 or state == SCons.Node.up_to_date)
+
+ def is_literal(self):
+ """Always pass the string representation of a Node to
+ the command interpreter literally."""
+ return 1
+
+ def render_include_tree(self):
+ """
+ Return a text representation, suitable for displaying to the
+ user, of the include tree for the sources of this node.
+ """
+ if self.is_derived() and self.env:
+ env = self.get_build_env()
+ for s in self.sources:
+ scanner = self.get_source_scanner(s)
+ if scanner:
+ path = self.get_build_scanner_path(scanner)
+ else:
+ path = None
+ def f(node, env=env, scanner=scanner, path=path):
+ return node.get_found_includes(env, scanner, path)
+ return SCons.Util.render_tree(s, f, 1)
+ else:
+ return None
+
+ def get_abspath(self):
+ """
+ Return an absolute path to the Node. This will return simply
+ str(Node) by default, but for Node types that have a concept of
+ relative path, this might return something different.
+ """
+ return str(self)
+
+ def for_signature(self):
+ """
+ Return a string representation of the Node that will always
+ be the same for this particular Node, no matter what. This
+ is by contrast to the __str__() method, which might, for
+ instance, return a relative path for a file Node. The purpose
+ of this method is to generate a value to be used in signature
+ calculation for the command line used to build a target, and
+ we use this method instead of str() to avoid unnecessary
+ rebuilds. This method does not need to return something that
+ would actually work in a command line; it can return any kind of
+ nonsense, so long as it does not change.
+ """
+ return str(self)
+
+ def get_string(self, for_signature):
+ """This is a convenience function designed primarily to be
+ used in command generators (i.e., CommandGeneratorActions or
+ Environment variables that are callable), which are called
+ with a for_signature argument that is nonzero if the command
+ generator is being called to generate a signature for the
+ command line, which determines if we should rebuild or not.
+
+ Such command generators should use this method in preference
+ to str(Node) when converting a Node to a string, passing
+ in the for_signature parameter, such that we will call
+ Node.for_signature() or str(Node) properly, depending on whether
+ we are calculating a signature or actually constructing a
+ command line."""
+ if for_signature:
+ return self.for_signature()
+ return str(self)
+
+ def get_subst_proxy(self):
+ """
+ This method is expected to return an object that will function
+ exactly like this Node, except that it implements any additional
+ special features that we would like to be in effect for
+ Environment variable substitution. The principle use is that
+ some Nodes would like to implement a __getattr__() method,
+ but putting that in the Node type itself has a tendency to kill
+ performance. We instead put it in a proxy and return it from
+ this method. It is legal for this method to return self
+ if no new functionality is needed for Environment substitution.
+ """
+ return self
+
+ def explain(self):
+ if not self.exists():
+ return "building `%s' because it doesn't exist\n" % self
+
+ if self.always_build:
+ return "rebuilding `%s' because AlwaysBuild() is specified\n" % self
+
+ old = self.get_stored_info()
+ if old is None:
+ return None
+
+ old = old.binfo
+ old.prepare_dependencies()
+
+ try:
+ old_bkids = old.bsources + old.bdepends + old.bimplicit
+ old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs
+ except AttributeError:
+ return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self
+
+ new = self.get_binfo()
+
+ new_bkids = new.bsources + new.bdepends + new.bimplicit
+ new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs
+
+ osig = dict(izip(old_bkids, old_bkidsigs))
+ nsig = dict(izip(new_bkids, new_bkidsigs))
+
+ # The sources and dependencies we'll want to report are all stored
+ # as relative paths to this target's directory, but we want to
+ # report them relative to the top-level SConstruct directory,
+ # so we only print them after running them through this lambda
+ # to turn them into the right relative Node and then return
+ # its string.
+ def stringify( s, E=self.dir.Entry ) :
+ if hasattr( s, 'dir' ) :
+ return str(E(s))
+ return str(s)
+
+ lines = []
+
+ removed = filter(lambda x, nk=new_bkids: not x in nk, old_bkids)
+ if removed:
+ removed = map(stringify, removed)
+ fmt = "`%s' is no longer a dependency\n"
+ lines.extend(map(lambda s, fmt=fmt: fmt % s, removed))
+
+ for k in new_bkids:
+ if not k in old_bkids:
+ lines.append("`%s' is a new dependency\n" % stringify(k))
+ elif k.changed_since_last_build(self, osig[k]):
+ lines.append("`%s' changed\n" % stringify(k))
+
+ if len(lines) == 0 and old_bkids != new_bkids:
+ lines.append("the dependency order changed:\n" +
+ "%sold: %s\n" % (' '*15, map(stringify, old_bkids)) +
+ "%snew: %s\n" % (' '*15, map(stringify, new_bkids)))
+
+ if len(lines) == 0:
+ def fmt_with_title(title, strlines):
+ lines = string.split(strlines, '\n')
+ sep = '\n' + ' '*(15 + len(title))
+ return ' '*15 + title + string.join(lines, sep) + '\n'
+ if old.bactsig != new.bactsig:
+ if old.bact == new.bact:
+ lines.append("the contents of the build action changed\n" +
+ fmt_with_title('action: ', new.bact))
+ else:
+ lines.append("the build action changed:\n" +
+ fmt_with_title('old: ', old.bact) +
+ fmt_with_title('new: ', new.bact))
+
+ if len(lines) == 0:
+ return "rebuilding `%s' for unknown reasons\n" % self
+
+ preamble = "rebuilding `%s' because" % self
+ if len(lines) == 1:
+ return "%s %s" % (preamble, lines[0])
+ else:
+ lines = ["%s:\n" % preamble] + lines
+ return string.join(lines, ' '*11)
+
+try:
+ [].extend(UserList.UserList([]))
+except TypeError:
+ # Python 1.5.2 doesn't allow a list to be extended by list-like
+ # objects (such as UserList instances), so just punt and use
+ # real lists.
+ def NodeList(l):
+ return l
+else:
+ class NodeList(UserList.UserList):
+ def __str__(self):
+ return str(map(str, self.data))
+
+def get_children(node, parent): return node.children()
+def ignore_cycle(node, stack): pass
+def do_nothing(node, parent): pass
+
+class Walker:
+ """An iterator for walking a Node tree.
+
+ This is depth-first, children are visited before the parent.
+ The Walker object can be initialized with any node, and
+ returns the next node on the descent with each next() call.
+ 'kids_func' is an optional function that will be called to
+ get the children of a node instead of calling 'children'.
+ 'cycle_func' is an optional function that will be called
+ when a cycle is detected.
+
+ This class does not get caught in node cycles caused, for example,
+ by C header file include loops.
+ """
+ def __init__(self, node, kids_func=get_children,
+ cycle_func=ignore_cycle,
+ eval_func=do_nothing):
+ self.kids_func = kids_func
+ self.cycle_func = cycle_func
+ self.eval_func = eval_func
+ node.wkids = copy.copy(kids_func(node, None))
+ self.stack = [node]
+ self.history = {} # used to efficiently detect and avoid cycles
+ self.history[node] = None
+
+ def next(self):
+ """Return the next node for this walk of the tree.
+
+ This function is intentionally iterative, not recursive,
+ to sidestep any issues of stack size limitations.
+ """
+
+ while self.stack:
+ if self.stack[-1].wkids:
+ node = self.stack[-1].wkids.pop(0)
+ if not self.stack[-1].wkids:
+ self.stack[-1].wkids = None
+ if self.history.has_key(node):
+ self.cycle_func(node, self.stack)
+ else:
+ node.wkids = copy.copy(self.kids_func(node, self.stack[-1]))
+ self.stack.append(node)
+ self.history[node] = None
+ else:
+ node = self.stack.pop()
+ del self.history[node]
+ if node:
+ if self.stack:
+ parent = self.stack[-1]
+ else:
+ parent = None
+ self.eval_func(node, parent)
+ return node
+ return None
+
+ def is_done(self):
+ return not self.stack
+
+
+arg2nodes_lookups = []
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Options/BoolOption.py b/src/engine/SCons/Options/BoolOption.py
new file mode 100644
index 0000000..53cd532
--- /dev/null
+++ b/src/engine/SCons/Options/BoolOption.py
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Options/BoolOption.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility. The new equivalent is the Variables/
+class hierarchy. These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+import SCons.Warnings
+
+warned = False
+
+def BoolOption(*args, **kw):
+ global warned
+ if not warned:
+ msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+ warned = True
+ return apply(SCons.Variables.BoolVariable, args, kw)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Options/EnumOption.py b/src/engine/SCons/Options/EnumOption.py
new file mode 100644
index 0000000..6e10139
--- /dev/null
+++ b/src/engine/SCons/Options/EnumOption.py
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Options/EnumOption.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility. The new equivalent is the Variables/
+class hierarchy. These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+import SCons.Warnings
+
+warned = False
+
+def EnumOption(*args, **kw):
+ global warned
+ if not warned:
+ msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+ warned = True
+ return apply(SCons.Variables.EnumVariable, args, kw)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Options/ListOption.py b/src/engine/SCons/Options/ListOption.py
new file mode 100644
index 0000000..146e289
--- /dev/null
+++ b/src/engine/SCons/Options/ListOption.py
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Options/ListOption.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility. The new equivalent is the Variables/
+class hierarchy. These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+import SCons.Warnings
+
+warned = False
+
+def ListOption(*args, **kw):
+ global warned
+ if not warned:
+ msg = "The ListOption() function is deprecated; use the ListVariable() function instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+ warned = True
+ return apply(SCons.Variables.ListVariable, args, kw)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Options/PackageOption.py b/src/engine/SCons/Options/PackageOption.py
new file mode 100644
index 0000000..7d04053
--- /dev/null
+++ b/src/engine/SCons/Options/PackageOption.py
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Options/PackageOption.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility. The new equivalent is the Variables/
+class hierarchy. These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+import SCons.Warnings
+
+warned = False
+
+def PackageOption(*args, **kw):
+ global warned
+ if not warned:
+ msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+ warned = True
+ return apply(SCons.Variables.PackageVariable, args, kw)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Options/PathOption.py b/src/engine/SCons/Options/PathOption.py
new file mode 100644
index 0000000..91653d4
--- /dev/null
+++ b/src/engine/SCons/Options/PathOption.py
@@ -0,0 +1,76 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Options/PathOption.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility. The new equivalent is the Variables/
+class hierarchy. These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+import SCons.Warnings
+
+warned = False
+
+class _PathOptionClass:
+ def warn(self):
+ global warned
+ if not warned:
+ msg = "The PathOption() function is deprecated; use the PathVariable() function instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+ warned = True
+
+ def __call__(self, *args, **kw):
+ self.warn()
+ return apply(SCons.Variables.PathVariable, args, kw)
+
+ def PathAccept(self, *args, **kw):
+ self.warn()
+ return apply(SCons.Variables.PathVariable.PathAccept, args, kw)
+
+ def PathIsDir(self, *args, **kw):
+ self.warn()
+ return apply(SCons.Variables.PathVariable.PathIsDir, args, kw)
+
+ def PathIsDirCreate(self, *args, **kw):
+ self.warn()
+ return apply(SCons.Variables.PathVariable.PathIsDirCreate, args, kw)
+
+ def PathIsFile(self, *args, **kw):
+ self.warn()
+ return apply(SCons.Variables.PathVariable.PathIsFile, args, kw)
+
+ def PathExists(self, *args, **kw):
+ self.warn()
+ return apply(SCons.Variables.PathVariable.PathExists, args, kw)
+
+PathOption = _PathOptionClass()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Options/__init__.py b/src/engine/SCons/Options/__init__.py
new file mode 100644
index 0000000..eb18dbf
--- /dev/null
+++ b/src/engine/SCons/Options/__init__.py
@@ -0,0 +1,74 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Options/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility. The new equivalent is the Variables/
+class hierarchy. These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+import SCons.Warnings
+
+from BoolOption import BoolOption # okay
+from EnumOption import EnumOption # okay
+from ListOption import ListOption # naja
+from PackageOption import PackageOption # naja
+from PathOption import PathOption # okay
+
+warned = False
+
+class Options(SCons.Variables.Variables):
+ def __init__(self, *args, **kw):
+ global warned
+ if not warned:
+ msg = "The Options class is deprecated; use the Variables class instead."
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+ warned = True
+ apply(SCons.Variables.Variables.__init__,
+ (self,) + args,
+ kw)
+
+ def AddOptions(self, *args, **kw):
+ return apply(SCons.Variables.Variables.AddVariables,
+ (self,) + args,
+ kw)
+
+ def UnknownOptions(self, *args, **kw):
+ return apply(SCons.Variables.Variables.UnknownVariables,
+ (self,) + args,
+ kw)
+
+ def FormatOptionHelpText(self, *args, **kw):
+ return apply(SCons.Variables.Variables.FormatVariableHelpText,
+ (self,) + args,
+ kw)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py
new file mode 100644
index 0000000..fb54fe1
--- /dev/null
+++ b/src/engine/SCons/PathList.py
@@ -0,0 +1,232 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/PathList.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """SCons.PathList
+
+A module for handling lists of directory paths (the sort of things
+that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and
+efficiency as we can while still keeping the evaluation delayed so that we
+Do the Right Thing (almost) regardless of how the variable is specified.
+
+"""
+
+import os
+import string
+
+import SCons.Memoize
+import SCons.Node
+import SCons.Util
+
+#
+# Variables to specify the different types of entries in a PathList object:
+#
+
+TYPE_STRING_NO_SUBST = 0 # string with no '$'
+TYPE_STRING_SUBST = 1 # string containing '$'
+TYPE_OBJECT = 2 # other object
+
+def node_conv(obj):
+ """
+ This is the "string conversion" routine that we have our substitutions
+ use to return Nodes, not strings. This relies on the fact that an
+ EntryProxy object has a get() method that returns the underlying
+ Node that it wraps, which is a bit of architectural dependence
+ that we might need to break or modify in the future in response to
+ additional requirements.
+ """
+ try:
+ get = obj.get
+ except AttributeError:
+ if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ):
+ result = obj
+ else:
+ result = str(obj)
+ else:
+ result = get()
+ return result
+
+class _PathList:
+ """
+ An actual PathList object.
+ """
+ def __init__(self, pathlist):
+ """
+ Initializes a PathList object, canonicalizing the input and
+ pre-processing it for quicker substitution later.
+
+ The stored representation of the PathList is a list of tuples
+ containing (type, value), where the "type" is one of the TYPE_*
+ variables defined above. We distinguish between:
+
+ strings that contain no '$' and therefore need no
+ delayed-evaluation string substitution (we expect that there
+ will be many of these and that we therefore get a pretty
+ big win from avoiding string substitution)
+
+ strings that contain '$' and therefore need substitution
+ (the hard case is things like '${TARGET.dir}/include',
+ which require re-evaluation for every target + source)
+
+ other objects (which may be something like an EntryProxy
+ that needs a method called to return a Node)
+
+ Pre-identifying the type of each element in the PathList up-front
+ and storing the type in the list of tuples is intended to reduce
+ the amount of calculation when we actually do the substitution
+ over and over for each target.
+ """
+ if SCons.Util.is_String(pathlist):
+ pathlist = string.split(pathlist, os.pathsep)
+ elif not SCons.Util.is_Sequence(pathlist):
+ pathlist = [pathlist]
+
+ pl = []
+ for p in pathlist:
+ try:
+ index = string.find(p, '$')
+ except (AttributeError, TypeError):
+ type = TYPE_OBJECT
+ else:
+ if index == -1:
+ type = TYPE_STRING_NO_SUBST
+ else:
+ type = TYPE_STRING_SUBST
+ pl.append((type, p))
+
+ self.pathlist = tuple(pl)
+
+ def __len__(self): return len(self.pathlist)
+
+ def __getitem__(self, i): return self.pathlist[i]
+
+ def subst_path(self, env, target, source):
+ """
+ Performs construction variable substitution on a pre-digested
+ PathList for a specific target and source.
+ """
+ result = []
+ for type, value in self.pathlist:
+ if type == TYPE_STRING_SUBST:
+ value = env.subst(value, target=target, source=source,
+ conv=node_conv)
+ if SCons.Util.is_Sequence(value):
+ result.extend(value)
+ continue
+
+ elif type == TYPE_OBJECT:
+ value = node_conv(value)
+ if value:
+ result.append(value)
+ return tuple(result)
+
+
+class PathListCache:
+ """
+ A class to handle caching of PathList lookups.
+
+ This class gets instantiated once and then deleted from the namespace,
+ so it's used as a Singleton (although we don't enforce that in the
+ usual Pythonic ways). We could have just made the cache a dictionary
+ in the module namespace, but putting it in this class allows us to
+ use the same Memoizer pattern that we use elsewhere to count cache
+ hits and misses, which is very valuable.
+
+ Lookup keys in the cache are computed by the _PathList_key() method.
+ Cache lookup should be quick, so we don't spend cycles canonicalizing
+ all forms of the same lookup key. For example, 'x:y' and ['x',
+ 'y'] logically represent the same list, but we don't bother to
+ split string representations and treat those two equivalently.
+ (Note, however, that we do, treat lists and tuples the same.)
+
+ The main type of duplication we're trying to catch will come from
+ looking up the same path list from two different clones of the
+ same construction environment. That is, given
+
+ env2 = env1.Clone()
+
+ both env1 and env2 will have the same CPPPATH value, and we can
+ cheaply avoid re-parsing both values of CPPPATH by using the
+ common value from this cache.
+ """
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ def __init__(self):
+ self._memo = {}
+
+ def _PathList_key(self, pathlist):
+ """
+ Returns the key for memoization of PathLists.
+
+ Note that we want this to be pretty quick, so we don't completely
+ canonicalize all forms of the same list. For example,
+ 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically
+ represent the same list if you're executing from $ROOT, but
+ we're not going to bother splitting strings into path elements,
+ or massaging strings into Nodes, to identify that equivalence.
+ We just want to eliminate obvious redundancy from the normal
+ case of re-using exactly the same cloned value for a path.
+ """
+ if SCons.Util.is_Sequence(pathlist):
+ pathlist = tuple(SCons.Util.flatten(pathlist))
+ return pathlist
+
+ memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key))
+
+ def PathList(self, pathlist):
+ """
+ Returns the cached _PathList object for the specified pathlist,
+ creating and caching a new object as necessary.
+ """
+ pathlist = self._PathList_key(pathlist)
+ try:
+ memo_dict = self._memo['PathList']
+ except KeyError:
+ memo_dict = {}
+ self._memo['PathList'] = memo_dict
+ else:
+ try:
+ return memo_dict[pathlist]
+ except KeyError:
+ pass
+
+ result = _PathList(pathlist)
+
+ memo_dict[pathlist] = result
+
+ return result
+
+PathList = PathListCache().PathList
+
+
+del PathListCache
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/PathListTests.py b/src/engine/SCons/PathListTests.py
new file mode 100644
index 0000000..b6ec142
--- /dev/null
+++ b/src/engine/SCons/PathListTests.py
@@ -0,0 +1,170 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/PathListTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.PathList
+
+
+class subst_pathTestCase(unittest.TestCase):
+
+ def setUp(self):
+
+ class FakeEnvironment:
+ def __init__(self, **kw):
+ self.kw = kw
+ def subst(self, s, target=None, source=None, conv=lambda x: x):
+ if s[0] == '$':
+ s = s[1:]
+ if s == 'target':
+ s = target
+ elif s == 'source':
+ s = source
+ else:
+ s = self.kw[s]
+ return s
+
+ self.env = FakeEnvironment(AAA = 'aaa', NULL = '')
+
+ def test_node(self):
+ """Test the subst_path() method on a Node
+ """
+
+ import SCons.Node
+
+ class A:
+ pass
+
+ n = SCons.Node.Node()
+
+ pl = SCons.PathList.PathList((n,))
+
+ result = pl.subst_path(self.env, 'y', 'z')
+
+ assert result == (n,), result
+
+ def test_object(self):
+ """Test the subst_path() method on a non-Node object
+ """
+
+ class A:
+ def __str__(self):
+ return '<object A>'
+
+ a = A()
+
+ pl = SCons.PathList.PathList((a,))
+
+ result = pl.subst_path(self.env, 'y', 'z')
+
+ assert result == ('<object A>',), result
+
+ def test_object_get(self):
+ """Test the subst_path() method on an object with a get() method
+ """
+
+ class B:
+ def get(self):
+ return 'b'
+
+ b = B()
+
+ pl = SCons.PathList.PathList((b,))
+
+ result = pl.subst_path(self.env, 'y', 'z')
+
+ assert result == ('b',), result
+
+ def test_string(self):
+ """Test the subst_path() method on a non-substitution string
+ """
+
+ self.env.subst = lambda s, target, source, conv: 'NOT THIS STRING'
+
+ pl = SCons.PathList.PathList(('x'))
+
+ result = pl.subst_path(self.env, 'y', 'z')
+
+ assert result == ('x',), result
+
+ def test_subst(self):
+ """Test the subst_path() method on substitution strings
+ """
+
+ pl = SCons.PathList.PathList(('$AAA', '$NULL'))
+
+ result = pl.subst_path(self.env, 'y', 'z')
+
+ assert result == ('aaa',), result
+
+
+class PathListCacheTestCase(unittest.TestCase):
+
+ def test_no_PathListCache(self):
+ """Make sure the PathListCache class is not visible
+ """
+ try:
+ SCons.PathList.PathListCache
+ except AttributeError:
+ pass
+ else:
+ self.fail("Found PathListCache unexpectedly\n")
+
+
+class PathListTestCase(unittest.TestCase):
+
+ def test_PathList(self):
+ """Test the PathList() entry point
+ """
+
+ x1 = SCons.PathList.PathList(('x',))
+ x2 = SCons.PathList.PathList(['x',])
+
+ assert x1 is x2, (x1, x2)
+
+ x3 = SCons.PathList.PathList('x')
+
+ assert not x1 is x3, (x1, x3)
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ subst_pathTestCase,
+ PathListCacheTestCase,
+ PathListTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py
new file mode 100644
index 0000000..e1e19cd
--- /dev/null
+++ b/src/engine/SCons/Platform/PlatformTests.py
@@ -0,0 +1,126 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/PlatformTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Platform
+import UserDict
+
+class Environment(UserDict.UserDict):
+ def Detect(self, cmd):
+ return cmd
+ def AppendENVPath(self, key, value):
+ pass
+
+class PlatformTestCase(unittest.TestCase):
+ def test_Platform(self):
+ """Test the Platform() function"""
+ p = SCons.Platform.Platform('cygwin')
+ assert str(p) == 'cygwin', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '.exe', env
+ assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
+
+ p = SCons.Platform.Platform('os2')
+ assert str(p) == 'os2', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '.exe', env
+ assert env['LIBSUFFIX'] == '.lib', env
+
+ p = SCons.Platform.Platform('posix')
+ assert str(p) == 'posix', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '', env
+ assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
+
+ p = SCons.Platform.Platform('irix')
+ assert str(p) == 'irix', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '', env
+ assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
+
+ p = SCons.Platform.Platform('aix')
+ assert str(p) == 'aix', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '', env
+ assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
+
+ p = SCons.Platform.Platform('sunos')
+ assert str(p) == 'sunos', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '', env
+ assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
+
+ p = SCons.Platform.Platform('hpux')
+ assert str(p) == 'hpux', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '', env
+ assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
+
+ p = SCons.Platform.Platform('win32')
+ assert str(p) == 'win32', p
+ env = Environment()
+ p(env)
+ assert env['PROGSUFFIX'] == '.exe', env
+ assert env['LIBSUFFIX'] == '.lib', env
+ assert str
+
+ try:
+ p = SCons.Platform.Platform('_does_not_exist_')
+ except SCons.Errors.UserError:
+ pass
+ else:
+ raise
+
+ env = Environment()
+ SCons.Platform.Platform()(env)
+ assert env != {}, env
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(PlatformTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py
new file mode 100644
index 0000000..671e929
--- /dev/null
+++ b/src/engine/SCons/Platform/__init__.py
@@ -0,0 +1,236 @@
+"""SCons.Platform
+
+SCons platform selection.
+
+This looks for modules that define a callable object that can modify a
+construction environment as appropriate for a given platform.
+
+Note that we take a more simplistic view of "platform" than Python does.
+We're looking for a single string that determines a set of
+tool-independent variables with which to initialize a construction
+environment. Consequently, we'll examine both sys.platform and os.name
+(and anything else that might come in to play) in order to return some
+specification which is unique enough for our purposes.
+
+Note that because this subsysem just *selects* a callable that can
+modify a construction environment, it's possible for people to define
+their own "platform specification" in an arbitrary callable function.
+No one needs to use or tie in to this subsystem in order to roll
+their own platform definition.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.compat
+
+import imp
+import os
+import string
+import sys
+import tempfile
+
+import SCons.Errors
+import SCons.Subst
+import SCons.Tool
+
+def platform_default():
+ """Return the platform string for our execution environment.
+
+ The returned value should map to one of the SCons/Platform/*.py
+ files. Since we're architecture independent, though, we don't
+ care about the machine architecture.
+ """
+ osname = os.name
+ if osname == 'java':
+ osname = os._osType
+ if osname == 'posix':
+ if sys.platform == 'cygwin':
+ return 'cygwin'
+ elif string.find(sys.platform, 'irix') != -1:
+ return 'irix'
+ elif string.find(sys.platform, 'sunos') != -1:
+ return 'sunos'
+ elif string.find(sys.platform, 'hp-ux') != -1:
+ return 'hpux'
+ elif string.find(sys.platform, 'aix') != -1:
+ return 'aix'
+ elif string.find(sys.platform, 'darwin') != -1:
+ return 'darwin'
+ else:
+ return 'posix'
+ elif os.name == 'os2':
+ return 'os2'
+ else:
+ return sys.platform
+
+def platform_module(name = platform_default()):
+ """Return the imported module for the platform.
+
+ This looks for a module name that matches the specified argument.
+ If the name is unspecified, we fetch the appropriate default for
+ our execution environment.
+ """
+ full_name = 'SCons.Platform.' + name
+ if not sys.modules.has_key(full_name):
+ if os.name == 'java':
+ eval(full_name)
+ else:
+ try:
+ file, path, desc = imp.find_module(name,
+ sys.modules['SCons.Platform'].__path__)
+ try:
+ mod = imp.load_module(full_name, file, path, desc)
+ finally:
+ if file:
+ file.close()
+ except ImportError:
+ try:
+ import zipimport
+ importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] )
+ mod = importer.load_module(full_name)
+ except ImportError:
+ raise SCons.Errors.UserError, "No platform named '%s'" % name
+ setattr(SCons.Platform, name, mod)
+ return sys.modules[full_name]
+
+def DefaultToolList(platform, env):
+ """Select a default tool list for the specified platform.
+ """
+ return SCons.Tool.tool_list(platform, env)
+
+class PlatformSpec:
+ def __init__(self, name):
+ self.name = name
+
+ def __str__(self):
+ return self.name
+
+class TempFileMunge:
+ """A callable class. You can set an Environment variable to this,
+ then call it with a string argument, then it will perform temporary
+ file substitution on it. This is used to circumvent the long command
+ line limitation.
+
+ Example usage:
+ env["TEMPFILE"] = TempFileMunge
+ env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}"
+
+ By default, the name of the temporary file used begins with a
+ prefix of '@'. This may be configred for other tool chains by
+ setting '$TEMPFILEPREFIX'.
+
+ env["TEMPFILEPREFIX"] = '-@' # diab compiler
+ env["TEMPFILEPREFIX"] = '-via' # arm tool chain
+ """
+ def __init__(self, cmd):
+ self.cmd = cmd
+
+ def __call__(self, target, source, env, for_signature):
+ if for_signature:
+ # If we're being called for signature calculation, it's
+ # because we're being called by the string expansion in
+ # Subst.py, which has the logic to strip any $( $) that
+ # may be in the command line we squirreled away. So we
+ # just return the raw command line and let the upper
+ # string substitution layers do their thing.
+ return self.cmd
+
+ # Now we're actually being called because someone is actually
+ # going to try to execute the command, so we have to do our
+ # own expansion.
+ cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0]
+ try:
+ maxline = int(env.subst('$MAXLINELENGTH'))
+ except ValueError:
+ maxline = 2048
+
+ if (reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)) <= maxline:
+ return self.cmd
+
+ # We do a normpath because mktemp() has what appears to be
+ # a bug in Windows that will use a forward slash as a path
+ # delimiter. Windows's link mistakes that for a command line
+ # switch and barfs.
+ #
+ # We use the .lnk suffix for the benefit of the Phar Lap
+ # linkloc linker, which likes to append an .lnk suffix if
+ # none is given.
+ (fd, tmp) = tempfile.mkstemp('.lnk', text=True)
+ native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp))
+
+ if env['SHELL'] and env['SHELL'] == 'sh':
+ # The sh shell will try to escape the backslashes in the
+ # path, so unescape them.
+ native_tmp = string.replace(native_tmp, '\\', r'\\\\')
+ # In Cygwin, we want to use rm to delete the temporary
+ # file, because del does not exist in the sh shell.
+ rm = env.Detect('rm') or 'del'
+ else:
+ # Don't use 'rm' if the shell is not sh, because rm won't
+ # work with the Windows shells (cmd.exe or command.com) or
+ # Windows path names.
+ rm = 'del'
+
+ prefix = env.subst('$TEMPFILEPREFIX')
+ if not prefix:
+ prefix = '@'
+
+ args = map(SCons.Subst.quote_spaces, cmd[1:])
+ os.write(fd, string.join(args, " ") + "\n")
+ os.close(fd)
+ # XXX Using the SCons.Action.print_actions value directly
+ # like this is bogus, but expedient. This class should
+ # really be rewritten as an Action that defines the
+ # __call__() and strfunction() methods and lets the
+ # normal action-execution logic handle whether or not to
+ # print/execute the action. The problem, though, is all
+ # of that is decided before we execute this method as
+ # part of expanding the $TEMPFILE construction variable.
+ # Consequently, refactoring this will have to wait until
+ # we get more flexible with allowing Actions to exist
+ # independently and get strung together arbitrarily like
+ # Ant tasks. In the meantime, it's going to be more
+ # user-friendly to not let obsession with architectural
+ # purity get in the way of just being helpful, so we'll
+ # reach into SCons.Action directly.
+ if SCons.Action.print_actions:
+ print("Using tempfile "+native_tmp+" for command line:\n"+
+ str(cmd[0]) + " " + string.join(args," "))
+ return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ]
+
+def Platform(name = platform_default()):
+ """Select a canned Platform specification.
+ """
+ module = platform_module(name)
+ spec = PlatformSpec(name)
+ spec.__call__ = module.generate
+ return spec
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/__init__.xml b/src/engine/SCons/Platform/__init__.xml
new file mode 100644
index 0000000..dc5a415
--- /dev/null
+++ b/src/engine/SCons/Platform/__init__.xml
@@ -0,0 +1,183 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<cvar name="ESCAPE">
+<summary>
+A function that will be called to escape shell special characters in
+command lines. The function should take one argument: the command line
+string to escape; and should return the escaped command line.
+</summary>
+</cvar>
+
+<cvar name="LIBPREFIX">
+<summary>
+The prefix used for (static) library file names.
+A default value is set for each platform
+(posix, win32, os2, etc.),
+but the value is overridden by individual tools
+(ar, mslib, sgiar, sunar, tlib, etc.)
+to reflect the names of the libraries they create.
+</summary>
+</cvar>
+
+<cvar name="LIBPREFIXES">
+<summary>
+A list of all legal prefixes for library file names.
+When searching for library dependencies,
+SCons will look for files with these prefixes,
+the base library name,
+and suffixes in the &cv-LIBSUFFIXES; list.
+</summary>
+</cvar>
+
+<cvar name="LIBSUFFIX">
+<summary>
+The suffix used for (static) library file names.
+A default value is set for each platform
+(posix, win32, os2, etc.),
+but the value is overridden by individual tools
+(ar, mslib, sgiar, sunar, tlib, etc.)
+to reflect the names of the libraries they create.
+</summary>
+</cvar>
+
+<cvar name="LIBSUFFIXES">
+<summary>
+A list of all legal suffixes for library file names.
+When searching for library dependencies,
+SCons will look for files with prefixes, in the &cv-LIBPREFIXES; list,
+the base library name,
+and these suffixes.
+</summary>
+</cvar>
+
+<cvar name="OBJPREFIX">
+<summary>
+The prefix used for (static) object file names.
+</summary>
+</cvar>
+
+<cvar name="OBJSUFFIX">
+<summary>
+The suffix used for (static) object file names.
+</summary>
+</cvar>
+
+<cvar name="PLATFORM">
+<summary>
+The name of the platform used to create the Environment. If no platform is
+specified when the Environment is created,
+&scons;
+autodetects the platform.
+
+<example>
+env = Environment(tools = [])
+if env['PLATFORM'] == 'cygwin':
+ Tool('mingw')(env)
+else:
+ Tool('msvc')(env)
+</example>
+</summary>
+</cvar>
+
+<cvar name="HOST_OS">
+ <summary>
+ The name of the host operating system used to create the Environment.
+ If a platform is specified when creating the Environment, then
+ that Platform's logic will handle setting this value.
+ This value is immutable, and should not be changed by the user after
+ the Environment is initialized.
+ Currently only set for Win32.
+ </summary>
+</cvar>
+
+<cvar name="HOST_ARCH">
+ <summary>
+ The name of the host hardware architecture used to create the Environment.
+ If a platform is specified when creating the Environment, then
+ that Platform's logic will handle setting this value.
+ This value is immutable, and should not be changed by the user after
+ the Environment is initialized.
+ Currently only set for Win32.
+ </summary>
+</cvar>
+
+<cvar name="TARGET_OS">
+ <summary>
+ The name of the target operating system for the compiled objects
+ created by this Environment.
+ This defaults to the value of HOST_OS, and the user can override it.
+ Currently only set for Win32.
+ </summary>
+</cvar>
+
+<cvar name="TARGET_ARCH">
+ <summary>
+ The name of the target hardware architecture for the compiled objects
+ created by this Environment.
+ This defaults to the value of HOST_ARCH, and the user can override it.
+ Currently only set for Win32.
+ </summary>
+</cvar>
+
+
+<cvar name="PROGPREFIX">
+<summary>
+The prefix used for executable file names.
+</summary>
+</cvar>
+
+<cvar name="PROGSUFFIX">
+<summary>
+The suffix used for executable file names.
+</summary>
+</cvar>
+
+<cvar name="SHELL">
+<summary>
+A string naming the shell program that will be passed to the
+&cv-SPAWN;
+function.
+See the
+&cv-SPAWN;
+construction variable for more information.
+</summary>
+</cvar>
+
+<cvar name="SHLIBPREFIX">
+<summary>
+The prefix used for shared library file names.
+</summary>
+</cvar>
+
+<cvar name="SHLIBSUFFIX">
+<summary>
+The suffix used for shared library file names.
+</summary>
+</cvar>
+
+<cvar name="SHOBJPREFIX">
+<summary>
+The prefix used for shared object file names.
+</summary>
+</cvar>
+
+<cvar name="SHOBJSUFFIX">
+<summary>
+The suffix used for shared object file names.
+</summary>
+</cvar>
+
+<cvar name="TEMPFILEPREFIX">
+<summary>
+The prefix for a temporary file used
+to execute lines longer than $MAXLINELENGTH.
+The default is '@'.
+This may be set for toolchains that use other values,
+such as '-@' for the diab compiler
+or '-via' for ARM toolchain.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Platform/aix.py b/src/engine/SCons/Platform/aix.py
new file mode 100644
index 0000000..8d6b6e6
--- /dev/null
+++ b/src/engine/SCons/Platform/aix.py
@@ -0,0 +1,70 @@
+"""engine.SCons.Platform.aix
+
+Platform-specific initialization for IBM AIX systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/aix.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import string
+
+import posix
+
+def get_xlc(env, xlc=None, xlc_r=None, packages=[]):
+ # Use the AIX package installer tool lslpp to figure out where a
+ # given xl* compiler is installed and what version it is.
+ xlcPath = None
+ xlcVersion = None
+
+ if xlc is None:
+ xlc = env.get('CC', 'xlc')
+ if xlc_r is None:
+ xlc_r = xlc + '_r'
+ for package in packages:
+ cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'"
+ line = os.popen(cmd).readline()
+ if line:
+ v, p = string.split(line, ':')[1:3]
+ xlcVersion = string.split(v)[1]
+ xlcPath = string.split(p)[0]
+ xlcPath = xlcPath[:xlcPath.rindex('/')]
+ break
+ return (xlcPath, xlc, xlc_r, xlcVersion)
+
+def generate(env):
+ posix.generate(env)
+ #Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion
+ env['MAXLINELENGTH'] = 21576
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py
new file mode 100644
index 0000000..cf6ecce
--- /dev/null
+++ b/src/engine/SCons/Platform/cygwin.py
@@ -0,0 +1,55 @@
+"""SCons.Platform.cygwin
+
+Platform-specific initialization for Cygwin systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/cygwin.py 4577 2009/12/27 19:44:43 scons"
+
+import posix
+from SCons.Platform import TempFileMunge
+
+def generate(env):
+ posix.generate(env)
+
+ env['PROGPREFIX'] = ''
+ env['PROGSUFFIX'] = '.exe'
+ env['SHLIBPREFIX'] = ''
+ env['SHLIBSUFFIX'] = '.dll'
+ env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ]
+ env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['TEMPFILE'] = TempFileMunge
+ env['TEMPFILEPREFIX'] = '@'
+ env['MAXLINELENGTH'] = 2048
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/darwin.py b/src/engine/SCons/Platform/darwin.py
new file mode 100644
index 0000000..35f8fc8
--- /dev/null
+++ b/src/engine/SCons/Platform/darwin.py
@@ -0,0 +1,46 @@
+"""engine.SCons.Platform.darwin
+
+Platform-specific initialization for Mac OS X systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/darwin.py 4577 2009/12/27 19:44:43 scons"
+
+import posix
+
+def generate(env):
+ posix.generate(env)
+ env['SHLIBSUFFIX'] = '.dylib'
+ env['ENV']['PATH'] = env['ENV']['PATH'] + ':/sw/bin'
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/hpux.py b/src/engine/SCons/Platform/hpux.py
new file mode 100644
index 0000000..2d55556
--- /dev/null
+++ b/src/engine/SCons/Platform/hpux.py
@@ -0,0 +1,46 @@
+"""engine.SCons.Platform.hpux
+
+Platform-specific initialization for HP-UX systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/hpux.py 4577 2009/12/27 19:44:43 scons"
+
+import posix
+
+def generate(env):
+ posix.generate(env)
+ #Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion
+ env['MAXLINELENGTH'] = 2045000
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/irix.py b/src/engine/SCons/Platform/irix.py
new file mode 100644
index 0000000..c6a4990
--- /dev/null
+++ b/src/engine/SCons/Platform/irix.py
@@ -0,0 +1,44 @@
+"""SCons.Platform.irix
+
+Platform-specific initialization for SGI IRIX systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/irix.py 4577 2009/12/27 19:44:43 scons"
+
+import posix
+
+def generate(env):
+ posix.generate(env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/os2.py b/src/engine/SCons/Platform/os2.py
new file mode 100644
index 0000000..930a5a9
--- /dev/null
+++ b/src/engine/SCons/Platform/os2.py
@@ -0,0 +1,58 @@
+"""SCons.Platform.os2
+
+Platform-specific initialization for OS/2 systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/os2.py 4577 2009/12/27 19:44:43 scons"
+import win32
+
+def generate(env):
+ if not env.has_key('ENV'):
+ env['ENV'] = {}
+ env['OBJPREFIX'] = ''
+ env['OBJSUFFIX'] = '.obj'
+ env['SHOBJPREFIX'] = '$OBJPREFIX'
+ env['SHOBJSUFFIX'] = '$OBJSUFFIX'
+ env['PROGPREFIX'] = ''
+ env['PROGSUFFIX'] = '.exe'
+ env['LIBPREFIX'] = ''
+ env['LIBSUFFIX'] = '.lib'
+ env['SHLIBPREFIX'] = ''
+ env['SHLIBSUFFIX'] = '.dll'
+ env['LIBPREFIXES'] = '$LIBPREFIX'
+ env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['HOST_OS'] = 'os2'
+ env['HOST_ARCH'] = win32.get_architecture().arch
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py
new file mode 100644
index 0000000..50dfb69
--- /dev/null
+++ b/src/engine/SCons/Platform/posix.py
@@ -0,0 +1,264 @@
+"""SCons.Platform.posix
+
+Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/posix.py 4577 2009/12/27 19:44:43 scons"
+
+import errno
+import os
+import os.path
+import string
+import subprocess
+import sys
+import select
+
+import SCons.Util
+from SCons.Platform import TempFileMunge
+
+exitvalmap = {
+ 2 : 127,
+ 13 : 126,
+}
+
+def escape(arg):
+ "escape shell special characters"
+ slash = '\\'
+ special = '"$()'
+
+ arg = string.replace(arg, slash, slash+slash)
+ for c in special:
+ arg = string.replace(arg, c, slash+c)
+
+ return '"' + arg + '"'
+
+def exec_system(l, env):
+ stat = os.system(string.join(l))
+ if stat & 0xff:
+ return stat | 0x80
+ return stat >> 8
+
+def exec_spawnvpe(l, env):
+ stat = os.spawnvpe(os.P_WAIT, l[0], l, env)
+ # os.spawnvpe() returns the actual exit code, not the encoding
+ # returned by os.waitpid() or os.system().
+ return stat
+
+def exec_fork(l, env):
+ pid = os.fork()
+ if not pid:
+ # Child process.
+ exitval = 127
+ try:
+ os.execvpe(l[0], l, env)
+ except OSError, e:
+ exitval = exitvalmap.get(e[0], e[0])
+ sys.stderr.write("scons: %s: %s\n" % (l[0], e[1]))
+ os._exit(exitval)
+ else:
+ # Parent process.
+ pid, stat = os.waitpid(pid, 0)
+ if stat & 0xff:
+ return stat | 0x80
+ return stat >> 8
+
+def _get_env_command(sh, escape, cmd, args, env):
+ s = string.join(args)
+ if env:
+ l = ['env', '-'] + \
+ map(lambda t, e=escape: e(t[0])+'='+e(t[1]), env.items()) + \
+ [sh, '-c', escape(s)]
+ s = string.join(l)
+ return s
+
+def env_spawn(sh, escape, cmd, args, env):
+ return exec_system([_get_env_command( sh, escape, cmd, args, env)], env)
+
+def spawnvpe_spawn(sh, escape, cmd, args, env):
+ return exec_spawnvpe([sh, '-c', string.join(args)], env)
+
+def fork_spawn(sh, escape, cmd, args, env):
+ return exec_fork([sh, '-c', string.join(args)], env)
+
+def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr):
+ stdout_eof = stderr_eof = 0
+ while not (stdout_eof and stderr_eof):
+ try:
+ (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], [])
+ if cmd_stdout in i:
+ str = cmd_stdout.read()
+ if len(str) == 0:
+ stdout_eof = 1
+ elif stdout is not None:
+ stdout.write(str)
+ if cmd_stderr in i:
+ str = cmd_stderr.read()
+ if len(str) == 0:
+ #sys.__stderr__.write( "stderr_eof=1\n" )
+ stderr_eof = 1
+ else:
+ #sys.__stderr__.write( "str(stderr) = %s\n" % str )
+ stderr.write(str)
+ except select.error, (_errno, _strerror):
+ if _errno != errno.EINTR:
+ raise
+
+def exec_popen3(l, env, stdout, stderr):
+ proc = subprocess.Popen(string.join(l),
+ stdout=stdout,
+ stderr=stderr,
+ shell=True)
+ stat = proc.wait()
+ if stat & 0xff:
+ return stat | 0x80
+ return stat >> 8
+
+def exec_piped_fork(l, env, stdout, stderr):
+ # spawn using fork / exec and providing a pipe for the command's
+ # stdout / stderr stream
+ if stdout != stderr:
+ (rFdOut, wFdOut) = os.pipe()
+ (rFdErr, wFdErr) = os.pipe()
+ else:
+ (rFdOut, wFdOut) = os.pipe()
+ rFdErr = rFdOut
+ wFdErr = wFdOut
+ # do the fork
+ pid = os.fork()
+ if not pid:
+ # Child process
+ os.close( rFdOut )
+ if rFdOut != rFdErr:
+ os.close( rFdErr )
+ os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ?
+ os.dup2( wFdErr, 2 )
+ os.close( wFdOut )
+ if stdout != stderr:
+ os.close( wFdErr )
+ exitval = 127
+ try:
+ os.execvpe(l[0], l, env)
+ except OSError, e:
+ exitval = exitvalmap.get(e[0], e[0])
+ stderr.write("scons: %s: %s\n" % (l[0], e[1]))
+ os._exit(exitval)
+ else:
+ # Parent process
+ pid, stat = os.waitpid(pid, 0)
+ os.close( wFdOut )
+ if stdout != stderr:
+ os.close( wFdErr )
+ childOut = os.fdopen( rFdOut )
+ if stdout != stderr:
+ childErr = os.fdopen( rFdErr )
+ else:
+ childErr = childOut
+ process_cmd_output(childOut, childErr, stdout, stderr)
+ os.close( rFdOut )
+ if stdout != stderr:
+ os.close( rFdErr )
+ if stat & 0xff:
+ return stat | 0x80
+ return stat >> 8
+
+def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr):
+ # spawn using Popen3 combined with the env command
+ # the command name and the command's stdout is written to stdout
+ # the command's stderr is written to stderr
+ return exec_popen3([_get_env_command(sh, escape, cmd, args, env)],
+ env, stdout, stderr)
+
+def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
+ # spawn using fork / exec and providing a pipe for the command's
+ # stdout / stderr stream
+ return exec_piped_fork([sh, '-c', string.join(args)],
+ env, stdout, stderr)
+
+
+
+def generate(env):
+ # If os.spawnvpe() exists, we use it to spawn commands. Otherwise
+ # if the env utility exists, we use os.system() to spawn commands,
+ # finally we fall back on os.fork()/os.exec().
+ #
+ # os.spawnvpe() is prefered because it is the most efficient. But
+ # for Python versions without it, os.system() is prefered because it
+ # is claimed that it works better with threads (i.e. -j) and is more
+ # efficient than forking Python.
+ #
+ # NB: Other people on the scons-users mailing list have claimed that
+ # os.fork()/os.exec() works better than os.system(). There may just
+ # not be a default that works best for all users.
+
+ if os.__dict__.has_key('spawnvpe'):
+ spawn = spawnvpe_spawn
+ elif env.Detect('env'):
+ spawn = env_spawn
+ else:
+ spawn = fork_spawn
+
+ if env.Detect('env'):
+ pspawn = piped_env_spawn
+ else:
+ pspawn = piped_fork_spawn
+
+ if not env.has_key('ENV'):
+ env['ENV'] = {}
+ env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin'
+ env['OBJPREFIX'] = ''
+ env['OBJSUFFIX'] = '.o'
+ env['SHOBJPREFIX'] = '$OBJPREFIX'
+ env['SHOBJSUFFIX'] = '$OBJSUFFIX'
+ env['PROGPREFIX'] = ''
+ env['PROGSUFFIX'] = ''
+ env['LIBPREFIX'] = 'lib'
+ env['LIBSUFFIX'] = '.a'
+ env['SHLIBPREFIX'] = '$LIBPREFIX'
+ env['SHLIBSUFFIX'] = '.so'
+ env['LIBPREFIXES'] = [ '$LIBPREFIX' ]
+ env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['PSPAWN'] = pspawn
+ env['SPAWN'] = spawn
+ env['SHELL'] = 'sh'
+ env['ESCAPE'] = escape
+ env['TEMPFILE'] = TempFileMunge
+ env['TEMPFILEPREFIX'] = '@'
+ #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion
+ #Note: specific platforms might rise or lower this value
+ env['MAXLINELENGTH'] = 128072
+
+ # This platform supports RPATH specifications.
+ env['__RPATH'] = '$_RPATH'
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/posix.xml b/src/engine/SCons/Platform/posix.xml
new file mode 100644
index 0000000..6336202
--- /dev/null
+++ b/src/engine/SCons/Platform/posix.xml
@@ -0,0 +1,51 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<cvar name="RPATH">
+<summary>
+A list of paths to search for shared libraries when running programs.
+Currently only used in the GNU (gnulink),
+IRIX (sgilink) and Sun (sunlink) linkers.
+Ignored on platforms and toolchains that don't support it.
+Note that the paths added to RPATH
+are not transformed by
+&scons;
+in any way: if you want an absolute
+path, you must make it absolute yourself.
+</summary>
+</cvar>
+
+<cvar name="_RPATH">
+<summary>
+An automatically-generated construction variable
+containing the rpath flags to be used when linking
+a program with shared libraries.
+The value of &cv-_RPATH; is created
+by appending &cv-RPATHPREFIX; and &cv-RPATHSUFFIX;
+to the beginning and end
+of each directory in &cv-RPATH;.
+</summary>
+</cvar>
+
+<cvar name="RPATHPREFIX">
+<summary>
+The prefix used to specify a directory to be searched for
+shared libraries when running programs.
+This will be appended to the beginning of each directory
+in the &cv-RPATH; construction variable
+when the &cv-_RPATH; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="RPATHSUFFIX">
+<summary>
+The suffix used to specify a directory to be searched for
+shared libraries when running programs.
+This will be appended to the end of each directory
+in the &cv-RPATH; construction variable
+when the &cv-_RPATH; variable is automatically generated.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Platform/sunos.py b/src/engine/SCons/Platform/sunos.py
new file mode 100644
index 0000000..5604598
--- /dev/null
+++ b/src/engine/SCons/Platform/sunos.py
@@ -0,0 +1,50 @@
+"""engine.SCons.Platform.sunos
+
+Platform-specific initialization for Sun systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/sunos.py 4577 2009/12/27 19:44:43 scons"
+
+import posix
+
+def generate(env):
+ posix.generate(env)
+ # Based on sunSparc 8:32bit
+ # ARG_MAX=1048320 - 3000 for environment expansion
+ env['MAXLINELENGTH'] = 1045320
+ env['PKGINFO'] = 'pkginfo'
+ env['PKGCHK'] = '/usr/sbin/pkgchk'
+ env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin'
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/sunos.xml b/src/engine/SCons/Platform/sunos.xml
new file mode 100644
index 0000000..72dd85f
--- /dev/null
+++ b/src/engine/SCons/Platform/sunos.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+
+<cvar name="PKGCHK">
+<summary>
+On Solaris systems,
+the package-checking program that will
+be used (along with &cv-PKGINFO;)
+to look for installed versions of
+the Sun PRO C++ compiler.
+The default is
+<filename>/usr/sbin/pgkchk</filename>.
+</summary>
+</cvar>
+
+<cvar name="PKGINFO">
+<summary>
+On Solaris systems,
+the package information program that will
+be used (along with &cv-PKGCHK;)
+to look for installed versions of
+the Sun PRO C++ compiler.
+The default is
+<filename>pkginfo</filename>.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py
new file mode 100644
index 0000000..5672315
--- /dev/null
+++ b/src/engine/SCons/Platform/win32.py
@@ -0,0 +1,386 @@
+"""SCons.Platform.win32
+
+Platform-specific initialization for Win32 systems.
+
+There normally shouldn't be any need to import this module directly. It
+will usually be imported through the generic SCons.Platform.Platform()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Platform/win32.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+import sys
+import tempfile
+
+from SCons.Platform.posix import exitvalmap
+from SCons.Platform import TempFileMunge
+import SCons.Util
+
+try:
+ import msvcrt
+ import win32api
+ import win32con
+
+ msvcrt.get_osfhandle
+ win32api.SetHandleInformation
+ win32con.HANDLE_FLAG_INHERIT
+except ImportError:
+ parallel_msg = \
+ "you do not seem to have the pywin32 extensions installed;\n" + \
+ "\tparallel (-j) builds may not work reliably with open Python files."
+except AttributeError:
+ parallel_msg = \
+ "your pywin32 extensions do not support file handle operations;\n" + \
+ "\tparallel (-j) builds may not work reliably with open Python files."
+else:
+ parallel_msg = None
+
+ import __builtin__
+
+ _builtin_file = __builtin__.file
+ _builtin_open = __builtin__.open
+
+ def _scons_file(*args, **kw):
+ fp = apply(_builtin_file, args, kw)
+ win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()),
+ win32con.HANDLE_FLAG_INHERIT,
+ 0)
+ return fp
+
+ def _scons_open(*args, **kw):
+ fp = apply(_builtin_open, args, kw)
+ win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()),
+ win32con.HANDLE_FLAG_INHERIT,
+ 0)
+ return fp
+
+ __builtin__.file = _scons_file
+ __builtin__.open = _scons_open
+
+
+
+# The upshot of all this is that, if you are using Python 1.5.2,
+# you had better have cmd or command.com in your PATH when you run
+# scons.
+
+def piped_spawn(sh, escape, cmd, args, env, stdout, stderr):
+ # There is no direct way to do that in python. What we do
+ # here should work for most cases:
+ # In case stdout (stderr) is not redirected to a file,
+ # we redirect it into a temporary file tmpFileStdout
+ # (tmpFileStderr) and copy the contents of this file
+ # to stdout (stderr) given in the argument
+ if not sh:
+ sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
+ return 127
+ else:
+ # one temporary file for stdout and stderr
+ tmpFileStdout = os.path.normpath(tempfile.mktemp())
+ tmpFileStderr = os.path.normpath(tempfile.mktemp())
+
+ # check if output is redirected
+ stdoutRedirected = 0
+ stderrRedirected = 0
+ for arg in args:
+ # are there more possibilities to redirect stdout ?
+ if (string.find( arg, ">", 0, 1 ) != -1 or
+ string.find( arg, "1>", 0, 2 ) != -1):
+ stdoutRedirected = 1
+ # are there more possibilities to redirect stderr ?
+ if string.find( arg, "2>", 0, 2 ) != -1:
+ stderrRedirected = 1
+
+ # redirect output of non-redirected streams to our tempfiles
+ if stdoutRedirected == 0:
+ args.append(">" + str(tmpFileStdout))
+ if stderrRedirected == 0:
+ args.append("2>" + str(tmpFileStderr))
+
+ # actually do the spawn
+ try:
+ args = [sh, '/C', escape(string.join(args)) ]
+ ret = os.spawnve(os.P_WAIT, sh, args, env)
+ except OSError, e:
+ # catch any error
+ try:
+ ret = exitvalmap[e[0]]
+ except KeyError:
+ sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1]))
+ if stderr is not None:
+ stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+ # copy child output from tempfiles to our streams
+ # and do clean up stuff
+ if stdout is not None and stdoutRedirected == 0:
+ try:
+ stdout.write(open( tmpFileStdout, "r" ).read())
+ os.remove( tmpFileStdout )
+ except (IOError, OSError):
+ pass
+
+ if stderr is not None and stderrRedirected == 0:
+ try:
+ stderr.write(open( tmpFileStderr, "r" ).read())
+ os.remove( tmpFileStderr )
+ except (IOError, OSError):
+ pass
+ return ret
+
+def exec_spawn(l, env):
+ try:
+ result = os.spawnve(os.P_WAIT, l[0], l, env)
+ except OSError, e:
+ try:
+ result = exitvalmap[e[0]]
+ sys.stderr.write("scons: %s: %s\n" % (l[0], e[1]))
+ except KeyError:
+ result = 127
+ if len(l) > 2:
+ if len(l[2]) < 1000:
+ command = string.join(l[0:3])
+ else:
+ command = l[0]
+ else:
+ command = l[0]
+ sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1]))
+ return result
+
+def spawn(sh, escape, cmd, args, env):
+ if not sh:
+ sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
+ return 127
+ return exec_spawn([sh, '/C', escape(string.join(args))], env)
+
+# Windows does not allow special characters in file names anyway, so no
+# need for a complex escape function, we will just quote the arg, except
+# that "cmd /c" requires that if an argument ends with a backslash it
+# needs to be escaped so as not to interfere with closing double quote
+# that we add.
+def escape(x):
+ if x[-1] == '\\':
+ x = x + '\\'
+ return '"' + x + '"'
+
+# Get the windows system directory name
+_system_root = None
+
+def get_system_root():
+ global _system_root
+ if _system_root is not None:
+ return _system_root
+
+ # A resonable default if we can't read the registry
+ val = os.environ.get('SystemRoot', "C:/WINDOWS")
+
+ if SCons.Util.can_read_reg:
+ try:
+ # Look for Windows NT system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows NT\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ except SCons.Util.RegError:
+ try:
+ # Okay, try the Windows 9x system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ except KeyboardInterrupt:
+ raise
+ except:
+ pass
+ _system_root = val
+ return val
+
+# Get the location of the program files directory
+def get_program_files_dir():
+ # Now see if we can look in the registry...
+ val = ''
+ if SCons.Util.can_read_reg:
+ try:
+ # Look for Windows Program Files directory
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir')
+ except SCons.Util.RegError:
+ val = ''
+ pass
+
+ if val == '':
+ # A reasonable default if we can't read the registry
+ # (Actually, it's pretty reasonable even if we can :-)
+ val = os.path.join(os.path.dirname(get_system_root()),"Program Files")
+
+ return val
+
+
+
+# Determine which windows CPU were running on.
+class ArchDefinition:
+ """
+ A class for defining architecture-specific settings and logic.
+ """
+ def __init__(self, arch, synonyms=[]):
+ self.arch = arch
+ self.synonyms = synonyms
+
+SupportedArchitectureList = [
+ ArchDefinition(
+ 'x86',
+ ['i386', 'i486', 'i586', 'i686'],
+ ),
+
+ ArchDefinition(
+ 'x86_64',
+ ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'],
+ ),
+
+ ArchDefinition(
+ 'ia64',
+ ['IA64'],
+ ),
+]
+
+SupportedArchitectureMap = {}
+for a in SupportedArchitectureList:
+ SupportedArchitectureMap[a.arch] = a
+ for s in a.synonyms:
+ SupportedArchitectureMap[s] = a
+
+def get_architecture(arch=None):
+ """Returns the definition for the specified architecture string.
+
+ If no string is specified, the system default is returned (as defined
+ by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment
+ variables).
+ """
+ if arch is None:
+ arch = os.environ.get('PROCESSOR_ARCHITEW6432')
+ if not arch:
+ arch = os.environ.get('PROCESSOR_ARCHITECTURE')
+ return SupportedArchitectureMap.get(arch, ArchDefinition('', ['']))
+
+def generate(env):
+ # Attempt to find cmd.exe (for WinNT/2k/XP) or
+ # command.com for Win9x
+ cmd_interp = ''
+ # First see if we can look in the registry...
+ if SCons.Util.can_read_reg:
+ try:
+ # Look for Windows NT system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows NT\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ cmd_interp = os.path.join(val, 'System32\\cmd.exe')
+ except SCons.Util.RegError:
+ try:
+ # Okay, try the Windows 9x system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ cmd_interp = os.path.join(val, 'command.com')
+ except KeyboardInterrupt:
+ raise
+ except:
+ pass
+
+ # For the special case of not having access to the registry, we
+ # use a temporary path and pathext to attempt to find the command
+ # interpreter. If we fail, we try to find the interpreter through
+ # the env's PATH. The problem with that is that it might not
+ # contain an ENV and a PATH.
+ if not cmd_interp:
+ systemroot = get_system_root()
+ tmp_path = systemroot + os.pathsep + \
+ os.path.join(systemroot,'System32')
+ tmp_pathext = '.com;.exe;.bat;.cmd'
+ if os.environ.has_key('PATHEXT'):
+ tmp_pathext = os.environ['PATHEXT']
+ cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext)
+ if not cmd_interp:
+ cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext)
+
+ if not cmd_interp:
+ cmd_interp = env.Detect('cmd')
+ if not cmd_interp:
+ cmd_interp = env.Detect('command')
+
+
+ if not env.has_key('ENV'):
+ env['ENV'] = {}
+
+ # Import things from the external environment to the construction
+ # environment's ENV. This is a potential slippery slope, because we
+ # *don't* want to make builds dependent on the user's environment by
+ # default. We're doing this for SystemRoot, though, because it's
+ # needed for anything that uses sockets, and seldom changes, and
+ # for SystemDrive because it's related.
+ #
+ # Weigh the impact carefully before adding other variables to this list.
+ import_env = [ 'SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ]
+ for var in import_env:
+ v = os.environ.get(var)
+ if v:
+ env['ENV'][var] = v
+
+ if not env['ENV'].has_key('COMSPEC'):
+ v = os.environ.get("COMSPEC")
+ if v:
+ env['ENV']['COMSPEC'] = v
+
+ env.AppendENVPath('PATH', get_system_root() + '\System32')
+
+ env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD'
+ env['OBJPREFIX'] = ''
+ env['OBJSUFFIX'] = '.obj'
+ env['SHOBJPREFIX'] = '$OBJPREFIX'
+ env['SHOBJSUFFIX'] = '$OBJSUFFIX'
+ env['PROGPREFIX'] = ''
+ env['PROGSUFFIX'] = '.exe'
+ env['LIBPREFIX'] = ''
+ env['LIBSUFFIX'] = '.lib'
+ env['SHLIBPREFIX'] = ''
+ env['SHLIBSUFFIX'] = '.dll'
+ env['LIBPREFIXES'] = [ '$LIBPREFIX' ]
+ env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ]
+ env['PSPAWN'] = piped_spawn
+ env['SPAWN'] = spawn
+ env['SHELL'] = cmd_interp
+ env['TEMPFILE'] = TempFileMunge
+ env['TEMPFILEPREFIX'] = '@'
+ env['MAXLINELENGTH'] = 2048
+ env['ESCAPE'] = escape
+
+ env['HOST_OS'] = 'win32'
+ env['HOST_ARCH'] = get_architecture().arch
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/win32.xml b/src/engine/SCons/Platform/win32.xml
new file mode 100644
index 0000000..dc60c90
--- /dev/null
+++ b/src/engine/SCons/Platform/win32.xml
@@ -0,0 +1,14 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<cvar name="MAXLINELENGTH">
+<summary>
+The maximum number of characters allowed on an external command line.
+On Win32 systems,
+link lines longer than this many characters
+are linked via a temporary file name.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py
new file mode 100644
index 0000000..0151cf7
--- /dev/null
+++ b/src/engine/SCons/SConf.py
@@ -0,0 +1,1038 @@
+"""SCons.SConf
+
+Autoconf-like configuration support.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/SConf.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import re
+import string
+import StringIO
+import sys
+import traceback
+import types
+
+import SCons.Action
+import SCons.Builder
+import SCons.Errors
+import SCons.Job
+import SCons.Node.FS
+import SCons.Taskmaster
+import SCons.Util
+import SCons.Warnings
+import SCons.Conftest
+
+from SCons.Debug import Trace
+
+# Turn off the Conftest error logging
+SCons.Conftest.LogInputFiles = 0
+SCons.Conftest.LogErrorMessages = 0
+
+# Set
+build_type = None
+build_types = ['clean', 'help']
+
+def SetBuildType(type):
+ global build_type
+ build_type = type
+
+# to be set, if we are in dry-run mode
+dryrun = 0
+
+AUTO=0 # use SCons dependency scanning for up-to-date checks
+FORCE=1 # force all tests to be rebuilt
+CACHE=2 # force all tests to be taken from cache (raise an error, if necessary)
+cache_mode = AUTO
+
+def SetCacheMode(mode):
+ """Set the Configure cache mode. mode must be one of "auto", "force",
+ or "cache"."""
+ global cache_mode
+ if mode == "auto":
+ cache_mode = AUTO
+ elif mode == "force":
+ cache_mode = FORCE
+ elif mode == "cache":
+ cache_mode = CACHE
+ else:
+ raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
+
+progress_display = SCons.Util.display # will be overwritten by SCons.Script
+def SetProgressDisplay(display):
+ """Set the progress display to use (called from SCons.Script)"""
+ global progress_display
+ progress_display = display
+
+SConfFS = None
+
+_ac_build_counter = 0 # incremented, whenever TryBuild is called
+_ac_config_logs = {} # all config.log files created in this build
+_ac_config_hs = {} # all config.h files created in this build
+sconf_global = None # current sconf object
+
+def _createConfigH(target, source, env):
+ t = open(str(target[0]), "w")
+ defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0])))
+ t.write("""#ifndef %(DEFNAME)s_SEEN
+#define %(DEFNAME)s_SEEN
+
+""" % {'DEFNAME' : defname})
+ t.write(source[0].get_contents())
+ t.write("""
+#endif /* %(DEFNAME)s_SEEN */
+""" % {'DEFNAME' : defname})
+ t.close()
+
+def _stringConfigH(target, source, env):
+ return "scons: Configure: creating " + str(target[0])
+
+def CreateConfigHBuilder(env):
+ """Called just before the building targets phase begins."""
+ if len(_ac_config_hs) == 0:
+ return
+ action = SCons.Action.Action(_createConfigH,
+ _stringConfigH)
+ sconfigHBld = SCons.Builder.Builder(action=action)
+ env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} )
+ for k in _ac_config_hs.keys():
+ env.SConfigHBuilder(k, env.Value(_ac_config_hs[k]))
+
+class SConfWarning(SCons.Warnings.Warning):
+ pass
+SCons.Warnings.enableWarningClass(SConfWarning)
+
+# some error definitions
+class SConfError(SCons.Errors.UserError):
+ def __init__(self,msg):
+ SCons.Errors.UserError.__init__(self,msg)
+
+class ConfigureDryRunError(SConfError):
+ """Raised when a file or directory needs to be updated during a Configure
+ process, but the user requested a dry-run"""
+ def __init__(self,target):
+ if not isinstance(target, SCons.Node.FS.File):
+ msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
+ else:
+ msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
+ SConfError.__init__(self,msg)
+
+class ConfigureCacheError(SConfError):
+ """Raised when a use explicitely requested the cache feature, but the test
+ is run the first time."""
+ def __init__(self,target):
+ SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
+
+# define actions for building text files
+def _createSource( target, source, env ):
+ fd = open(str(target[0]), "w")
+ fd.write(source[0].get_contents())
+ fd.close()
+def _stringSource( target, source, env ):
+ return (str(target[0]) + ' <-\n |' +
+ string.replace( source[0].get_contents(),
+ '\n', "\n |" ) )
+
+# python 2.2 introduces types.BooleanType
+BooleanTypes = [types.IntType]
+if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
+
+class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
+ """
+ Special build info for targets of configure tests. Additional members
+ are result (did the builder succeed last time?) and string, which
+ contains messages of the original build phase.
+ """
+ result = None # -> 0/None -> no error, != 0 error
+ string = None # the stdout / stderr output when building the target
+
+ def set_build_result(self, result, string):
+ self.result = result
+ self.string = string
+
+
+class Streamer:
+ """
+ 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
+ """
+ def __init__(self, orig):
+ self.orig = orig
+ self.s = StringIO.StringIO()
+
+ def write(self, str):
+ if self.orig:
+ self.orig.write(str)
+ self.s.write(str)
+
+ def writelines(self, lines):
+ for l in lines:
+ self.write(l + '\n')
+
+ def getvalue(self):
+ """
+ Return everything written to orig since the Streamer was created.
+ """
+ return self.s.getvalue()
+
+ def flush(self):
+ if self.orig:
+ self.orig.flush()
+ self.s.flush()
+
+
+class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
+ """
+ This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
+ correctly and knows about the current cache_mode.
+ """
+ def display(self, message):
+ if sconf_global.logstream:
+ sconf_global.logstream.write("scons: Configure: " + message + "\n")
+
+ def display_cached_string(self, bi):
+ """
+ Logs the original builder messages, given the SConfBuildInfo instance
+ bi.
+ """
+ if not isinstance(bi, SConfBuildInfo):
+ SCons.Warnings.warn(SConfWarning,
+ "The stored build information has an unexpected class: %s" % bi.__class__)
+ else:
+ self.display("The original builder output was:\n" +
+ string.replace(" |" + str(bi.string),
+ "\n", "\n |"))
+
+ def failed(self):
+ # check, if the reason was a ConfigureDryRunError or a
+ # ConfigureCacheError and if yes, reraise the exception
+ exc_type = self.exc_info()[0]
+ if issubclass(exc_type, SConfError):
+ raise
+ elif issubclass(exc_type, SCons.Errors.BuildError):
+ # we ignore Build Errors (occurs, when a test doesn't pass)
+ # Clear the exception to prevent the contained traceback
+ # to build a reference cycle.
+ self.exc_clear()
+ else:
+ self.display('Caught exception while building "%s":\n' %
+ self.targets[0])
+ try:
+ excepthook = sys.excepthook
+ except AttributeError:
+ # Earlier versions of Python don't have sys.excepthook...
+ def excepthook(type, value, tb):
+ traceback.print_tb(tb)
+ print type, value
+ apply(excepthook, self.exc_info())
+ return SCons.Taskmaster.Task.failed(self)
+
+ def collect_node_states(self):
+ # returns (is_up_to_date, cached_error, cachable)
+ # where is_up_to_date is 1, if the node(s) are up_to_date
+ # cached_error is 1, if the node(s) are up_to_date, but the
+ # build will fail
+ # cachable is 0, if some nodes are not in our cache
+ T = 0
+ changed = False
+ cached_error = False
+ cachable = True
+ for t in self.targets:
+ if T: Trace('%s' % (t))
+ bi = t.get_stored_info().binfo
+ if isinstance(bi, SConfBuildInfo):
+ if T: Trace(': SConfBuildInfo')
+ if cache_mode == CACHE:
+ t.set_state(SCons.Node.up_to_date)
+ if T: Trace(': set_state(up_to-date)')
+ else:
+ if T: Trace(': get_state() %s' % t.get_state())
+ if T: Trace(': changed() %s' % t.changed())
+ if (t.get_state() != SCons.Node.up_to_date and t.changed()):
+ changed = True
+ if T: Trace(': changed %s' % changed)
+ cached_error = cached_error or bi.result
+ else:
+ if T: Trace(': else')
+ # the node hasn't been built in a SConf context or doesn't
+ # exist
+ cachable = False
+ changed = ( t.get_state() != SCons.Node.up_to_date )
+ if T: Trace(': changed %s' % changed)
+ if T: Trace('\n')
+ return (not changed, cached_error, cachable)
+
+ def execute(self):
+ if not self.targets[0].has_builder():
+ return
+
+ sconf = sconf_global
+
+ is_up_to_date, cached_error, cachable = self.collect_node_states()
+
+ if cache_mode == CACHE and not cachable:
+ raise ConfigureCacheError(self.targets[0])
+ elif cache_mode == FORCE:
+ is_up_to_date = 0
+
+ if cached_error and is_up_to_date:
+ self.display("Building \"%s\" failed in a previous run and all "
+ "its sources are up to date." % str(self.targets[0]))
+ binfo = self.targets[0].get_stored_info().binfo
+ self.display_cached_string(binfo)
+ raise SCons.Errors.BuildError # will be 'caught' in self.failed
+ elif is_up_to_date:
+ self.display("\"%s\" is up to date." % str(self.targets[0]))
+ binfo = self.targets[0].get_stored_info().binfo
+ self.display_cached_string(binfo)
+ elif dryrun:
+ raise ConfigureDryRunError(self.targets[0])
+ else:
+ # note stdout and stderr are the same here
+ s = sys.stdout = sys.stderr = Streamer(sys.stdout)
+ try:
+ env = self.targets[0].get_build_env()
+ if cache_mode == FORCE:
+ # Set up the Decider() to force rebuilds by saying
+ # that every source has changed. Note that we still
+ # call the environment's underlying source decider so
+ # that the correct .sconsign info will get calculated
+ # and keep the build state consistent.
+ def force_build(dependency, target, prev_ni,
+ env_decider=env.decide_source):
+ env_decider(dependency, target, prev_ni)
+ return True
+ if env.decide_source.func_code is not force_build.func_code:
+ env.Decider(force_build)
+ env['PSTDOUT'] = env['PSTDERR'] = s
+ try:
+ sconf.cached = 0
+ self.targets[0].build()
+ finally:
+ sys.stdout = sys.stderr = env['PSTDOUT'] = \
+ env['PSTDERR'] = sconf.logstream
+ except KeyboardInterrupt:
+ raise
+ except SystemExit:
+ exc_value = sys.exc_info()[1]
+ raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
+ except Exception, e:
+ for t in self.targets:
+ binfo = t.get_binfo()
+ binfo.__class__ = SConfBuildInfo
+ binfo.set_build_result(1, s.getvalue())
+ sconsign_entry = SCons.SConsign.SConsignEntry()
+ sconsign_entry.binfo = binfo
+ #sconsign_entry.ninfo = self.get_ninfo()
+ # We'd like to do this as follows:
+ # t.store_info(binfo)
+ # However, we need to store it as an SConfBuildInfo
+ # object, and store_info() will turn it into a
+ # regular FileNodeInfo if the target is itself a
+ # regular File.
+ sconsign = t.dir.sconsign()
+ sconsign.set_entry(t.name, sconsign_entry)
+ sconsign.merge()
+ raise e
+ else:
+ for t in self.targets:
+ binfo = t.get_binfo()
+ binfo.__class__ = SConfBuildInfo
+ binfo.set_build_result(0, s.getvalue())
+ sconsign_entry = SCons.SConsign.SConsignEntry()
+ sconsign_entry.binfo = binfo
+ #sconsign_entry.ninfo = self.get_ninfo()
+ # We'd like to do this as follows:
+ # t.store_info(binfo)
+ # However, we need to store it as an SConfBuildInfo
+ # object, and store_info() will turn it into a
+ # regular FileNodeInfo if the target is itself a
+ # regular File.
+ sconsign = t.dir.sconsign()
+ sconsign.set_entry(t.name, sconsign_entry)
+ sconsign.merge()
+
+class SConfBase:
+ """This is simply a class to represent a configure context. After
+ creating a SConf object, you can call any tests. After finished with your
+ tests, be sure to call the Finish() method, which returns the modified
+ environment.
+ Some words about caching: In most cases, it is not necessary to cache
+ Test results explicitely. Instead, we use the scons dependency checking
+ mechanism. For example, if one wants to compile a test program
+ (SConf.TryLink), the compiler is only called, if the program dependencies
+ have changed. However, if the program could not be compiled in a former
+ SConf run, we need to explicitely cache this error.
+ """
+
+ def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
+ log_file='$CONFIGURELOG', config_h = None, _depth = 0):
+ """Constructor. Pass additional tests in the custom_tests-dictinary,
+ e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
+ defines a custom test.
+ Note also the conf_dir and log_file arguments (you may want to
+ build tests in the VariantDir, not in the SourceDir)
+ """
+ global SConfFS
+ if not SConfFS:
+ SConfFS = SCons.Node.FS.default_fs or \
+ SCons.Node.FS.FS(env.fs.pathTop)
+ if sconf_global is not None:
+ raise (SCons.Errors.UserError,
+ "Only one SConf object may be active at one time")
+ self.env = env
+ if log_file is not None:
+ log_file = SConfFS.File(env.subst(log_file))
+ self.logfile = log_file
+ self.logstream = None
+ self.lastTarget = None
+ self.depth = _depth
+ self.cached = 0 # will be set, if all test results are cached
+
+ # add default tests
+ default_tests = {
+ 'CheckCC' : CheckCC,
+ 'CheckCXX' : CheckCXX,
+ 'CheckSHCC' : CheckSHCC,
+ 'CheckSHCXX' : CheckSHCXX,
+ 'CheckFunc' : CheckFunc,
+ 'CheckType' : CheckType,
+ 'CheckTypeSize' : CheckTypeSize,
+ 'CheckDeclaration' : CheckDeclaration,
+ 'CheckHeader' : CheckHeader,
+ 'CheckCHeader' : CheckCHeader,
+ 'CheckCXXHeader' : CheckCXXHeader,
+ 'CheckLib' : CheckLib,
+ 'CheckLibWithHeader' : CheckLibWithHeader,
+ }
+ self.AddTests(default_tests)
+ self.AddTests(custom_tests)
+ self.confdir = SConfFS.Dir(env.subst(conf_dir))
+ if config_h is not None:
+ config_h = SConfFS.File(config_h)
+ self.config_h = config_h
+ self._startup()
+
+ def Finish(self):
+ """Call this method after finished with your tests:
+ env = sconf.Finish()
+ """
+ self._shutdown()
+ return self.env
+
+ def Define(self, name, value = None, comment = None):
+ """
+ Define a pre processor symbol name, with the optional given value in the
+ current config header.
+
+ If value is None (default), then #define name is written. If value is not
+ none, then #define name value is written.
+
+ comment is a string which will be put as a C comment in the
+ header, to explain the meaning of the value (appropriate C comments /* and
+ */ will be put automatically."""
+ lines = []
+ if comment:
+ comment_str = "/* %s */" % comment
+ lines.append(comment_str)
+
+ if value is not None:
+ define_str = "#define %s %s" % (name, value)
+ else:
+ define_str = "#define %s" % name
+ lines.append(define_str)
+ lines.append('')
+
+ self.config_h_text = self.config_h_text + string.join(lines, '\n')
+
+ def BuildNodes(self, nodes):
+ """
+ Tries to build the given nodes immediately. Returns 1 on success,
+ 0 on error.
+ """
+ if self.logstream is not None:
+ # override stdout / stderr to write in log file
+ oldStdout = sys.stdout
+ sys.stdout = self.logstream
+ oldStderr = sys.stderr
+ sys.stderr = self.logstream
+
+ # the engine assumes the current path is the SConstruct directory ...
+ old_fs_dir = SConfFS.getcwd()
+ old_os_dir = os.getcwd()
+ SConfFS.chdir(SConfFS.Top, change_os_dir=1)
+
+ # Because we take responsibility here for writing out our
+ # own .sconsign info (see SConfBuildTask.execute(), above),
+ # we override the store_info() method with a null place-holder
+ # so we really control how it gets written.
+ for n in nodes:
+ n.store_info = n.do_not_store_info
+
+ ret = 1
+
+ try:
+ # ToDo: use user options for calc
+ save_max_drift = SConfFS.get_max_drift()
+ SConfFS.set_max_drift(0)
+ tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
+ # we don't want to build tests in parallel
+ jobs = SCons.Job.Jobs(1, tm )
+ jobs.run()
+ for n in nodes:
+ state = n.get_state()
+ if (state != SCons.Node.executed and
+ state != SCons.Node.up_to_date):
+ # the node could not be built. we return 0 in this case
+ ret = 0
+ finally:
+ SConfFS.set_max_drift(save_max_drift)
+ os.chdir(old_os_dir)
+ SConfFS.chdir(old_fs_dir, change_os_dir=0)
+ if self.logstream is not None:
+ # restore stdout / stderr
+ sys.stdout = oldStdout
+ sys.stderr = oldStderr
+ return ret
+
+ def pspawn_wrapper(self, sh, escape, cmd, args, env):
+ """Wrapper function for handling piped spawns.
+
+ This looks to the calling interface (in Action.py) like a "normal"
+ spawn, but associates the call with the PSPAWN variable from
+ the construction environment and with the streams to which we
+ want the output logged. This gets slid into the construction
+ environment as the SPAWN variable so Action.py doesn't have to
+ know or care whether it's spawning a piped command or not.
+ """
+ return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
+
+
+ def TryBuild(self, builder, text = None, extension = ""):
+ """Low level TryBuild implementation. Normally you don't need to
+ call that - you can use TryCompile / TryLink / TryRun instead
+ """
+ global _ac_build_counter
+
+ # Make sure we have a PSPAWN value, and save the current
+ # SPAWN value.
+ try:
+ self.pspawn = self.env['PSPAWN']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
+ try:
+ save_spawn = self.env['SPAWN']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+
+ nodesToBeBuilt = []
+
+ f = "conftest_" + str(_ac_build_counter)
+ pref = self.env.subst( builder.builder.prefix )
+ suff = self.env.subst( builder.builder.suffix )
+ target = self.confdir.File(pref + f + suff)
+
+ try:
+ # Slide our wrapper into the construction environment as
+ # the SPAWN function.
+ self.env['SPAWN'] = self.pspawn_wrapper
+ sourcetext = self.env.Value(text)
+
+ if text is not None:
+ textFile = self.confdir.File(f + extension)
+ textFileNode = self.env.SConfSourceBuilder(target=textFile,
+ source=sourcetext)
+ nodesToBeBuilt.extend(textFileNode)
+ source = textFileNode
+ else:
+ source = None
+
+ nodes = builder(target = target, source = source)
+ if not SCons.Util.is_List(nodes):
+ nodes = [nodes]
+ nodesToBeBuilt.extend(nodes)
+ result = self.BuildNodes(nodesToBeBuilt)
+
+ finally:
+ self.env['SPAWN'] = save_spawn
+
+ _ac_build_counter = _ac_build_counter + 1
+ if result:
+ self.lastTarget = nodes[0]
+ else:
+ self.lastTarget = None
+
+ return result
+
+ def TryAction(self, action, text = None, extension = ""):
+ """Tries to execute the given action with optional source file
+ contents <text> and optional source file extension <extension>,
+ Returns the status (0 : failed, 1 : ok) and the contents of the
+ output file.
+ """
+ builder = SCons.Builder.Builder(action=action)
+ self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
+ ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
+ del self.env['BUILDERS']['SConfActionBuilder']
+ if ok:
+ outputStr = self.lastTarget.get_contents()
+ return (1, outputStr)
+ return (0, "")
+
+ def TryCompile( self, text, extension):
+ """Compiles the program given in text to an env.Object, using extension
+ as file extension (e.g. '.c'). Returns 1, if compilation was
+ successful, 0 otherwise. The target is saved in self.lastTarget (for
+ further processing).
+ """
+ return self.TryBuild(self.env.Object, text, extension)
+
+ def TryLink( self, text, extension ):
+ """Compiles the program given in text to an executable env.Program,
+ using extension as file extension (e.g. '.c'). Returns 1, if
+ compilation was successful, 0 otherwise. The target is saved in
+ self.lastTarget (for further processing).
+ """
+ return self.TryBuild(self.env.Program, text, extension )
+
+ def TryRun(self, text, extension ):
+ """Compiles and runs the program given in text, using extension
+ as file extension (e.g. '.c'). Returns (1, outputStr) on success,
+ (0, '') otherwise. The target (a file containing the program's stdout)
+ is saved in self.lastTarget (for further processing).
+ """
+ ok = self.TryLink(text, extension)
+ if( ok ):
+ prog = self.lastTarget
+ pname = prog.path
+ output = self.confdir.File(os.path.basename(pname)+'.out')
+ node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
+ ok = self.BuildNodes(node)
+ if ok:
+ outputStr = output.get_contents()
+ return( 1, outputStr)
+ return (0, "")
+
+ class TestWrapper:
+ """A wrapper around Tests (to ensure sanity)"""
+ def __init__(self, test, sconf):
+ self.test = test
+ self.sconf = sconf
+ def __call__(self, *args, **kw):
+ if not self.sconf.active:
+ raise (SCons.Errors.UserError,
+ "Test called after sconf.Finish()")
+ context = CheckContext(self.sconf)
+ ret = apply(self.test, (context,) + args, kw)
+ if self.sconf.config_h is not None:
+ self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
+ context.Result("error: no result")
+ return ret
+
+ def AddTest(self, test_name, test_instance):
+ """Adds test_class to this SConf instance. It can be called with
+ self.test_name(...)"""
+ setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
+
+ def AddTests(self, tests):
+ """Adds all the tests given in the tests dictionary to this SConf
+ instance
+ """
+ for name in tests.keys():
+ self.AddTest(name, tests[name])
+
+ def _createDir( self, node ):
+ dirName = str(node)
+ if dryrun:
+ if not os.path.isdir( dirName ):
+ raise ConfigureDryRunError(dirName)
+ else:
+ if not os.path.isdir( dirName ):
+ os.makedirs( dirName )
+ node._exists = 1
+
+ def _startup(self):
+ """Private method. Set up logstream, and set the environment
+ variables necessary for a piped build
+ """
+ global _ac_config_logs
+ global sconf_global
+ global SConfFS
+
+ self.lastEnvFs = self.env.fs
+ self.env.fs = SConfFS
+ self._createDir(self.confdir)
+ self.confdir.up().add_ignore( [self.confdir] )
+
+ if self.logfile is not None and not dryrun:
+ # truncate logfile, if SConf.Configure is called for the first time
+ # in a build
+ if _ac_config_logs.has_key(self.logfile):
+ log_mode = "a"
+ else:
+ _ac_config_logs[self.logfile] = None
+ log_mode = "w"
+ fp = open(str(self.logfile), log_mode)
+ self.logstream = SCons.Util.Unbuffered(fp)
+ # logfile may stay in a build directory, so we tell
+ # the build system not to override it with a eventually
+ # existing file with the same name in the source directory
+ self.logfile.dir.add_ignore( [self.logfile] )
+
+ tb = traceback.extract_stack()[-3-self.depth]
+ old_fs_dir = SConfFS.getcwd()
+ SConfFS.chdir(SConfFS.Top, change_os_dir=0)
+ self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
+ (tb[0], tb[1], str(self.confdir)) )
+ SConfFS.chdir(old_fs_dir)
+ else:
+ self.logstream = None
+ # we use a special builder to create source files from TEXT
+ action = SCons.Action.Action(_createSource,
+ _stringSource)
+ sconfSrcBld = SCons.Builder.Builder(action=action)
+ self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
+ self.config_h_text = _ac_config_hs.get(self.config_h, "")
+ self.active = 1
+ # only one SConf instance should be active at a time ...
+ sconf_global = self
+
+ def _shutdown(self):
+ """Private method. Reset to non-piped spawn"""
+ global sconf_global, _ac_config_hs
+
+ if not self.active:
+ raise SCons.Errors.UserError, "Finish may be called only once!"
+ if self.logstream is not None and not dryrun:
+ self.logstream.write("\n")
+ self.logstream.close()
+ self.logstream = None
+ # remove the SConfSourceBuilder from the environment
+ blds = self.env['BUILDERS']
+ del blds['SConfSourceBuilder']
+ self.env.Replace( BUILDERS=blds )
+ self.active = 0
+ sconf_global = None
+ if not self.config_h is None:
+ _ac_config_hs[self.config_h] = self.config_h_text
+ self.env.fs = self.lastEnvFs
+
+class CheckContext:
+ """Provides a context for configure tests. Defines how a test writes to the
+ screen and log file.
+
+ A typical test is just a callable with an instance of CheckContext as
+ first argument:
+
+ def CheckCustom(context, ...)
+ context.Message('Checking my weird test ... ')
+ ret = myWeirdTestFunction(...)
+ context.Result(ret)
+
+ Often, myWeirdTestFunction will be one of
+ context.TryCompile/context.TryLink/context.TryRun. The results of
+ those are cached, for they are only rebuild, if the dependencies have
+ changed.
+ """
+
+ def __init__(self, sconf):
+ """Constructor. Pass the corresponding SConf instance."""
+ self.sconf = sconf
+ self.did_show_result = 0
+
+ # for Conftest.py:
+ self.vardict = {}
+ self.havedict = {}
+ self.headerfilename = None
+ self.config_h = "" # config_h text will be stored here
+ # we don't regenerate the config.h file after each test. That means,
+ # that tests won't be able to include the config.h file, and so
+ # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major
+ # issue, though. If it turns out, that we need to include config.h
+ # in tests, we must ensure, that the dependencies are worked out
+ # correctly. Note that we can't use Conftest.py's support for config.h,
+ # cause we will need to specify a builder for the config.h file ...
+
+ def Message(self, text):
+ """Inform about what we are doing right now, e.g.
+ 'Checking for SOMETHING ... '
+ """
+ self.Display(text)
+ self.sconf.cached = 1
+ self.did_show_result = 0
+
+ def Result(self, res):
+ """Inform about the result of the test. res may be an integer or a
+ string. In case of an integer, the written text will be 'yes' or 'no'.
+ The result is only displayed when self.did_show_result is not set.
+ """
+ if type(res) in BooleanTypes:
+ if res:
+ text = "yes"
+ else:
+ text = "no"
+ elif type(res) == types.StringType:
+ text = res
+ else:
+ raise TypeError, "Expected string, int or bool, got " + str(type(res))
+
+ if self.did_show_result == 0:
+ # Didn't show result yet, do it now.
+ self.Display(text + "\n")
+ self.did_show_result = 1
+
+ def TryBuild(self, *args, **kw):
+ return apply(self.sconf.TryBuild, args, kw)
+
+ def TryAction(self, *args, **kw):
+ return apply(self.sconf.TryAction, args, kw)
+
+ def TryCompile(self, *args, **kw):
+ return apply(self.sconf.TryCompile, args, kw)
+
+ def TryLink(self, *args, **kw):
+ return apply(self.sconf.TryLink, args, kw)
+
+ def TryRun(self, *args, **kw):
+ return apply(self.sconf.TryRun, args, kw)
+
+ def __getattr__( self, attr ):
+ if( attr == 'env' ):
+ return self.sconf.env
+ elif( attr == 'lastTarget' ):
+ return self.sconf.lastTarget
+ else:
+ raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
+
+ #### Stuff used by Conftest.py (look there for explanations).
+
+ def BuildProg(self, text, ext):
+ self.sconf.cached = 1
+ # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
+ return not self.TryBuild(self.env.Program, text, ext)
+
+ def CompileProg(self, text, ext):
+ self.sconf.cached = 1
+ # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
+ return not self.TryBuild(self.env.Object, text, ext)
+
+ def CompileSharedObject(self, text, ext):
+ self.sconf.cached = 1
+ # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc.
+ return not self.TryBuild(self.env.SharedObject, text, ext)
+
+ def RunProg(self, text, ext):
+ self.sconf.cached = 1
+ # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
+ st, out = self.TryRun(text, ext)
+ return not st, out
+
+ def AppendLIBS(self, lib_name_list):
+ oldLIBS = self.env.get( 'LIBS', [] )
+ self.env.Append(LIBS = lib_name_list)
+ return oldLIBS
+
+ def PrependLIBS(self, lib_name_list):
+ oldLIBS = self.env.get( 'LIBS', [] )
+ self.env.Prepend(LIBS = lib_name_list)
+ return oldLIBS
+
+ def SetLIBS(self, val):
+ oldLIBS = self.env.get( 'LIBS', [] )
+ self.env.Replace(LIBS = val)
+ return oldLIBS
+
+ def Display(self, msg):
+ if self.sconf.cached:
+ # We assume that Display is called twice for each test here
+ # once for the Checking for ... message and once for the result.
+ # The self.sconf.cached flag can only be set between those calls
+ msg = "(cached) " + msg
+ self.sconf.cached = 0
+ progress_display(msg, append_newline=0)
+ self.Log("scons: Configure: " + msg + "\n")
+
+ def Log(self, msg):
+ if self.sconf.logstream is not None:
+ self.sconf.logstream.write(msg)
+
+ #### End of stuff used by Conftest.py.
+
+
+def SConf(*args, **kw):
+ if kw.get(build_type, True):
+ kw['_depth'] = kw.get('_depth', 0) + 1
+ for bt in build_types:
+ try:
+ del kw[bt]
+ except KeyError:
+ pass
+ return apply(SConfBase, args, kw)
+ else:
+ return SCons.Util.Null()
+
+
+def CheckFunc(context, function_name, header = None, language = None):
+ res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language)
+ context.did_show_result = 1
+ return not res
+
+def CheckType(context, type_name, includes = "", language = None):
+ res = SCons.Conftest.CheckType(context, type_name,
+ header = includes, language = language)
+ context.did_show_result = 1
+ return not res
+
+def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
+ res = SCons.Conftest.CheckTypeSize(context, type_name,
+ header = includes, language = language,
+ expect = expect)
+ context.did_show_result = 1
+ return res
+
+def CheckDeclaration(context, declaration, includes = "", language = None):
+ res = SCons.Conftest.CheckDeclaration(context, declaration,
+ includes = includes,
+ language = language)
+ context.did_show_result = 1
+ return not res
+
+def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
+ # used by CheckHeader and CheckLibWithHeader to produce C - #include
+ # statements from the specified header (list)
+ if not SCons.Util.is_List(headers):
+ headers = [headers]
+ l = []
+ if leaveLast:
+ lastHeader = headers[-1]
+ headers = headers[:-1]
+ else:
+ lastHeader = None
+ for s in headers:
+ l.append("#include %s%s%s\n"
+ % (include_quotes[0], s, include_quotes[1]))
+ return string.join(l, ''), lastHeader
+
+def CheckHeader(context, header, include_quotes = '<>', language = None):
+ """
+ A test for a C or C++ header file.
+ """
+ prog_prefix, hdr_to_check = \
+ createIncludesFromHeaders(header, 1, include_quotes)
+ res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
+ language = language,
+ include_quotes = include_quotes)
+ context.did_show_result = 1
+ return not res
+
+def CheckCC(context):
+ res = SCons.Conftest.CheckCC(context)
+ context.did_show_result = 1
+ return not res
+
+def CheckCXX(context):
+ res = SCons.Conftest.CheckCXX(context)
+ context.did_show_result = 1
+ return not res
+
+def CheckSHCC(context):
+ res = SCons.Conftest.CheckSHCC(context)
+ context.did_show_result = 1
+ return not res
+
+def CheckSHCXX(context):
+ res = SCons.Conftest.CheckSHCXX(context)
+ context.did_show_result = 1
+ return not res
+
+# Bram: Make this function obsolete? CheckHeader() is more generic.
+
+def CheckCHeader(context, header, include_quotes = '""'):
+ """
+ A test for a C header file.
+ """
+ return CheckHeader(context, header, include_quotes, language = "C")
+
+
+# Bram: Make this function obsolete? CheckHeader() is more generic.
+
+def CheckCXXHeader(context, header, include_quotes = '""'):
+ """
+ A test for a C++ header file.
+ """
+ return CheckHeader(context, header, include_quotes, language = "C++")
+
+
+def CheckLib(context, library = None, symbol = "main",
+ header = None, language = None, autoadd = 1):
+ """
+ A test for a library. See also CheckLibWithHeader.
+ Note that library may also be None to test whether the given symbol
+ compiles without flags.
+ """
+
+ if library == []:
+ library = [None]
+
+ if not SCons.Util.is_List(library):
+ library = [library]
+
+ # ToDo: accept path for the library
+ res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
+ language = language, autoadd = autoadd)
+ context.did_show_result = 1
+ return not res
+
+# XXX
+# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
+
+def CheckLibWithHeader(context, libs, header, language,
+ call = None, autoadd = 1):
+ # ToDo: accept path for library. Support system header files.
+ """
+ Another (more sophisticated) test for a library.
+ Checks, if library and header is available for language (may be 'C'
+ or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
+ As in CheckLib, we support library=None, to test if the call compiles
+ without extra link flags.
+ """
+ prog_prefix, dummy = \
+ createIncludesFromHeaders(header, 0)
+ if libs == []:
+ libs = [None]
+
+ if not SCons.Util.is_List(libs):
+ libs = [libs]
+
+ res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
+ call = call, language = language, autoadd = autoadd)
+ context.did_show_result = 1
+ return not res
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
new file mode 100644
index 0000000..71bdb57
--- /dev/null
+++ b/src/engine/SCons/SConfTests.py
@@ -0,0 +1,759 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/SConfTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import re
+import string
+import StringIO
+import sys
+from types import *
+import unittest
+
+import TestCmd
+
+sys.stdout = StringIO.StringIO()
+
+if sys.platform == 'win32':
+ existing_lib = "msvcrt"
+else:
+ existing_lib = "m"
+
+class SConfTestCase(unittest.TestCase):
+
+ def setUp(self):
+ # we always want to start with a clean directory
+ self.save_cwd = os.getcwd()
+ self.test = TestCmd.TestCmd(workdir = '')
+ os.chdir(self.test.workpath(''))
+
+ def tearDown(self):
+ self.test.cleanup()
+ import SCons.SConsign
+ SCons.SConsign.Reset()
+ os.chdir(self.save_cwd)
+
+ def _resetSConfState(self):
+ # Ok, this is tricky, and i do not know, if everything is sane.
+ # We try to reset scons' state (including all global variables)
+ import SCons.SConsign
+ SCons.SConsign.write() # simulate normal scons-finish
+ for n in sys.modules.keys():
+ if string.split(n, '.')[0] == 'SCons' and n[:12] != 'SCons.compat':
+ m = sys.modules[n]
+ if type(m) is ModuleType:
+ # if this is really a scons module, clear its namespace
+ del sys.modules[n]
+ m.__dict__.clear()
+ # we only use SCons.Environment and SCons.SConf for these tests.
+ import SCons.Environment
+ import SCons.SConf
+ self.Environment = SCons.Environment
+ self.SConf = SCons.SConf
+ # and we need a new environment, cause references may point to
+ # old modules (well, at least this is safe ...)
+ self.scons_env = self.Environment.Environment()
+ self.scons_env.AppendENVPath('PATH', os.environ['PATH'])
+
+ # we want to do some autodetection here
+ # this stuff works with
+ # - cygwin on Windows (using cmd.exe, not bash)
+ # - posix
+ # - msvc on Windows (hopefully)
+ if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
+ not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
+ not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
+ raise Exception, "This test needs an installed compiler!"
+ if self.scons_env['CXX'] == 'g++':
+ global existing_lib
+ existing_lib = 'm'
+
+ if sys.platform in ['cygwin', 'win32']:
+ # On Windows, SCons.Platform.win32 redefines the builtin
+ # file() and open() functions to close the file handles.
+ # This interferes with the unittest.py infrastructure in
+ # some way. Just sidestep the issue by restoring the
+ # original builtin functions whenever we have to reset
+ # all of our global state.
+
+ import __builtin__
+ import SCons.Platform.win32
+
+ __builtin__.file = SCons.Platform.win32._builtin_file
+ __builtin__.open = SCons.Platform.win32._builtin_open
+
+ def _baseTryXXX(self, TryFunc):
+ # TryCompile and TryLink are much the same, so we can test them
+ # in one method, we pass the function as a string ('TryCompile',
+ # 'TryLink'), so we are aware of reloading modules.
+
+ def checks(self, sconf, TryFuncString):
+ TryFunc = self.SConf.SConfBase.__dict__[TryFuncString]
+ res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" )
+ res2 = TryFunc( sconf,
+ '#include "no_std_header.h"\nint main() {return 0; }\n',
+ '.c' )
+ return (res1,res2)
+
+ # 1. test initial behaviour (check ok / failed)
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ res = checks( self, sconf, TryFunc )
+ assert res[0] and not res[1], res
+ finally:
+ sconf.Finish()
+
+ # 2.1 test the error caching mechanism (no dependencies have changed)
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ res = checks( self, sconf, TryFunc )
+ assert res[0] and not res[1], res
+ finally:
+ sconf.Finish()
+ # we should have exactly one one error cached
+ log = self.test.read( self.test.workpath('config.log') )
+ expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
+ firstOcc = expr.match( log )
+ assert firstOcc is not None, log
+ secondOcc = expr.match( log, firstOcc.end(0) )
+ assert secondOcc is None, log
+
+ # 2.2 test the error caching mechanism (dependencies have changed)
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ no_std_header_h = self.test.workpath('config.tests', 'no_std_header.h')
+ test_h = self.test.write( no_std_header_h,
+ "/* we are changing a dependency now */\n" );
+ try:
+ res = checks( self, sconf, TryFunc )
+ log = self.test.read( self.test.workpath('config.log') )
+ assert res[0] and res[1], res
+ finally:
+ sconf.Finish()
+
+ def test_TryBuild(self):
+ """Test SConf.TryBuild
+ """
+ # 1 test that we can try a builder that returns a list of nodes
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ class MyBuilder:
+ def __init__(self):
+ self.prefix = ''
+ self.suffix = ''
+ def __call__(self, env, target, source):
+ class MyNode:
+ def __init__(self, name):
+ self.name = name
+ self.state = None
+ self.waiting_parents = set()
+ self.side_effects = []
+ self.builder = None
+ self.prerequisites = []
+ def disambiguate(self):
+ return self
+ def has_builder(self):
+ return 1
+ def add_pre_action(self, *actions):
+ pass
+ def add_post_action(self, *actions):
+ pass
+ def children(self):
+ return []
+ def get_state(self):
+ return self.state
+ def set_state(self, state):
+ self.state = state
+ def alter_targets(self):
+ return [], None
+ def depends_on(self, nodes):
+ return None
+ def postprocess(self):
+ pass
+ def clear(self):
+ pass
+ def is_up_to_date(self):
+ return None
+ def prepare(self):
+ pass
+ def push_to_cache(self):
+ pass
+ def retrieve_from_cache(self):
+ return 0
+ def build(self, **kw):
+ return
+ def built(self):
+ pass
+ def get_stored_info(self):
+ pass
+ def do_not_store_info(self):
+ pass
+ def get_executor(self):
+ class Executor:
+ def __init__(self, targets):
+ self.targets = targets
+ def get_all_targets(self):
+ return self.targets
+ return Executor([self])
+ return [MyNode('n1'), MyNode('n2')]
+ try:
+ self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
+ sconf.TryBuild(self.scons_env.SConfActionBuilder)
+ finally:
+ sconf.Finish()
+
+ def test_TryCompile(self):
+ """Test SConf.TryCompile
+ """
+ self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
+
+ def test_TryLink(self):
+ """Test SConf.TryLink
+ """
+ self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink )
+
+ def test_TryRun(self):
+ """Test SConf.TryRun
+ """
+ def checks(sconf):
+ prog = """
+#include <stdio.h>
+int main() {
+ printf( "Hello" );
+ return 0;
+}
+"""
+ res1 = sconf.TryRun( prog, ".c" )
+ res2 = sconf.TryRun( "not a c program\n", ".c" )
+ return (res1, res2)
+
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ res = checks(sconf)
+ assert res[0][0] and res[0][1] == "Hello", res
+ assert not res[1][0] and res[1][1] == "", res
+ finally:
+ sconf.Finish()
+ log = self.test.read( self.test.workpath('config.log') )
+
+ # test the caching mechanism
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ res = checks(sconf)
+ assert res[0][0] and res[0][1] == "Hello", res
+ assert not res[1][0] and res[1][1] == "", res
+ finally:
+ sconf.Finish()
+ # we should have exactly one error cached
+ log = self.test.read( self.test.workpath('config.log') )
+ expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
+ firstOcc = expr.match( log )
+ assert firstOcc is not None, log
+ secondOcc = expr.match( log, firstOcc.end(0) )
+ assert secondOcc is None, log
+
+
+ def test_TryAction(self):
+ """Test SConf.TryAction
+ """
+ def actionOK(target, source, env):
+ open(str(target[0]), "w").write( "RUN OK\n" )
+ return None
+ def actionFAIL(target, source, env):
+ return 1
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ (ret, output) = sconf.TryAction(action=actionOK)
+ assert ret and output == "RUN OK" + os.linesep, (ret, output)
+ (ret, output) = sconf.TryAction(action=actionFAIL)
+ assert not ret and output == "", (ret, output)
+ finally:
+ sconf.Finish()
+
+ def _test_check_compilers(self, comp, func, name):
+ """This is the implementation for CheckCC and CheckCXX tests."""
+ from copy import deepcopy
+
+ # Check that Check* works
+ r = func()
+ assert r, "could not find %s ?" % comp
+
+ # Check that Check* does fail if comp is not available in env
+ oldcomp = deepcopy(self.scons_env[comp])
+ del self.scons_env[comp]
+ r = func()
+ assert not r, "%s worked wo comp ?" % name
+
+ # Check that Check* does fail if comp is set but empty
+ self.scons_env[comp] = ''
+ r = func()
+ assert not r, "%s worked with comp = '' ?" % name
+
+ # Check that Check* does fail if comp is set to buggy executable
+ self.scons_env[comp] = 'thiscccompilerdoesnotexist'
+ r = func()
+ assert not r, "%s worked with comp = thiscompilerdoesnotexist ?" % name
+
+ # Check that Check* does fail if CFLAGS is buggy
+ self.scons_env[comp] = oldcomp
+ self.scons_env['%sFLAGS' % comp] = '/WX qwertyuiop.c'
+ r = func()
+ assert not r, "%s worked with %sFLAGS = qwertyuiop ?" % (name, comp)
+
+ def test_CheckCC(self):
+ """Test SConf.CheckCC()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ try:
+ self._test_check_compilers('CC', sconf.CheckCC, 'CheckCC')
+ except AssertionError:
+ sys.stderr.write(self.test.read('config.log'))
+ raise
+ finally:
+ sconf.Finish()
+
+ def test_CheckSHCC(self):
+ """Test SConf.CheckSHCC()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ try:
+ self._test_check_compilers('SHCC', sconf.CheckSHCC, 'CheckSHCC')
+ except AssertionError:
+ sys.stderr.write(self.test.read('config.log'))
+ raise
+ finally:
+ sconf.Finish()
+
+ def test_CheckCXX(self):
+ """Test SConf.CheckCXX()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ try:
+ self._test_check_compilers('CXX', sconf.CheckCXX, 'CheckCXX')
+ except AssertionError:
+ sys.stderr.write(self.test.read('config.log'))
+ raise
+ finally:
+ sconf.Finish()
+
+ def test_CheckSHCXX(self):
+ """Test SConf.CheckSHCXX()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ try:
+ self._test_check_compilers('SHCXX', sconf.CheckSHCXX, 'CheckSHCXX')
+ except AssertionError:
+ sys.stderr.write(self.test.read('config.log'))
+ raise
+ finally:
+ sconf.Finish()
+
+
+ def test_CheckHeader(self):
+ """Test SConf.CheckHeader()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ # CheckHeader()
+ r = sconf.CheckHeader( "stdio.h", include_quotes="<>", language="C" )
+ assert r, "did not find stdio.h"
+ r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C" )
+ assert not r, "unexpectedly found HopefullyNoHeader.noh"
+ r = sconf.CheckHeader( "vector", include_quotes="<>", language="C++" )
+ assert r, "did not find vector"
+ r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C++" )
+ assert not r, "unexpectedly found HopefullyNoHeader.noh"
+
+ finally:
+ sconf.Finish()
+
+ def test_CheckCHeader(self):
+ """Test SConf.CheckCHeader()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+
+ try:
+ # CheckCHeader()
+ r = sconf.CheckCHeader( "stdio.h", include_quotes="<>" )
+ assert r, "did not find stdio.h"
+ r = sconf.CheckCHeader( ["math.h", "stdio.h"], include_quotes="<>" )
+ assert r, "did not find stdio.h, #include math.h first"
+ r = sconf.CheckCHeader( "HopefullyNoCHeader.noh" )
+ assert not r, "unexpectedly found HopefullyNoCHeader.noh"
+
+ finally:
+ sconf.Finish()
+
+ def test_CheckCXXHeader(self):
+ """Test SConf.CheckCXXHeader()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+
+ try:
+ # CheckCXXHeader()
+ r = sconf.CheckCXXHeader( "vector", include_quotes="<>" )
+ assert r, "did not find vector"
+ r = sconf.CheckCXXHeader( ["stdio.h", "vector"], include_quotes="<>" )
+ assert r, "did not find vector, #include stdio.h first"
+ r = sconf.CheckCXXHeader( "HopefullyNoCXXHeader.noh" )
+ assert not r, "unexpectedly found HopefullyNoCXXHeader.noh"
+
+ finally:
+ sconf.Finish()
+
+ def test_CheckLib(self):
+ """Test SConf.CheckLib()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+
+ try:
+ # CheckLib()
+ r = sconf.CheckLib( existing_lib, "main", autoadd=0 )
+ assert r, "did not find %s" % existing_lib
+ r = sconf.CheckLib( "hopefullynolib", "main", autoadd=0 )
+ assert not r, "unexpectedly found hopefullynolib"
+
+ # CheckLib() with list of libs
+ r = sconf.CheckLib( [existing_lib], "main", autoadd=0 )
+ assert r, "did not find %s" % existing_lib
+ r = sconf.CheckLib( ["hopefullynolib"], "main", autoadd=0 )
+ assert not r, "unexpectedly found hopefullynolib"
+ # This is a check that a null list doesn't find functions
+ # that are in libraries that must be explicitly named.
+ # This works on POSIX systems where you have to -lm to
+ # get the math functions, but it fails on Visual Studio
+ # where you apparently get all those functions for free.
+ # Comment out this check until someone who understands
+ # Visual Studio better can come up with a corresponding
+ # test (if that ever really becomes necessary).
+ #r = sconf.CheckLib( [], "sin", autoadd=0 )
+ #assert not r, "unexpectedly found nonexistent library"
+ r = sconf.CheckLib( [existing_lib,"hopefullynolib"], "main", autoadd=0 )
+ assert r, "did not find %s,%s " % (existing_lib,r)
+ r = sconf.CheckLib( ["hopefullynolib",existing_lib], "main", autoadd=0 )
+ assert r, "did not find %s " % existing_lib
+
+ # CheckLib() with autoadd
+ def libs(env):
+ return env.get('LIBS', [])
+
+ env = sconf.env.Clone()
+
+ try:
+ r = sconf.CheckLib( existing_lib, "main", autoadd=1 )
+ assert r, "did not find main in %s" % existing_lib
+ expect = libs(env) + [existing_lib]
+ got = libs(sconf.env)
+ assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
+
+ sconf.env = env.Clone()
+ r = sconf.CheckLib( existing_lib, "main", autoadd=0 )
+ assert r, "did not find main in %s" % existing_lib
+ expect = libs(env)
+ got = libs(sconf.env)
+ assert got == expect, "before and after LIBS were not the same"
+ finally:
+ sconf.env = env
+ finally:
+ sconf.Finish()
+
+ def test_CheckLibWithHeader(self):
+ """Test SConf.CheckLibWithHeader()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+
+ try:
+ # CheckLibWithHeader()
+ r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
+ assert r, "did not find %s" % existing_lib
+ r = sconf.CheckLibWithHeader( existing_lib, ["stdio.h", "math.h"], "C", autoadd=0 )
+ assert r, "did not find %s, #include stdio.h first" % existing_lib
+ r = sconf.CheckLibWithHeader( "hopefullynolib", "math.h", "C", autoadd=0 )
+ assert not r, "unexpectedly found hopefullynolib"
+
+ # CheckLibWithHeader() with lists of libs
+ r = sconf.CheckLibWithHeader( [existing_lib], "math.h", "C", autoadd=0 )
+ assert r, "did not find %s" % existing_lib
+ r = sconf.CheckLibWithHeader( [existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 )
+ assert r, "did not find %s, #include stdio.h first" % existing_lib
+ # This is a check that a null list doesn't find functions
+ # that are in libraries that must be explicitly named.
+ # This works on POSIX systems where you have to -lm to
+ # get the math functions, but it fails on Visual Studio
+ # where you apparently get all those functions for free.
+ # Comment out this check until someone who understands
+ # Visual Studio better can come up with a corresponding
+ # test (if that ever really becomes necessary).
+ #r = sconf.CheckLibWithHeader( [], "math.h", "C", call="sin(3);", autoadd=0 )
+ #assert not r, "unexpectedly found non-existent library"
+ r = sconf.CheckLibWithHeader( ["hopefullynolib"], "math.h", "C", autoadd=0 )
+ assert not r, "unexpectedly found hopefullynolib"
+ r = sconf.CheckLibWithHeader( ["hopefullynolib",existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 )
+ assert r, "did not find %s, #include stdio.h first" % existing_lib
+ r = sconf.CheckLibWithHeader( [existing_lib,"hopefullynolib"], ["stdio.h", "math.h"], "C", autoadd=0 )
+ assert r, "did not find %s, #include stdio.h first" % existing_lib
+
+ # CheckLibWithHeader with autoadd
+ def libs(env):
+ return env.get('LIBS', [])
+
+ env = sconf.env.Clone()
+
+ try:
+ r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=1 )
+ assert r, "did not find math.h with %s" % existing_lib
+ expect = libs(env) + [existing_lib]
+ got = libs(sconf.env)
+ assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
+
+ sconf.env = env.Clone()
+ r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
+ assert r, "did not find math.h with %s" % existing_lib
+ expect = libs(env)
+ got = libs(sconf.env)
+ assert got == expect, "before and after LIBS were not the same"
+ finally:
+ sconf.env = env
+
+ finally:
+ sconf.Finish()
+
+ def test_CheckFunc(self):
+ """Test SConf.CheckFunc()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+
+ try:
+ # CheckFunc()
+ r = sconf.CheckFunc('strcpy')
+ assert r, "did not find strcpy"
+ r = sconf.CheckFunc('strcpy', '/* header */ char strcpy();')
+ assert r, "did not find strcpy"
+ r = sconf.CheckFunc('hopefullynofunction')
+ assert not r, "unexpectedly found hopefullynofunction"
+
+ finally:
+ sconf.Finish()
+
+ def test_Define(self):
+ """Test SConf.Define()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'),
+ config_h = self.test.workpath('config.h'))
+ try:
+ # XXX: we test the generated config.h string. This is not so good,
+ # ideally, we would like to test if the generated file included in
+ # a test program does what we want.
+
+ # Test defining one symbol wo value
+ sconf.config_h_text = ''
+ sconf.Define('YOP')
+ assert sconf.config_h_text == '#define YOP\n'
+
+ # Test defining one symbol with integer value
+ sconf.config_h_text = ''
+ sconf.Define('YOP', 1)
+ assert sconf.config_h_text == '#define YOP 1\n'
+
+ # Test defining one symbol with string value
+ sconf.config_h_text = ''
+ sconf.Define('YOP', '"YIP"')
+ assert sconf.config_h_text == '#define YOP "YIP"\n'
+
+ # Test defining one symbol with string value
+ sconf.config_h_text = ''
+ sconf.Define('YOP', "YIP")
+ assert sconf.config_h_text == '#define YOP YIP\n'
+
+ finally:
+ sconf.Finish()
+
+ def test_CheckTypeSize(self):
+ """Test SConf.CheckTypeSize()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ # CheckTypeSize()
+
+ # In ANSI C, sizeof(char) == 1.
+ r = sconf.CheckTypeSize('char', expect = 1)
+ assert r == 1, "sizeof(char) != 1 ??"
+ r = sconf.CheckTypeSize('char', expect = 0)
+ assert r == 0, "sizeof(char) == 0 ??"
+ r = sconf.CheckTypeSize('char', expect = 2)
+ assert r == 0, "sizeof(char) == 2 ??"
+ r = sconf.CheckTypeSize('char')
+ assert r == 1, "sizeof(char) != 1 ??"
+ r = sconf.CheckTypeSize('const unsigned char')
+ assert r == 1, "sizeof(const unsigned char) != 1 ??"
+
+ # Checking C++
+ r = sconf.CheckTypeSize('const unsigned char', language = 'C++')
+ assert r == 1, "sizeof(const unsigned char) != 1 ??"
+
+ # Checking Non-existing type
+ r = sconf.CheckTypeSize('thistypedefhasnotchancetosexist_scons')
+ assert r == 0, \
+ "Checking size of thistypedefhasnotchancetosexist_scons succeeded ?"
+
+ finally:
+ sconf.Finish()
+
+ def test_CheckDeclaration(self):
+ """Test SConf.CheckDeclaration()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ # In ANSI C, malloc should be available in stdlib
+ r = sconf.CheckDeclaration('malloc', includes = "#include <stdlib.h>")
+ assert r, "malloc not declared ??"
+ # For C++, __cplusplus should be declared
+ r = sconf.CheckDeclaration('__cplusplus', language = 'C++')
+ assert r, "__cplusplus not declared in C++ ??"
+ r = sconf.CheckDeclaration('__cplusplus', language = 'C')
+ assert not r, "__cplusplus declared in C ??"
+ finally:
+ sconf.Finish()
+
+ def test_(self):
+ """Test SConf.CheckType()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ # CheckType()
+ r = sconf.CheckType('off_t', '#include <sys/types.h>\n')
+ assert r, "did not find off_t"
+ r = sconf.CheckType('hopefullynotypedef_not')
+ assert not r, "unexpectedly found hopefullynotypedef_not"
+
+ finally:
+ sconf.Finish()
+
+ def test_CustomChecks(self):
+ """Test Custom Checks
+ """
+ def CheckCustom(test):
+ test.Message( "Checking UserTest ... " )
+ prog = """
+#include <stdio.h>
+
+int main() {
+ printf( "Hello" );
+ return 0;
+}
+"""
+ (ret, output) = test.TryRun( prog, ".c" )
+ test.Result( ret )
+ assert ret and output == "Hello", (ret, output)
+ return ret
+
+
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ custom_tests={'CheckCustom': CheckCustom},
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ ret = sconf.CheckCustom()
+ assert ret, ret
+ finally:
+ sconf.Finish()
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(SConfTestCase, 'test_')
+ res = unittest.TextTestRunner().run(suite)
+ if not res.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py
new file mode 100644
index 0000000..2149f0d
--- /dev/null
+++ b/src/engine/SCons/SConsign.py
@@ -0,0 +1,381 @@
+"""SCons.SConsign
+
+Writing and reading information to the .sconsign file or files.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/SConsign.py 4577 2009/12/27 19:44:43 scons"
+
+import cPickle
+import os
+import os.path
+
+import SCons.dblite
+import SCons.Warnings
+
+def corrupt_dblite_warning(filename):
+ SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+ "Ignoring corrupt .sconsign file: %s"%filename)
+
+SCons.dblite.ignore_corrupt_dbfiles = 1
+SCons.dblite.corruption_warning = corrupt_dblite_warning
+
+#XXX Get rid of the global array so this becomes re-entrant.
+sig_files = []
+
+# Info for the database SConsign implementation (now the default):
+# "DataBase" is a dictionary that maps top-level SConstruct directories
+# to open database handles.
+# "DB_Module" is the Python database module to create the handles.
+# "DB_Name" is the base name of the database file (minus any
+# extension the underlying DB module will add).
+DataBase = {}
+DB_Module = SCons.dblite
+DB_Name = ".sconsign"
+DB_sync_list = []
+
+def Get_DataBase(dir):
+ global DataBase, DB_Module, DB_Name
+ top = dir.fs.Top
+ if not os.path.isabs(DB_Name) and top.repositories:
+ mode = "c"
+ for d in [top] + top.repositories:
+ if dir.is_under(d):
+ try:
+ return DataBase[d], mode
+ except KeyError:
+ path = d.entry_abspath(DB_Name)
+ try: db = DataBase[d] = DB_Module.open(path, mode)
+ except (IOError, OSError): pass
+ else:
+ if mode != "r":
+ DB_sync_list.append(db)
+ return db, mode
+ mode = "r"
+ try:
+ return DataBase[top], "c"
+ except KeyError:
+ db = DataBase[top] = DB_Module.open(DB_Name, "c")
+ DB_sync_list.append(db)
+ return db, "c"
+ except TypeError:
+ print "DataBase =", DataBase
+ raise
+
+def Reset():
+ """Reset global state. Used by unit tests that end up using
+ SConsign multiple times to get a clean slate for each test."""
+ global sig_files, DB_sync_list
+ sig_files = []
+ DB_sync_list = []
+
+normcase = os.path.normcase
+
+def write():
+ global sig_files
+ for sig_file in sig_files:
+ sig_file.write(sync=0)
+ for db in DB_sync_list:
+ try:
+ syncmethod = db.sync
+ except AttributeError:
+ pass # Not all anydbm modules have sync() methods.
+ else:
+ syncmethod()
+
+class SConsignEntry:
+ """
+ Wrapper class for the generic entry in a .sconsign file.
+ The Node subclass populates it with attributes as it pleases.
+
+ XXX As coded below, we do expect a '.binfo' attribute to be added,
+ but we'll probably generalize this in the next refactorings.
+ """
+ current_version_id = 1
+ def __init__(self):
+ # Create an object attribute from the class attribute so it ends up
+ # in the pickled data in the .sconsign file.
+ _version_id = self.current_version_id
+ def convert_to_sconsign(self):
+ self.binfo.convert_to_sconsign()
+ def convert_from_sconsign(self, dir, name):
+ self.binfo.convert_from_sconsign(dir, name)
+
+class Base:
+ """
+ This is the controlling class for the signatures for the collection of
+ entries associated with a specific directory. The actual directory
+ association will be maintained by a subclass that is specific to
+ the underlying storage method. This class provides a common set of
+ methods for fetching and storing the individual bits of information
+ that make up signature entry.
+ """
+ def __init__(self):
+ self.entries = {}
+ self.dirty = False
+ self.to_be_merged = {}
+
+ def get_entry(self, filename):
+ """
+ Fetch the specified entry attribute.
+ """
+ return self.entries[filename]
+
+ def set_entry(self, filename, obj):
+ """
+ Set the entry.
+ """
+ self.entries[filename] = obj
+ self.dirty = True
+
+ def do_not_set_entry(self, filename, obj):
+ pass
+
+ def store_info(self, filename, node):
+ entry = node.get_stored_info()
+ entry.binfo.merge(node.get_binfo())
+ self.to_be_merged[filename] = node
+ self.dirty = True
+
+ def do_not_store_info(self, filename, node):
+ pass
+
+ def merge(self):
+ for key, node in self.to_be_merged.items():
+ entry = node.get_stored_info()
+ try:
+ ninfo = entry.ninfo
+ except AttributeError:
+ # This happens with SConf Nodes, because the configuration
+ # subsystem takes direct control over how the build decision
+ # is made and its information stored.
+ pass
+ else:
+ ninfo.merge(node.get_ninfo())
+ self.entries[key] = entry
+ self.to_be_merged = {}
+
+class DB(Base):
+ """
+ A Base subclass that reads and writes signature information
+ from a global .sconsign.db* file--the actual file suffix is
+ determined by the database module.
+ """
+ def __init__(self, dir):
+ Base.__init__(self)
+
+ self.dir = dir
+
+ db, mode = Get_DataBase(dir)
+
+ # Read using the path relative to the top of the Repository
+ # (self.dir.tpath) from which we're fetching the signature
+ # information.
+ path = normcase(dir.tpath)
+ try:
+ rawentries = db[path]
+ except KeyError:
+ pass
+ else:
+ try:
+ self.entries = cPickle.loads(rawentries)
+ if type(self.entries) is not type({}):
+ self.entries = {}
+ raise TypeError
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+ "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e))
+ for key, entry in self.entries.items():
+ entry.convert_from_sconsign(dir, key)
+
+ if mode == "r":
+ # This directory is actually under a repository, which means
+ # likely they're reaching in directly for a dependency on
+ # a file there. Don't actually set any entry info, so we
+ # won't try to write to that .sconsign.dblite file.
+ self.set_entry = self.do_not_set_entry
+ self.store_info = self.do_not_store_info
+
+ global sig_files
+ sig_files.append(self)
+
+ def write(self, sync=1):
+ if not self.dirty:
+ return
+
+ self.merge()
+
+ db, mode = Get_DataBase(self.dir)
+
+ # Write using the path relative to the top of the SConstruct
+ # directory (self.dir.path), not relative to the top of
+ # the Repository; we only write to our own .sconsign file,
+ # not to .sconsign files in Repositories.
+ path = normcase(self.dir.path)
+ for key, entry in self.entries.items():
+ entry.convert_to_sconsign()
+ db[path] = cPickle.dumps(self.entries, 1)
+
+ if sync:
+ try:
+ syncmethod = db.sync
+ except AttributeError:
+ # Not all anydbm modules have sync() methods.
+ pass
+ else:
+ syncmethod()
+
+class Dir(Base):
+ def __init__(self, fp=None, dir=None):
+ """
+ fp - file pointer to read entries from
+ """
+ Base.__init__(self)
+
+ if not fp:
+ return
+
+ self.entries = cPickle.load(fp)
+ if type(self.entries) is not type({}):
+ self.entries = {}
+ raise TypeError
+
+ if dir:
+ for key, entry in self.entries.items():
+ entry.convert_from_sconsign(dir, key)
+
+class DirFile(Dir):
+ """
+ Encapsulates reading and writing a per-directory .sconsign file.
+ """
+ def __init__(self, dir):
+ """
+ dir - the directory for the file
+ """
+
+ self.dir = dir
+ self.sconsign = os.path.join(dir.path, '.sconsign')
+
+ try:
+ fp = open(self.sconsign, 'rb')
+ except IOError:
+ fp = None
+
+ try:
+ Dir.__init__(self, fp, dir)
+ except KeyboardInterrupt:
+ raise
+ except:
+ SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+ "Ignoring corrupt .sconsign file: %s"%self.sconsign)
+
+ global sig_files
+ sig_files.append(self)
+
+ def write(self, sync=1):
+ """
+ Write the .sconsign file to disk.
+
+ Try to write to a temporary file first, and rename it if we
+ succeed. If we can't write to the temporary file, it's
+ probably because the directory isn't writable (and if so,
+ how did we build anything in this directory, anyway?), so
+ try to write directly to the .sconsign file as a backup.
+ If we can't rename, try to copy the temporary contents back
+ to the .sconsign file. Either way, always try to remove
+ the temporary file at the end.
+ """
+ if not self.dirty:
+ return
+
+ self.merge()
+
+ temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
+ try:
+ file = open(temp, 'wb')
+ fname = temp
+ except IOError:
+ try:
+ file = open(self.sconsign, 'wb')
+ fname = self.sconsign
+ except IOError:
+ return
+ for key, entry in self.entries.items():
+ entry.convert_to_sconsign()
+ cPickle.dump(self.entries, file, 1)
+ file.close()
+ if fname != self.sconsign:
+ try:
+ mode = os.stat(self.sconsign)[0]
+ os.chmod(self.sconsign, 0666)
+ os.unlink(self.sconsign)
+ except (IOError, OSError):
+ # Try to carry on in the face of either OSError
+ # (things like permission issues) or IOError (disk
+ # or network issues). If there's a really dangerous
+ # issue, it should get re-raised by the calls below.
+ pass
+ try:
+ os.rename(fname, self.sconsign)
+ except OSError:
+ # An OSError failure to rename may indicate something
+ # like the directory has no write permission, but
+ # the .sconsign file itself might still be writable,
+ # so try writing on top of it directly. An IOError
+ # here, or in any of the following calls, would get
+ # raised, indicating something like a potentially
+ # serious disk or network issue.
+ open(self.sconsign, 'wb').write(open(fname, 'rb').read())
+ os.chmod(self.sconsign, mode)
+ try:
+ os.unlink(temp)
+ except (IOError, OSError):
+ pass
+
+ForDirectory = DB
+
+def File(name, dbm_module=None):
+ """
+ Arrange for all signatures to be stored in a global .sconsign.db*
+ file.
+ """
+ global ForDirectory, DB_Name, DB_Module
+ if name is None:
+ ForDirectory = DirFile
+ DB_Module = None
+ else:
+ ForDirectory = DB
+ DB_Name = name
+ if not dbm_module is None:
+ DB_Module = dbm_module
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py
new file mode 100644
index 0000000..1ebe31f
--- /dev/null
+++ b/src/engine/SCons/SConsignTests.py
@@ -0,0 +1,397 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/SConsignTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import sys
+import TestCmd
+import unittest
+
+import SCons.dblite
+
+import SCons.SConsign
+
+class BuildInfo:
+ def merge(self, object):
+ pass
+
+class DummySConsignEntry:
+ def __init__(self, name):
+ self.name = name
+ self.binfo = BuildInfo()
+ def convert_to_sconsign(self):
+ self.c_to_s = 1
+ def convert_from_sconsign(self, dir, name):
+ self.c_from_s = 1
+
+class FS:
+ def __init__(self, top):
+ self.Top = top
+ self.Top.repositories = []
+
+class DummyNode:
+ def __init__(self, path='not_a_valid_path', binfo=None):
+ self.path = path
+ self.tpath = path
+ self.fs = FS(self)
+ self.binfo = binfo
+ def get_stored_info(self):
+ return self.binfo
+ def get_binfo(self):
+ return self.binfo
+
+class SConsignTestCase(unittest.TestCase):
+ def setUp(self):
+ self.save_cwd = os.getcwd()
+ self.test = TestCmd.TestCmd(workdir = '')
+ os.chdir(self.test.workpath(''))
+ def tearDown(self):
+ self.test.cleanup()
+ SCons.SConsign.Reset()
+ os.chdir(self.save_cwd)
+
+class BaseTestCase(SConsignTestCase):
+
+ def test_Base(self):
+ aaa = DummySConsignEntry('aaa')
+ bbb = DummySConsignEntry('bbb')
+ bbb.arg1 = 'bbb arg1'
+ ccc = DummySConsignEntry('ccc')
+ ccc.arg2 = 'ccc arg2'
+
+ f = SCons.SConsign.Base()
+ f.set_entry('aaa', aaa)
+ f.set_entry('bbb', bbb)
+
+ #f.merge()
+
+ e = f.get_entry('aaa')
+ assert e == aaa, e
+ assert e.name == 'aaa', e.name
+
+ e = f.get_entry('bbb')
+ assert e == bbb, e
+ assert e.name == 'bbb', e.name
+ assert e.arg1 == 'bbb arg1', e.arg1
+ assert not hasattr(e, 'arg2'), e
+
+ f.set_entry('bbb', ccc)
+
+ e = f.get_entry('bbb')
+ assert e.name == 'ccc', e.name
+ assert not hasattr(e, 'arg1'), e
+ assert e.arg2 == 'ccc arg2', e.arg1
+
+ ddd = DummySConsignEntry('ddd')
+ eee = DummySConsignEntry('eee')
+ fff = DummySConsignEntry('fff')
+ fff.arg = 'fff arg'
+
+ f = SCons.SConsign.Base()
+ f.set_entry('ddd', ddd)
+ f.set_entry('eee', eee)
+
+ e = f.get_entry('ddd')
+ assert e == ddd, e
+ assert e.name == 'ddd', e.name
+
+ e = f.get_entry('eee')
+ assert e == eee, e
+ assert e.name == 'eee', e.name
+ assert not hasattr(e, 'arg'), e
+
+ f.set_entry('eee', fff)
+
+ e = f.get_entry('eee')
+ assert e.name == 'fff', e.name
+ assert e.arg == 'fff arg', e.arg
+
+ def test_store_info(self):
+ aaa = DummySConsignEntry('aaa')
+ bbb = DummySConsignEntry('bbb')
+ bbb.arg1 = 'bbb arg1'
+ ccc = DummySConsignEntry('ccc')
+ ccc.arg2 = 'ccc arg2'
+
+ f = SCons.SConsign.Base()
+ f.store_info('aaa', DummyNode('aaa', aaa))
+ f.store_info('bbb', DummyNode('bbb', bbb))
+
+ try:
+ e = f.get_entry('aaa')
+ except KeyError:
+ pass
+ else:
+ raise "unexpected entry %s" % e
+
+ try:
+ e = f.get_entry('bbb')
+ except KeyError:
+ pass
+ else:
+ raise "unexpected entry %s" % e
+
+ f.merge()
+
+ e = f.get_entry('aaa')
+ assert e == aaa, "aaa = %s, e = %s" % (aaa, e)
+ assert e.name == 'aaa', e.name
+
+ e = f.get_entry('bbb')
+ assert e == bbb, "bbb = %s, e = %s" % (bbb, e)
+ assert e.name == 'bbb', e.name
+ assert e.arg1 == 'bbb arg1', e.arg1
+ assert not hasattr(e, 'arg2'), e
+
+ f.store_info('bbb', DummyNode('bbb', ccc))
+
+ e = f.get_entry('bbb')
+ assert e == bbb, e
+ assert e.name == 'bbb', e.name
+ assert e.arg1 == 'bbb arg1', e.arg1
+ assert not hasattr(e, 'arg2'), e
+
+ f.merge()
+
+ e = f.get_entry('bbb')
+ assert e.name == 'ccc', e.name
+ assert not hasattr(e, 'arg1'), e
+ assert e.arg2 == 'ccc arg2', e.arg1
+
+ ddd = DummySConsignEntry('ddd')
+ eee = DummySConsignEntry('eee')
+ fff = DummySConsignEntry('fff')
+ fff.arg = 'fff arg'
+
+ f = SCons.SConsign.Base()
+ f.store_info('ddd', DummyNode('ddd', ddd))
+ f.store_info('eee', DummyNode('eee', eee))
+
+ f.merge()
+
+ e = f.get_entry('ddd')
+ assert e == ddd, e
+ assert e.name == 'ddd', e.name
+
+ e = f.get_entry('eee')
+ assert e == eee, e
+ assert e.name == 'eee', e.name
+ assert not hasattr(e, 'arg'), e
+
+ f.store_info('eee', DummyNode('eee', fff))
+
+ e = f.get_entry('eee')
+ assert e == eee, e
+ assert e.name == 'eee', e.name
+ assert not hasattr(e, 'arg'), e
+
+ f.merge()
+
+ e = f.get_entry('eee')
+ assert e.name == 'fff', e.name
+ assert e.arg == 'fff arg', e.arg
+
+class SConsignDBTestCase(SConsignTestCase):
+
+ def test_SConsignDB(self):
+ save_DataBase = SCons.SConsign.DataBase
+ SCons.SConsign.DataBase = {}
+ try:
+ d1 = SCons.SConsign.DB(DummyNode('dir1'))
+ d1.set_entry('aaa', DummySConsignEntry('aaa name'))
+ d1.set_entry('bbb', DummySConsignEntry('bbb name'))
+
+ aaa = d1.get_entry('aaa')
+ assert aaa.name == 'aaa name'
+ bbb = d1.get_entry('bbb')
+ assert bbb.name == 'bbb name'
+
+ d2 = SCons.SConsign.DB(DummyNode('dir2'))
+ d2.set_entry('ccc', DummySConsignEntry('ccc name'))
+ d2.set_entry('ddd', DummySConsignEntry('ddd name'))
+
+ ccc = d2.get_entry('ccc')
+ assert ccc.name == 'ccc name'
+ ddd = d2.get_entry('ddd')
+ assert ddd.name == 'ddd name'
+
+ d31 = SCons.SConsign.DB(DummyNode('dir3/sub1'))
+ d31.set_entry('eee', DummySConsignEntry('eee name'))
+ d31.set_entry('fff', DummySConsignEntry('fff name'))
+
+ eee = d31.get_entry('eee')
+ assert eee.name == 'eee name'
+ fff = d31.get_entry('fff')
+ assert fff.name == 'fff name'
+
+ d32 = SCons.SConsign.DB(DummyNode('dir3%ssub2' % os.sep))
+ d32.set_entry('ggg', DummySConsignEntry('ggg name'))
+ d32.set_entry('hhh', DummySConsignEntry('hhh name'))
+
+ ggg = d32.get_entry('ggg')
+ assert ggg.name == 'ggg name'
+ hhh = d32.get_entry('hhh')
+ assert hhh.name == 'hhh name'
+
+ finally:
+
+ SCons.SConsign.DataBase = save_DataBase
+
+class SConsignDirFileTestCase(SConsignTestCase):
+
+ def test_SConsignDirFile(self):
+ bi_foo = DummySConsignEntry('foo')
+ bi_bar = DummySConsignEntry('bar')
+
+ f = SCons.SConsign.DirFile(DummyNode())
+ f.set_entry('foo', bi_foo)
+ f.set_entry('bar', bi_bar)
+
+ e = f.get_entry('foo')
+ assert e == bi_foo, e
+ assert e.name == 'foo', e.name
+
+ e = f.get_entry('bar')
+ assert e == bi_bar, e
+ assert e.name == 'bar', e.name
+ assert not hasattr(e, 'arg'), e
+
+ bbb = DummySConsignEntry('bbb')
+ bbb.arg = 'bbb arg'
+
+ f.set_entry('bar', bbb)
+
+ e = f.get_entry('bar')
+ assert e.name == 'bbb', e.name
+ assert e.arg == 'bbb arg', e.arg
+
+
+class SConsignFileTestCase(SConsignTestCase):
+
+ def test_SConsignFile(self):
+ test = self.test
+ file = test.workpath('sconsign_file')
+
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name == ".sconsign", SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
+
+ SCons.SConsign.File(file)
+
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
+
+ SCons.SConsign.File(None)
+
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is None, SCons.SConsign.DB_Module
+
+ class Fake_DBM:
+ def open(self, name, mode):
+ self.name = name
+ self.mode = mode
+ return self
+ def __getitem__(self, key):
+ pass
+ def __setitem__(self, key, value):
+ pass
+
+ fake_dbm = Fake_DBM()
+
+ SCons.SConsign.File(file, fake_dbm)
+
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is fake_dbm, SCons.SConsign.DB_Module
+ assert not hasattr(fake_dbm, 'name'), fake_dbm
+ assert not hasattr(fake_dbm, 'mode'), fake_dbm
+
+ SCons.SConsign.ForDirectory(DummyNode(test.workpath('dir')))
+
+ assert not SCons.SConsign.DataBase is None, SCons.SConsign.DataBase
+ assert fake_dbm.name == file, fake_dbm.name
+ assert fake_dbm.mode == "c", fake_dbm.mode
+
+
+class writeTestCase(SConsignTestCase):
+
+ def test_write(self):
+
+ test = self.test
+ file = test.workpath('sconsign_file')
+
+ class Fake_DBM:
+ def __getitem__(self, key):
+ return None
+ def __setitem__(self, key, value):
+ pass
+ def open(self, name, mode):
+ self.sync_count = 0
+ return self
+ def sync(self):
+ self.sync_count = self.sync_count + 1
+
+ fake_dbm = Fake_DBM()
+
+ SCons.SConsign.DataBase = {}
+ SCons.SConsign.File(file, fake_dbm)
+
+ f = SCons.SConsign.DB(DummyNode())
+
+ bi_foo = DummySConsignEntry('foo')
+ bi_bar = DummySConsignEntry('bar')
+ f.set_entry('foo', bi_foo)
+ f.set_entry('bar', bi_bar)
+
+ SCons.SConsign.write()
+
+ assert bi_foo.c_to_s, bi_foo.c_to_s
+ assert bi_bar.c_to_s, bi_bar.c_to_s
+
+ assert fake_dbm.sync_count == 1, fake_dbm.sync_count
+
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ BaseTestCase,
+ SConsignDBTestCase,
+ SConsignDirFileTestCase,
+ SConsignFileTestCase,
+ writeTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
new file mode 100644
index 0000000..68ba74b
--- /dev/null
+++ b/src/engine/SCons/Scanner/C.py
@@ -0,0 +1,132 @@
+"""SCons.Scanner.C
+
+This module implements the depenency scanner for C/C++ code.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/C.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Node.FS
+import SCons.Scanner
+import SCons.Util
+
+import SCons.cpp
+
+class SConsCPPScanner(SCons.cpp.PreProcessor):
+ """
+ SCons-specific subclass of the cpp.py module's processing.
+
+ We subclass this so that: 1) we can deal with files represented
+ by Nodes, not strings; 2) we can keep track of the files that are
+ missing.
+ """
+ def __init__(self, *args, **kw):
+ apply(SCons.cpp.PreProcessor.__init__, (self,)+args, kw)
+ self.missing = []
+ def initialize_result(self, fname):
+ self.result = SCons.Util.UniqueList([fname])
+ def finalize_result(self, fname):
+ return self.result[1:]
+ def find_include_file(self, t):
+ keyword, quote, fname = t
+ result = SCons.Node.FS.find_file(fname, self.searchpath[quote])
+ if not result:
+ self.missing.append((fname, self.current_file))
+ return result
+ def read_file(self, file):
+ try:
+ fp = open(str(file.rfile()))
+ except EnvironmentError, e:
+ self.missing.append((file, self.current_file))
+ return ''
+ else:
+ return fp.read()
+
+def dictify_CPPDEFINES(env):
+ cppdefines = env.get('CPPDEFINES', {})
+ if cppdefines is None:
+ return {}
+ if SCons.Util.is_Sequence(cppdefines):
+ result = {}
+ for c in cppdefines:
+ if SCons.Util.is_Sequence(c):
+ result[c[0]] = c[1]
+ else:
+ result[c] = None
+ return result
+ if not SCons.Util.is_Dict(cppdefines):
+ return {cppdefines : None}
+ return cppdefines
+
+class SConsCPPScannerWrapper:
+ """
+ The SCons wrapper around a cpp.py scanner.
+
+ This is the actual glue between the calling conventions of generic
+ SCons scanners, and the (subclass of) cpp.py class that knows how
+ to look for #include lines with reasonably real C-preprocessor-like
+ evaluation of #if/#ifdef/#else/#elif lines.
+ """
+ def __init__(self, name, variable):
+ self.name = name
+ self.path = SCons.Scanner.FindPathDirs(variable)
+ def __call__(self, node, env, path = ()):
+ cpp = SConsCPPScanner(current = node.get_dir(),
+ cpppath = path,
+ dict = dictify_CPPDEFINES(env))
+ result = cpp(node)
+ for included, includer in cpp.missing:
+ fmt = "No dependency generated for file: %s (included from: %s) -- file not found"
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ fmt % (included, includer))
+ return result
+
+ def recurse_nodes(self, nodes):
+ return nodes
+ def select(self, node):
+ return self
+
+def CScanner():
+ """Return a prototype Scanner instance for scanning source files
+ that use the C pre-processor"""
+
+ # Here's how we would (or might) use the CPP scanner code above that
+ # knows how to evaluate #if/#ifdef/#else/#elif lines when searching
+ # for #includes. This is commented out for now until we add the
+ # right configurability to let users pick between the scanners.
+ #return SConsCPPScannerWrapper("CScanner", "CPPPATH")
+
+ cs = SCons.Scanner.ClassicCPP("CScanner",
+ "$CPPSUFFIXES",
+ "CPPPATH",
+ '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")')
+ return cs
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
new file mode 100644
index 0000000..6d7f8a2
--- /dev/null
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -0,0 +1,468 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/CTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import sys
+import TestCmd
+import unittest
+import UserDict
+
+import SCons.Node.FS
+import SCons.Warnings
+
+import SCons.Scanner.C
+
+test = TestCmd.TestCmd(workdir = '')
+
+os.chdir(test.workpath(''))
+
+# create some source files and headers:
+
+test.write('f1.cpp',"""
+#include \"f1.h\"
+#include <f2.h>
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write('f2.cpp',"""
+#include \"d1/f1.h\"
+#include <d2/f1.h>
+#include \"f1.h\"
+#import <f4.h>
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write('f3.cpp',"""
+#include \t "f1.h"
+ \t #include "f2.h"
+# \t include "f3-test.h"
+
+#include \t <d1/f1.h>
+ \t #include <d1/f2.h>
+# \t include <d1/f3-test.h>
+
+// #include "never.h"
+
+const char* x = "#include <never.h>"
+
+int main()
+{
+ return 0;
+}
+""")
+
+
+# for Emacs -> "
+
+test.subdir('d1', ['d1', 'd2'])
+
+headers = ['f1.h','f2.h', 'f3-test.h', 'fi.h', 'fj.h', 'never.h',
+ 'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h', 'd1/fi.h', 'd1/fj.h',
+ 'd1/d2/f1.h', 'd1/d2/f2.h', 'd1/d2/f3-test.h',
+ 'd1/d2/f4.h', 'd1/d2/fi.h', 'd1/d2/fj.h']
+
+for h in headers:
+ test.write(h, " ")
+
+test.write('f2.h',"""
+#include "fi.h"
+""")
+
+test.write('f3-test.h',"""
+#include <fj.h>
+""")
+
+
+test.subdir('include', 'subdir', ['subdir', 'include'])
+
+test.write('fa.cpp',"""
+#include \"fa.h\"
+#include <fb.h>
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write(['include', 'fa.h'], "\n")
+test.write(['include', 'fb.h'], "\n")
+test.write(['subdir', 'include', 'fa.h'], "\n")
+test.write(['subdir', 'include', 'fb.h'], "\n")
+
+
+test.subdir('repository', ['repository', 'include'],
+ ['repository', 'src' ])
+test.subdir('work', ['work', 'src'])
+
+test.write(['repository', 'include', 'iii.h'], "\n")
+
+test.write(['work', 'src', 'fff.c'], """
+#include <iii.h>
+#include <jjj.h>
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'work', 'src', 'aaa.c'], """
+#include "bbb.h"
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'work', 'src', 'bbb.h'], "\n")
+
+test.write([ 'repository', 'src', 'ccc.c'], """
+#include "ddd.h"
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'repository', 'src', 'ddd.h'], "\n")
+
+test.write('f5.c', """\
+#include\"f5a.h\"
+#include<f5b.h>
+""")
+
+test.write("f5a.h", "\n")
+test.write("f5b.h", "\n")
+
+# define some helpers:
+
+class DummyEnvironment(UserDict.UserDict):
+ def __init__(self, **kw):
+ UserDict.UserDict.__init__(self)
+ self.data.update(kw)
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ return self.data
+
+ def subst(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return self.data[strSubst[1:]]
+ return strSubst
+
+ def subst_list(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return [self.data[strSubst[1:]]]
+ return [[strSubst]]
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+else:
+ my_normpath = os.path.normpath
+
+def deps_match(self, deps, headers):
+ global my_normpath
+ scanned = map(my_normpath, map(str, deps))
+ expect = map(my_normpath, headers)
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+# define some tests:
+
+class CScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ """Find local files with no CPPPATH"""
+ env = DummyEnvironment(CPPPATH=[])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(env.File('f1.cpp'), env, path)
+ headers = ['f1.h', 'f2.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ """Find a file in a CPPPATH directory"""
+ env = DummyEnvironment(CPPPATH=[test.workpath("d1")])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(env.File('f1.cpp'), env, path)
+ headers = ['f1.h', 'd1/f2.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase3(unittest.TestCase):
+ def runTest(self):
+ """Find files in explicit subdirectories, ignore missing file"""
+ env = DummyEnvironment(CPPPATH=[test.workpath("d1")])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(env.File('f2.cpp'), env, path)
+ headers = ['d1/f1.h', 'f1.h', 'd1/d2/f1.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase4(unittest.TestCase):
+ def runTest(self):
+ """Find files in explicit subdirectories"""
+ env = DummyEnvironment(CPPPATH=[test.workpath("d1"), test.workpath("d1/d2")])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(env.File('f2.cpp'), env, path)
+ headers = ['d1/f1.h', 'f1.h', 'd1/d2/f1.h', 'd1/d2/f4.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase5(unittest.TestCase):
+ def runTest(self):
+ """Make sure files in repositories will get scanned"""
+ env = DummyEnvironment(CPPPATH=[])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+
+ n = env.File('f3.cpp')
+ def my_rexists(s=n):
+ s.rexists_called = 1
+ return s.old_rexists()
+ setattr(n, 'old_rexists', n.rexists)
+ setattr(n, 'rexists', my_rexists)
+
+ deps = s(n, env, path)
+
+ # Make sure rexists() got called on the file node being
+ # scanned, essential for cooperation with VariantDir functionality.
+ assert n.rexists_called
+
+ headers = ['f1.h', 'f2.h', 'f3-test.h',
+ 'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase6(unittest.TestCase):
+ def runTest(self):
+ """Find a same-named file in different directories when CPPPATH changes"""
+ env1 = DummyEnvironment(CPPPATH=[test.workpath("d1")])
+ env2 = DummyEnvironment(CPPPATH=[test.workpath("d1/d2")])
+ s = SCons.Scanner.C.CScanner()
+ path1 = s.path(env1)
+ path2 = s.path(env2)
+ deps1 = s(env1.File('f1.cpp'), env1, path1)
+ deps2 = s(env2.File('f1.cpp'), env2, path2)
+ headers1 = ['f1.h', 'd1/f2.h']
+ headers2 = ['f1.h', 'd1/d2/f2.h']
+ deps_match(self, deps1, headers1)
+ deps_match(self, deps2, headers2)
+
+class CScannerTestCase8(unittest.TestCase):
+ def runTest(self):
+ """Find files in a subdirectory relative to the current directory"""
+ env = DummyEnvironment(CPPPATH=["include"])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps1 = s(env.File('fa.cpp'), env, path)
+ env.fs.chdir(env.Dir('subdir'))
+ dir = env.fs.getcwd()
+ env.fs.chdir(env.Dir(''))
+ path = s.path(env, dir)
+ deps2 = s(env.File('#fa.cpp'), env, path)
+ headers1 = map(test.workpath, ['include/fa.h', 'include/fb.h'])
+ headers2 = ['include/fa.h', 'include/fb.h']
+ deps_match(self, deps1, headers1)
+ deps_match(self, deps2, headers2)
+
+class CScannerTestCase9(unittest.TestCase):
+ def runTest(self):
+ """Generate a warning when we can't find a #included file"""
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
+ class TestOut:
+ def __call__(self, x):
+ self.out = x
+
+ to = TestOut()
+ to.out = None
+ SCons.Warnings._warningOut = to
+ test.write('fa.h','\n')
+ fs = SCons.Node.FS.FS(test.workpath(''))
+ env = DummyEnvironment(CPPPATH=[])
+ env.fs = fs
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(fs.File('fa.cpp'), env, path)
+
+ # Did we catch the warning associated with not finding fb.h?
+ assert to.out
+
+ deps_match(self, deps, [ 'fa.h' ])
+ test.unlink('fa.h')
+
+class CScannerTestCase10(unittest.TestCase):
+ def runTest(self):
+ """Find files in the local directory when the scanned file is elsewhere"""
+ fs = SCons.Node.FS.FS(test.workpath(''))
+ fs.chdir(fs.Dir('include'))
+ env = DummyEnvironment(CPPPATH=[])
+ env.fs = fs
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ test.write('include/fa.cpp', test.read('fa.cpp'))
+ fs.chdir(fs.Dir('..'))
+ deps = s(fs.File('#include/fa.cpp'), env, path)
+ deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
+ test.unlink('include/fa.cpp')
+
+class CScannerTestCase11(unittest.TestCase):
+ def runTest(self):
+ """Handle dependencies on a derived .h file in a non-existent directory"""
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.Repository(test.workpath('repository'))
+
+ # Create a derived file in a directory that does not exist yet.
+ # This was a bug at one time.
+ f1=fs.File('include2/jjj.h')
+ f1.builder=1
+ env = DummyEnvironment(CPPPATH=['include', 'include2'])
+ env.fs = fs
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(fs.File('src/fff.c'), env, path)
+ deps_match(self, deps, [ test.workpath('repository/include/iii.h'),
+ 'include2/jjj.h' ])
+ os.chdir(test.workpath(''))
+
+class CScannerTestCase12(unittest.TestCase):
+ def runTest(self):
+ """Find files in VariantDir() directories"""
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.VariantDir('build1', 'src', 1)
+ fs.VariantDir('build2', 'src', 0)
+ fs.Repository(test.workpath('repository'))
+ env = DummyEnvironment(CPPPATH=[])
+ env.fs = fs
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps1 = s(fs.File('build1/aaa.c'), env, path)
+ deps_match(self, deps1, [ 'build1/bbb.h' ])
+ deps2 = s(fs.File('build2/aaa.c'), env, path)
+ deps_match(self, deps2, [ 'src/bbb.h' ])
+ deps3 = s(fs.File('build1/ccc.c'), env, path)
+ deps_match(self, deps3, [ 'build1/ddd.h' ])
+ deps4 = s(fs.File('build2/ccc.c'), env, path)
+ deps_match(self, deps4, [ test.workpath('repository/src/ddd.h') ])
+ os.chdir(test.workpath(''))
+
+class CScannerTestCase13(unittest.TestCase):
+ def runTest(self):
+ """Find files in directories named in a substituted environment variable"""
+ class SubstEnvironment(DummyEnvironment):
+ def subst(self, arg, target=None, source=None, conv=None, test=test):
+ if arg == "$blah":
+ return test.workpath("d1")
+ else:
+ return arg
+ env = SubstEnvironment(CPPPATH=["$blah"])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(env.File('f1.cpp'), env, path)
+ headers = ['f1.h', 'd1/f2.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase14(unittest.TestCase):
+ def runTest(self):
+ """Find files when there's no space between "#include" and the name"""
+ env = DummyEnvironment(CPPPATH=[])
+ s = SCons.Scanner.C.CScanner()
+ path = s.path(env)
+ deps = s(env.File('f5.c'), env, path)
+ headers = ['f5a.h', 'f5b.h']
+ deps_match(self, deps, headers)
+
+class CScannerTestCase15(unittest.TestCase):
+ def runTest(self):
+ """Verify scanner initialization with the suffixes in $CPPSUFFIXES"""
+ suffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+ ".h", ".H", ".hxx", ".hpp", ".hh",
+ ".F", ".fpp", ".FPP",
+ ".S", ".spp", ".SPP"]
+ env = DummyEnvironment(CPPSUFFIXES = suffixes)
+ s = SCons.Scanner.C.CScanner()
+ for suffix in suffixes:
+ assert suffix in s.get_skeys(env), "%s not in skeys" % suffix
+
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(CScannerTestCase1())
+ suite.addTest(CScannerTestCase2())
+ suite.addTest(CScannerTestCase3())
+ suite.addTest(CScannerTestCase4())
+ suite.addTest(CScannerTestCase5())
+ suite.addTest(CScannerTestCase6())
+ suite.addTest(CScannerTestCase8())
+ suite.addTest(CScannerTestCase9())
+ suite.addTest(CScannerTestCase10())
+ suite.addTest(CScannerTestCase11())
+ suite.addTest(CScannerTestCase12())
+ suite.addTest(CScannerTestCase13())
+ suite.addTest(CScannerTestCase14())
+ suite.addTest(CScannerTestCase15())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/D.py b/src/engine/SCons/Scanner/D.py
new file mode 100644
index 0000000..e0637c0
--- /dev/null
+++ b/src/engine/SCons/Scanner/D.py
@@ -0,0 +1,74 @@
+"""SCons.Scanner.D
+
+Scanner for the Digital Mars "D" programming language.
+
+Coded by Andy Friesen
+17 Nov 2003
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/D.py 4577 2009/12/27 19:44:43 scons"
+
+import re
+import string
+
+import SCons.Scanner
+
+def DScanner():
+ """Return a prototype Scanner instance for scanning D source files"""
+ ds = D()
+ return ds
+
+class D(SCons.Scanner.Classic):
+ def __init__ (self):
+ SCons.Scanner.Classic.__init__ (self,
+ name = "DScanner",
+ suffixes = '$DSUFFIXES',
+ path_variable = 'DPATH',
+ regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;')
+
+ self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M)
+
+ def find_include(self, include, source_dir, path):
+ # translate dots (package separators) to slashes
+ inc = string.replace(include, '.', '/')
+
+ i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path)
+ if i is None:
+ i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path)
+ return i, include
+
+ def find_include_names(self, node):
+ includes = []
+ for i in self.cre.findall(node.get_text_contents()):
+ includes = includes + self.cre2.findall(i)
+ return includes
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/Dir.py b/src/engine/SCons/Scanner/Dir.py
new file mode 100644
index 0000000..eacd855
--- /dev/null
+++ b/src/engine/SCons/Scanner/Dir.py
@@ -0,0 +1,111 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/Dir.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Node.FS
+import SCons.Scanner
+
+def only_dirs(nodes):
+ is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir)
+ return filter(is_Dir, nodes)
+
+def DirScanner(**kw):
+ """Return a prototype Scanner instance for scanning
+ directories for on-disk files"""
+ kw['node_factory'] = SCons.Node.FS.Entry
+ kw['recursive'] = only_dirs
+ return apply(SCons.Scanner.Base, (scan_on_disk, "DirScanner"), kw)
+
+def DirEntryScanner(**kw):
+ """Return a prototype Scanner instance for "scanning"
+ directory Nodes for their in-memory entries"""
+ kw['node_factory'] = SCons.Node.FS.Entry
+ kw['recursive'] = None
+ return apply(SCons.Scanner.Base, (scan_in_memory, "DirEntryScanner"), kw)
+
+skip_entry = {}
+
+skip_entry_list = [
+ '.',
+ '..',
+ '.sconsign',
+ # Used by the native dblite.py module.
+ '.sconsign.dblite',
+ # Used by dbm and dumbdbm.
+ '.sconsign.dir',
+ # Used by dbm.
+ '.sconsign.pag',
+ # Used by dumbdbm.
+ '.sconsign.dat',
+ '.sconsign.bak',
+ # Used by some dbm emulations using Berkeley DB.
+ '.sconsign.db',
+]
+
+for skip in skip_entry_list:
+ skip_entry[skip] = 1
+ skip_entry[SCons.Node.FS._my_normcase(skip)] = 1
+
+do_not_scan = lambda k: not skip_entry.has_key(k)
+
+def scan_on_disk(node, env, path=()):
+ """
+ Scans a directory for on-disk files and directories therein.
+
+ Looking up the entries will add these to the in-memory Node tree
+ representation of the file system, so all we have to do is just
+ that and then call the in-memory scanning function.
+ """
+ try:
+ flist = node.fs.listdir(node.abspath)
+ except (IOError, OSError):
+ return []
+ e = node.Entry
+ for f in filter(do_not_scan, flist):
+ # Add ./ to the beginning of the file name so if it begins with a
+ # '#' we don't look it up relative to the top-level directory.
+ e('./' + f)
+ return scan_in_memory(node, env, path)
+
+def scan_in_memory(node, env, path=()):
+ """
+ "Scans" a Node.FS.Dir for its in-memory entries.
+ """
+ try:
+ entries = node.entries
+ except AttributeError:
+ # It's not a Node.FS.Dir (or doesn't look enough like one for
+ # our purposes), which can happen if a target list containing
+ # mixed Node types (Dirs and Files, for example) has a Dir as
+ # the first entry.
+ return []
+ entry_list = filter(do_not_scan, entries.keys())
+ entry_list.sort()
+ return map(lambda n, e=entries: e[n], entry_list)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/DirTests.py b/src/engine/SCons/Scanner/DirTests.py
new file mode 100644
index 0000000..d244c5c
--- /dev/null
+++ b/src/engine/SCons/Scanner/DirTests.py
@@ -0,0 +1,140 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/DirTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+import sys
+import types
+import unittest
+
+import TestCmd
+import SCons.Node.FS
+import SCons.Scanner.Dir
+
+#class DummyNode:
+# def __init__(self, name, fs):
+# self.name = name
+# self.abspath = test.workpath(name)
+# self.fs = fs
+# def __str__(self):
+# return self.name
+# def Entry(self, name):
+# return self.fs.Entry(name)
+
+class DummyEnvironment:
+ def __init__(self, root):
+ self.fs = SCons.Node.FS.FS(root)
+ def Dir(self, name):
+ return self.fs.Dir(name)
+ def Entry(self, name):
+ return self.fs.Entry(name)
+ def File(self, name):
+ return self.fs.File(name)
+ def get_factory(self, factory):
+ return factory or self.fs.Entry
+
+class DirScannerTestBase(unittest.TestCase):
+ def setUp(self):
+ self.test = TestCmd.TestCmd(workdir = '')
+
+ self.test.subdir('dir', ['dir', 'sub'])
+
+ self.test.write(['dir', 'f1'], "dir/f1\n")
+ self.test.write(['dir', 'f2'], "dir/f2\n")
+ self.test.write(['dir', '.sconsign'], "dir/.sconsign\n")
+ self.test.write(['dir', '.sconsign.bak'], "dir/.sconsign.bak\n")
+ self.test.write(['dir', '.sconsign.dat'], "dir/.sconsign.dat\n")
+ self.test.write(['dir', '.sconsign.db'], "dir/.sconsign.db\n")
+ self.test.write(['dir', '.sconsign.dblite'], "dir/.sconsign.dblite\n")
+ self.test.write(['dir', '.sconsign.dir'], "dir/.sconsign.dir\n")
+ self.test.write(['dir', '.sconsign.pag'], "dir/.sconsign.pag\n")
+ self.test.write(['dir', 'sub', 'f3'], "dir/sub/f3\n")
+ self.test.write(['dir', 'sub', 'f4'], "dir/sub/f4\n")
+ self.test.write(['dir', 'sub', '.sconsign'], "dir/.sconsign\n")
+ self.test.write(['dir', 'sub', '.sconsign.bak'], "dir/.sconsign.bak\n")
+ self.test.write(['dir', 'sub', '.sconsign.dat'], "dir/.sconsign.dat\n")
+ self.test.write(['dir', 'sub', '.sconsign.dblite'], "dir/.sconsign.dblite\n")
+ self.test.write(['dir', 'sub', '.sconsign.dir'], "dir/.sconsign.dir\n")
+ self.test.write(['dir', 'sub', '.sconsign.pag'], "dir/.sconsign.pag\n")
+
+class DirScannerTestCase(DirScannerTestBase):
+ def runTest(self):
+ env = DummyEnvironment(self.test.workpath())
+
+ s = SCons.Scanner.Dir.DirScanner()
+
+ expect = [
+ os.path.join('dir', 'f1'),
+ os.path.join('dir', 'f2'),
+ os.path.join('dir', 'sub'),
+ ]
+ deps = s(env.Dir('dir'), env, ())
+ sss = map(str, deps)
+ assert sss == expect, sss
+
+ expect = [
+ os.path.join('dir', 'sub', 'f3'),
+ os.path.join('dir', 'sub', 'f4'),
+ ]
+ deps = s(env.Dir('dir/sub'), env, ())
+ sss = map(str, deps)
+ assert sss == expect, sss
+
+class DirEntryScannerTestCase(DirScannerTestBase):
+ def runTest(self):
+ env = DummyEnvironment(self.test.workpath())
+
+ s = SCons.Scanner.Dir.DirEntryScanner()
+
+ deps = s(env.Dir('dir'), env, ())
+ sss = map(str, deps)
+ assert sss == [], sss
+
+ deps = s(env.Dir('dir/sub'), env, ())
+ sss = map(str, deps)
+ assert sss == [], sss
+
+ # Make sure we don't blow up if handed a non-Dir node.
+ deps = s(env.File('dir/f1'), env, ())
+ sss = map(str, deps)
+ assert sss == [], sss
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(DirScannerTestCase())
+ suite.addTest(DirEntryScannerTestCase())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py
new file mode 100644
index 0000000..7c011a6
--- /dev/null
+++ b/src/engine/SCons/Scanner/Fortran.py
@@ -0,0 +1,320 @@
+"""SCons.Scanner.Fortran
+
+This module implements the dependency scanner for Fortran code.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/Fortran.py 4577 2009/12/27 19:44:43 scons"
+
+import re
+import string
+
+import SCons.Node
+import SCons.Node.FS
+import SCons.Scanner
+import SCons.Util
+import SCons.Warnings
+
+class F90Scanner(SCons.Scanner.Classic):
+ """
+ A Classic Scanner subclass for Fortran source files which takes
+ into account both USE and INCLUDE statements. This scanner will
+ work for both F77 and F90 (and beyond) compilers.
+
+ Currently, this scanner assumes that the include files do not contain
+ USE statements. To enable the ability to deal with USE statements
+ in include files, add logic right after the module names are found
+ to loop over each include file, search for and locate each USE
+ statement, and append each module name to the list of dependencies.
+ Caching the search results in a common dictionary somewhere so that
+ the same include file is not searched multiple times would be a
+ smart thing to do.
+ """
+
+ def __init__(self, name, suffixes, path_variable,
+ use_regex, incl_regex, def_regex, *args, **kw):
+
+ self.cre_use = re.compile(use_regex, re.M)
+ self.cre_incl = re.compile(incl_regex, re.M)
+ self.cre_def = re.compile(def_regex, re.M)
+
+ def _scan(node, env, path, self=self):
+ node = node.rfile()
+
+ if not node.exists():
+ return []
+
+ return self.scan(node, env, path)
+
+ kw['function'] = _scan
+ kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable)
+ kw['recursive'] = 1
+ kw['skeys'] = suffixes
+ kw['name'] = name
+
+ apply(SCons.Scanner.Current.__init__, (self,) + args, kw)
+
+ def scan(self, node, env, path=()):
+
+ # cache the includes list in node so we only scan it once:
+ if node.includes != None:
+ mods_and_includes = node.includes
+ else:
+ # retrieve all included filenames
+ includes = self.cre_incl.findall(node.get_text_contents())
+ # retrieve all USE'd module names
+ modules = self.cre_use.findall(node.get_text_contents())
+ # retrieve all defined module names
+ defmodules = self.cre_def.findall(node.get_text_contents())
+
+ # Remove all USE'd module names that are defined in the same file
+ d = {}
+ for m in defmodules:
+ d[m] = 1
+ modules = filter(lambda m, d=d: not d.has_key(m), modules)
+ #modules = self.undefinedModules(modules, defmodules)
+
+ # Convert module name to a .mod filename
+ suffix = env.subst('$FORTRANMODSUFFIX')
+ modules = map(lambda x, s=suffix: string.lower(x) + s, modules)
+ # Remove unique items from the list
+ mods_and_includes = SCons.Util.unique(includes+modules)
+ node.includes = mods_and_includes
+
+ # This is a hand-coded DSU (decorate-sort-undecorate, or
+ # Schwartzian transform) pattern. The sort key is the raw name
+ # of the file as specifed on the USE or INCLUDE line, which lets
+ # us keep the sort order constant regardless of whether the file
+ # is actually found in a Repository or locally.
+ nodes = []
+ source_dir = node.get_dir()
+ if callable(path):
+ path = path()
+ for dep in mods_and_includes:
+ n, i = self.find_include(dep, source_dir, path)
+
+ if n is None:
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node))
+ else:
+ sortkey = self.sort_key(dep)
+ nodes.append((sortkey, n))
+
+ nodes.sort()
+ nodes = map(lambda pair: pair[1], nodes)
+ return nodes
+
+def FortranScan(path_variable="FORTRANPATH"):
+ """Return a prototype Scanner instance for scanning source files
+ for Fortran USE & INCLUDE statements"""
+
+# The USE statement regex matches the following:
+#
+# USE module_name
+# USE :: module_name
+# USE, INTRINSIC :: module_name
+# USE, NON_INTRINSIC :: module_name
+#
+# Limitations
+#
+# -- While the regex can handle multiple USE statements on one line,
+# it cannot properly handle them if they are commented out.
+# In either of the following cases:
+#
+# ! USE mod_a ; USE mod_b [entire line is commented out]
+# USE mod_a ! ; USE mod_b [in-line comment of second USE statement]
+#
+# the second module name (mod_b) will be picked up as a dependency
+# even though it should be ignored. The only way I can see
+# to rectify this would be to modify the scanner to eliminate
+# the call to re.findall, read in the contents of the file,
+# treating the comment character as an end-of-line character
+# in addition to the normal linefeed, loop over each line,
+# weeding out the comments, and looking for the USE statements.
+# One advantage to this is that the regex passed to the scanner
+# would no longer need to match a semicolon.
+#
+# -- I question whether or not we need to detect dependencies to
+# INTRINSIC modules because these are built-in to the compiler.
+# If we consider them a dependency, will SCons look for them, not
+# find them, and kill the build? Or will we there be standard
+# compiler-specific directories we will need to point to so the
+# compiler and SCons can locate the proper object and mod files?
+
+# Here is a breakdown of the regex:
+#
+# (?i) : regex is case insensitive
+# ^ : start of line
+# (?: : group a collection of regex symbols without saving the match as a "group"
+# ^|; : matches either the start of the line or a semicolon - semicolon
+# ) : end the unsaved grouping
+# \s* : any amount of white space
+# USE : match the string USE, case insensitive
+# (?: : group a collection of regex symbols without saving the match as a "group"
+# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols)
+# (?: : group a collection of regex symbols without saving the match as a "group"
+# (?: : establish another unsaved grouping of regex symbols
+# \s* : any amount of white space
+# , : match a comma
+# \s* : any amount of white space
+# (?:NON_)? : optionally match the prefix NON_, case insensitive
+# INTRINSIC : match the string INTRINSIC, case insensitive
+# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression
+# \s* : any amount of white space
+# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute
+# ) : end the unsaved grouping
+# ) : end the unsaved grouping
+# \s* : match any amount of white space
+# (\w+) : match the module name that is being USE'd
+#
+#
+ use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)"
+
+
+# The INCLUDE statement regex matches the following:
+#
+# INCLUDE 'some_Text'
+# INCLUDE "some_Text"
+# INCLUDE "some_Text" ; INCLUDE "some_Text"
+# INCLUDE kind_"some_Text"
+# INCLUDE kind_'some_Text"
+#
+# where some_Text can include any alphanumeric and/or special character
+# as defined by the Fortran 2003 standard.
+#
+# Limitations:
+#
+# -- The Fortran standard dictates that a " or ' in the INCLUDE'd
+# string must be represented as a "" or '', if the quotes that wrap
+# the entire string are either a ' or ", respectively. While the
+# regular expression below can detect the ' or " characters just fine,
+# the scanning logic, presently is unable to detect them and reduce
+# them to a single instance. This probably isn't an issue since,
+# in practice, ' or " are not generally used in filenames.
+#
+# -- This regex will not properly deal with multiple INCLUDE statements
+# when the entire line has been commented out, ala
+#
+# ! INCLUDE 'some_file' ; INCLUDE 'some_file'
+#
+# In such cases, it will properly ignore the first INCLUDE file,
+# but will actually still pick up the second. Interestingly enough,
+# the regex will properly deal with these cases:
+#
+# INCLUDE 'some_file'
+# INCLUDE 'some_file' !; INCLUDE 'some_file'
+#
+# To get around the above limitation, the FORTRAN programmer could
+# simply comment each INCLUDE statement separately, like this
+#
+# ! INCLUDE 'some_file' !; INCLUDE 'some_file'
+#
+# The way I see it, the only way to get around this limitation would
+# be to modify the scanning logic to replace the calls to re.findall
+# with a custom loop that processes each line separately, throwing
+# away fully commented out lines before attempting to match against
+# the INCLUDE syntax.
+#
+# Here is a breakdown of the regex:
+#
+# (?i) : regex is case insensitive
+# (?: : begin a non-saving group that matches the following:
+# ^ : either the start of the line
+# | : or
+# ['">]\s*; : a semicolon that follows a single quote,
+# double quote or greater than symbol (with any
+# amount of whitespace in between). This will
+# allow the regex to match multiple INCLUDE
+# statements per line (although it also requires
+# the positive lookahead assertion that is
+# used below). It will even properly deal with
+# (i.e. ignore) cases in which the additional
+# INCLUDES are part of an in-line comment, ala
+# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' "
+# ) : end of non-saving group
+# \s* : any amount of white space
+# INCLUDE : match the string INCLUDE, case insensitive
+# \s+ : match one or more white space characters
+# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard
+# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol
+# (.+?) : match one or more characters that make up
+# the included path and file name and save it
+# in a group. The Fortran standard allows for
+# any non-control character to be used. The dot
+# operator will pick up any character, including
+# control codes, but I can't conceive of anyone
+# putting control codes in their file names.
+# The question mark indicates it is non-greedy so
+# that regex will match only up to the next quote,
+# double quote, or greater than symbol
+# (?=["'>]) : positive lookahead assertion to match the include
+# delimiter - an apostrophe, double quote, or
+# greater than symbol. This level of complexity
+# is required so that the include delimiter is
+# not consumed by the match, thus allowing the
+# sub-regex discussed above to uniquely match a
+# set of semicolon-separated INCLUDE statements
+# (as allowed by the F2003 standard)
+
+ include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])"""
+
+# The MODULE statement regex finds module definitions by matching
+# the following:
+#
+# MODULE module_name
+#
+# but *not* the following:
+#
+# MODULE PROCEDURE procedure_name
+#
+# Here is a breakdown of the regex:
+#
+# (?i) : regex is case insensitive
+# ^\s* : any amount of white space
+# MODULE : match the string MODULE, case insensitive
+# \s+ : match one or more white space characters
+# (?!PROCEDURE) : but *don't* match if the next word matches
+# PROCEDURE (negative lookahead assertion),
+# case insensitive
+# (\w+) : match one or more alphanumeric characters
+# that make up the defined module name and
+# save it in a group
+
+ def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)"""
+
+ scanner = F90Scanner("FortranScan",
+ "$FORTRANSUFFIXES",
+ path_variable,
+ use_regex,
+ include_regex,
+ def_regex)
+ return scanner
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
new file mode 100644
index 0000000..d3f3041
--- /dev/null
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -0,0 +1,543 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/FortranTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import sys
+import unittest
+
+import SCons.Scanner.Fortran
+import SCons.Node.FS
+import SCons.Warnings
+
+import TestCmd
+
+original = os.getcwd()
+
+test = TestCmd.TestCmd(workdir = '')
+
+os.chdir(test.workpath(''))
+
+# create some source files and headers:
+
+test.write('fff1.f',"""
+ PROGRAM FOO
+ INCLUDE 'f1.f'
+ include 'f2.f'
+ STOP
+ END
+""")
+
+test.write('fff2.f',"""
+ PROGRAM FOO
+ INCLUDE 'f2.f'
+ include 'd1/f2.f'
+ INCLUDE 'd2/f2.f'
+ STOP
+ END
+""")
+
+test.write('fff3.f',"""
+ PROGRAM FOO
+ INCLUDE 'f3.f' ; INCLUDE\t'd1/f3.f'
+ STOP
+ END
+""")
+
+
+# for Emacs -> "
+
+test.subdir('d1', ['d1', 'd2'])
+
+headers = ['fi.f', 'never.f',
+ 'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f',
+ 'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f',
+ 'd1/d2/f4.f', 'd1/d2/fi.f']
+
+for h in headers:
+ test.write(h, "\n")
+
+
+test.subdir('include', 'subdir', ['subdir', 'include'])
+
+test.write('fff4.f',"""
+ PROGRAM FOO
+ INCLUDE 'f4.f'
+ STOP
+ END
+""")
+
+test.write('include/f4.f', "\n")
+test.write('subdir/include/f4.f', "\n")
+
+test.write('fff5.f',"""
+ PROGRAM FOO
+ INCLUDE 'f5.f'
+ INCLUDE 'not_there.f'
+ STOP
+ END
+""")
+
+test.write('f5.f', "\n")
+
+test.subdir('repository', ['repository', 'include'],
+ [ 'repository', 'src' ])
+test.subdir('work', ['work', 'src'])
+
+test.write(['repository', 'include', 'iii.f'], "\n")
+
+test.write(['work', 'src', 'fff.f'], """
+ PROGRAM FOO
+ INCLUDE 'iii.f'
+ INCLUDE 'jjj.f'
+ STOP
+ END
+""")
+
+test.write([ 'work', 'src', 'aaa.f'], """
+ PROGRAM FOO
+ INCLUDE 'bbb.f'
+ STOP
+ END
+""")
+
+test.write([ 'work', 'src', 'bbb.f'], "\n")
+
+test.write([ 'repository', 'src', 'ccc.f'], """
+ PROGRAM FOO
+ INCLUDE 'ddd.f'
+ STOP
+ END
+""")
+
+test.write([ 'repository', 'src', 'ddd.f'], "\n")
+
+
+test.write('fff90a.f90',"""
+ PROGRAM FOO
+
+! Test comments - these includes should NOT be picked up
+C INCLUDE 'fi.f'
+# INCLUDE 'fi.f'
+ ! INCLUDE 'fi.f'
+
+ INCLUDE 'f1.f' ! in-line comments are valid syntax
+ INCLUDE"fi.f" ! space is significant - this should be ignored
+ INCLUDE <f2.f> ! Absoft compiler allows greater than/less than delimiters
+!
+! Allow kind type parameters
+ INCLUDE kindType_"f3.f"
+ INCLUDE kind_Type_"f4.f"
+!
+! Test multiple statements per line - use various spacings between semicolons
+ incLUDE 'f5.f';include "f6.f" ; include <f7.f>; include 'f8.f' ;include kindType_'f9.f'
+!
+! Test various USE statement syntaxes
+!
+ USE Mod01
+ use mod02
+ use use
+ USE mOD03, ONLY : someVar
+ USE MOD04 ,only:someVar
+ USE Mod05 , ONLY: someVar ! in-line comment
+ USE Mod06,ONLY :someVar,someOtherVar
+
+ USE mod07;USE mod08; USE mod09 ;USE mod10 ; USE mod11 ! Test various semicolon placements
+ use mod12 ;use mod13! Test comment at end of line
+
+! USE modi
+! USE modia ; use modib ! Scanner regexp will only ignore the first - this is a deficiency in the regexp
+ ! USE modic ; ! use modid ! Scanner regexp should ignore both modules
+ USE mod14 !; USE modi ! Only ignore the second
+ USE mod15!;USE modi
+ USE mod16 ! ; USE modi
+
+! Test semicolon syntax - use various spacings
+ USE :: mod17
+ USE::mod18
+ USE ::mod19 ; USE:: mod20
+
+ use, non_intrinsic :: mod21, ONLY : someVar ; use,intrinsic:: mod22
+ USE, NON_INTRINSIC::mod23 ; USE ,INTRINSIC ::mod24
+
+USE mod25 ! Test USE statement at the beginning of line
+
+
+; USE modi ! Scanner should ignore this since it isn't valid syntax
+ USEmodi ! No space in between USE and module name - ignore it
+ USE mod01 ! This one is a duplicate - there should only be one dependency to it.
+
+ STOP
+ END
+""")
+
+modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod',
+ 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod',
+ 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod',
+ 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod',
+ 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod']
+
+for m in modules:
+ test.write(m, "\n")
+
+test.subdir('modules')
+test.write(['modules', 'use.mod'], "\n")
+
+# define some helpers:
+
+class DummyEnvironment:
+ def __init__(self, listCppPath):
+ self.path = listCppPath
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ if not args:
+ return { 'FORTRANPATH': self.path, 'FORTRANMODSUFFIX' : ".mod" }
+ elif len(args) == 1 and args[0] == 'FORTRANPATH':
+ return self.path
+ else:
+ raise KeyError, "Dummy environment only has FORTRANPATH attribute."
+
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
+ def __getitem__(self,key):
+ return self.Dictionary()[key]
+
+ def __setitem__(self,key,value):
+ self.Dictionary()[key] = value
+
+ def __delitem__(self,key):
+ del self.Dictionary()[key]
+
+ def subst(self, arg, target=None, source=None, conv=None):
+ if arg[0] == '$':
+ return self[arg[1:]]
+ return arg
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+def deps_match(self, deps, headers):
+ scanned = map(os.path.normpath, map(str, deps))
+ expect = map(os.path.normpath, headers)
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+# define some tests:
+
+class FortranScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ test.write('f1.f', "\n")
+ test.write('f2.f', " INCLUDE 'fi.f'\n")
+ env = DummyEnvironment([])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff1.f'), env, path)
+ headers = ['f1.f', 'f2.f']
+ deps_match(self, deps, headers)
+ test.unlink('f1.f')
+ test.unlink('f2.f')
+
+class FortranScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ test.write('f1.f', "\n")
+ test.write('f2.f', " INCLUDE 'fi.f'\n")
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff1.f'), env, path)
+ headers = ['f1.f', 'f2.f']
+ deps_match(self, deps, headers)
+ test.unlink('f1.f')
+ test.unlink('f2.f')
+
+class FortranScannerTestCase3(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff1.f'), env, path)
+ headers = ['d1/f1.f', 'd1/f2.f']
+ deps_match(self, deps, headers)
+
+class FortranScannerTestCase4(unittest.TestCase):
+ def runTest(self):
+ test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n")
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff1.f'), env, path)
+ headers = ['d1/f1.f', 'd1/f2.f']
+ deps_match(self, deps, headers)
+ test.write(['d1', 'f2.f'], "\n")
+
+class FortranScannerTestCase5(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff2.f'), env, path)
+ headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/f2.f']
+ deps_match(self, deps, headers)
+
+class FortranScannerTestCase6(unittest.TestCase):
+ def runTest(self):
+ test.write('f2.f', "\n")
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff2.f'), env, path)
+ headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f']
+ deps_match(self, deps, headers)
+ test.unlink('f2.f')
+
+class FortranScannerTestCase7(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff2.f'), env, path)
+ headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f']
+ deps_match(self, deps, headers)
+
+class FortranScannerTestCase8(unittest.TestCase):
+ def runTest(self):
+ test.write('f2.f', "\n")
+ env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff2.f'), env, path)
+ headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f']
+ deps_match(self, deps, headers)
+ test.unlink('f2.f')
+
+class FortranScannerTestCase9(unittest.TestCase):
+ def runTest(self):
+ test.write('f3.f', "\n")
+ env = DummyEnvironment([])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+
+ n = env.File('fff3.f')
+ def my_rexists(s=n):
+ s.rexists_called = 1
+ return s.old_rexists()
+ setattr(n, 'old_rexists', n.rexists)
+ setattr(n, 'rexists', my_rexists)
+
+ deps = s(n, env, path)
+
+ # Make sure rexists() got called on the file node being
+ # scanned, essential for cooperation with VariantDir functionality.
+ assert n.rexists_called
+
+ headers = ['d1/f3.f', 'f3.f']
+ deps_match(self, deps, headers)
+ test.unlink('f3.f')
+
+class FortranScannerTestCase10(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(["include"])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps1 = s(env.File('fff4.f'), env, path)
+ env.fs.chdir(env.Dir('subdir'))
+ dir = env.fs.getcwd()
+ env.fs.chdir(env.Dir(''))
+ path = s.path(env, dir)
+ deps2 = s(env.File('#fff4.f'), env, path)
+ headers1 = map(test.workpath, ['include/f4.f'])
+ headers2 = ['include/f4.f']
+ deps_match(self, deps1, headers1)
+ deps_match(self, deps2, headers2)
+
+class FortranScannerTestCase11(unittest.TestCase):
+ def runTest(self):
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
+ class TestOut:
+ def __call__(self, x):
+ self.out = x
+
+ to = TestOut()
+ to.out = None
+ SCons.Warnings._warningOut = to
+ env = DummyEnvironment([])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff5.f'), env, path)
+
+ # Did we catch the warning from not finding not_there.f?
+ assert to.out
+
+ deps_match(self, deps, [ 'f5.f' ])
+
+class FortranScannerTestCase12(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([])
+ env.fs.chdir(env.Dir('include'))
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ test.write('include/fff4.f', test.read('fff4.f'))
+ deps = s(env.File('#include/fff4.f'), env, path)
+ env.fs.chdir(env.Dir(''))
+ deps_match(self, deps, ['f4.f'])
+ test.unlink('include/fff4.f')
+
+class FortranScannerTestCase13(unittest.TestCase):
+ def runTest(self):
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.Repository(test.workpath('repository'))
+
+ # Create a derived file in a directory that does not exist yet.
+ # This was a bug at one time.
+ f1=fs.File('include2/jjj.f')
+ f1.builder=1
+ env = DummyEnvironment(['include','include2'])
+ env.fs = fs
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(fs.File('src/fff.f'), env, path)
+ deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f'])
+ os.chdir(test.workpath(''))
+
+class FortranScannerTestCase14(unittest.TestCase):
+ def runTest(self):
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.VariantDir('build1', 'src', 1)
+ fs.VariantDir('build2', 'src', 0)
+ fs.Repository(test.workpath('repository'))
+ env = DummyEnvironment([])
+ env.fs = fs
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps1 = s(fs.File('build1/aaa.f'), env, path)
+ deps_match(self, deps1, [ 'build1/bbb.f' ])
+ deps2 = s(fs.File('build2/aaa.f'), env, path)
+ deps_match(self, deps2, [ 'src/bbb.f' ])
+ deps3 = s(fs.File('build1/ccc.f'), env, path)
+ deps_match(self, deps3, [ 'build1/ddd.f' ])
+ deps4 = s(fs.File('build2/ccc.f'), env, path)
+ deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ])
+ os.chdir(test.workpath(''))
+
+class FortranScannerTestCase15(unittest.TestCase):
+ def runTest(self):
+ class SubstEnvironment(DummyEnvironment):
+ def subst(self, arg, target=None, source=None, conv=None, test=test):
+ if arg == "$junk":
+ return test.workpath("d1")
+ else:
+ return arg
+ test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n")
+ env = SubstEnvironment(["$junk"])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff1.f'), env, path)
+ headers = ['d1/f1.f', 'd1/f2.f']
+ deps_match(self, deps, headers)
+ test.write(['d1', 'f2.f'], "\n")
+
+class FortranScannerTestCase16(unittest.TestCase):
+ def runTest(self):
+ test.write('f1.f', "\n")
+ test.write('f2.f', "\n")
+ test.write('f3.f', "\n")
+ test.write('f4.f', "\n")
+ test.write('f5.f', "\n")
+ test.write('f6.f', "\n")
+ test.write('f7.f', "\n")
+ test.write('f8.f', "\n")
+ test.write('f9.f', "\n")
+ test.write('f10.f', "\n")
+ env = DummyEnvironment([test.workpath('modules')])
+ s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
+ deps = s(env.File('fff90a.f90'), env, path)
+ headers = ['f1.f', 'f2.f', 'f3.f', 'f4.f', 'f5.f', 'f6.f', 'f7.f', 'f8.f', 'f9.f']
+ modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod',
+ 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod',
+ 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod',
+ 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod',
+ 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod', 'modules/use.mod']
+ deps_expected = headers + modules
+ deps_match(self, deps, deps_expected)
+ test.unlink('f1.f')
+ test.unlink('f2.f')
+ test.unlink('f3.f')
+ test.unlink('f4.f')
+ test.unlink('f5.f')
+ test.unlink('f6.f')
+ test.unlink('f7.f')
+ test.unlink('f8.f')
+ test.unlink('f9.f')
+ test.unlink('f10.f')
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(FortranScannerTestCase1())
+ suite.addTest(FortranScannerTestCase2())
+ suite.addTest(FortranScannerTestCase3())
+ suite.addTest(FortranScannerTestCase4())
+ suite.addTest(FortranScannerTestCase5())
+ suite.addTest(FortranScannerTestCase6())
+ suite.addTest(FortranScannerTestCase7())
+ suite.addTest(FortranScannerTestCase8())
+ suite.addTest(FortranScannerTestCase9())
+ suite.addTest(FortranScannerTestCase10())
+ suite.addTest(FortranScannerTestCase11())
+ suite.addTest(FortranScannerTestCase12())
+ suite.addTest(FortranScannerTestCase13())
+ suite.addTest(FortranScannerTestCase14())
+ suite.addTest(FortranScannerTestCase15())
+ suite.addTest(FortranScannerTestCase16())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/IDL.py b/src/engine/SCons/Scanner/IDL.py
new file mode 100644
index 0000000..d12f827
--- /dev/null
+++ b/src/engine/SCons/Scanner/IDL.py
@@ -0,0 +1,48 @@
+"""SCons.Scanner.IDL
+
+This module implements the depenency scanner for IDL (Interface
+Definition Language) files.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/IDL.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Node.FS
+import SCons.Scanner
+
+def IDLScan():
+ """Return a prototype Scanner instance for scanning IDL source files"""
+ cs = SCons.Scanner.ClassicCPP("IDLScan",
+ "$IDLSUFFIXES",
+ "CPPPATH",
+ '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")')
+ return cs
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py
new file mode 100644
index 0000000..9a53e38
--- /dev/null
+++ b/src/engine/SCons/Scanner/IDLTests.py
@@ -0,0 +1,453 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/IDLTests.py 4577 2009/12/27 19:44:43 scons"
+
+import TestCmd
+import SCons.Scanner.IDL
+import unittest
+import sys
+import os
+import os.path
+import SCons.Node.FS
+import SCons.Warnings
+
+test = TestCmd.TestCmd(workdir = '')
+
+os.chdir(test.workpath(''))
+
+# create some source files and headers:
+
+test.write('t1.idl','''
+#include "f1.idl"
+#include <f2.idl>
+import "f3.idl";
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring("IBarObject Interface"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+''')
+
+test.write('t2.idl',"""
+#include \"d1/f1.idl\"
+#include <d2/f1.idl>
+#include \"f1.idl\"
+import <f3.idl>;
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring(\"IBarObject Interface\"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+""")
+
+test.write('t3.idl',"""
+#include \t \"f1.idl\"
+ \t #include \"f2.idl\"
+# \t include \"f3-test.idl\"
+
+#include \t <d1/f1.idl>
+ \t #include <d1/f2.idl>
+# \t include <d1/f3-test.idl>
+
+import \t \"d1/f1.idl\"
+ \t import \"d1/f2.idl\"
+
+include \t \"never.idl\"
+ \t include \"never.idl\"
+
+// #include \"never.idl\"
+
+const char* x = \"#include <never.idl>\"
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring(\"IBarObject Interface\"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+""")
+
+test.subdir('d1', ['d1', 'd2'])
+
+headers = ['f1.idl','f2.idl', 'f3.idl', 'f3-test.idl', 'fi.idl', 'fj.idl', 'never.idl',
+ 'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl', 'd1/fi.idl', 'd1/fj.idl',
+ 'd1/d2/f1.idl', 'd1/d2/f2.idl', 'd1/d2/f3-test.idl',
+ 'd1/d2/f4.idl', 'd1/d2/fi.idl', 'd1/d2/fj.idl']
+
+for h in headers:
+ test.write(h, " ")
+
+test.write('f2.idl',"""
+#include "fi.idl"
+""")
+
+test.write('f3-test.idl',"""
+#include <fj.idl>
+""")
+
+
+test.subdir('include', 'subdir', ['subdir', 'include'])
+
+test.write('t4.idl',"""
+#include \"fa.idl\"
+#include <fb.idl>
+
+[
+ object,
+ uuid(22995106-CE26-4561-AF1B-C71C6934B840),
+ dual,
+ helpstring(\"IBarObject Interface\"),
+ pointer_default(unique)
+]
+interface IBarObject : IDispatch
+{
+};
+""")
+
+test.write(['include', 'fa.idl'], "\n")
+test.write(['include', 'fb.idl'], "\n")
+test.write(['subdir', 'include', 'fa.idl'], "\n")
+test.write(['subdir', 'include', 'fb.idl'], "\n")
+
+test.subdir('repository', ['repository', 'include'],
+ ['repository', 'src' ])
+test.subdir('work', ['work', 'src'])
+
+test.write(['repository', 'include', 'iii.idl'], "\n")
+
+test.write(['work', 'src', 'fff.c'], """
+#include <iii.idl>
+#include <jjj.idl>
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'work', 'src', 'aaa.c'], """
+#include "bbb.idl"
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'work', 'src', 'bbb.idl'], "\n")
+
+test.write([ 'repository', 'src', 'ccc.c'], """
+#include "ddd.idl"
+
+int main()
+{
+ return 0;
+}
+""")
+
+test.write([ 'repository', 'src', 'ddd.idl'], "\n")
+
+# define some helpers:
+
+class DummyEnvironment:
+ def __init__(self, listCppPath):
+ self.path = listCppPath
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ if not args:
+ return { 'CPPPATH': self.path }
+ elif len(args) == 1 and args[0] == 'CPPPATH':
+ return self.path
+ else:
+ raise KeyError, "Dummy environment only has CPPPATH attribute."
+
+ def subst(self, arg, target=None, source=None, conv=None):
+ return arg
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
+ def __getitem__(self,key):
+ return self.Dictionary()[key]
+
+ def __setitem__(self,key,value):
+ self.Dictionary()[key] = value
+
+ def __delitem__(self,key):
+ del self.Dictionary()[key]
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+global my_normpath
+my_normpath = os.path.normpath
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+
+def deps_match(self, deps, headers):
+ scanned = map(my_normpath, map(str, deps))
+ expect = map(my_normpath, headers)
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+# define some tests:
+
+class IDLScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(env.File('t1.idl'), env, path)
+ headers = ['f1.idl', 'f3.idl', 'f2.idl']
+ deps_match(self, deps, headers)
+
+class IDLScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(env.File('t1.idl'), env, path)
+ headers = ['f1.idl', 'f3.idl', 'd1/f2.idl']
+ deps_match(self, deps, headers)
+
+class IDLScannerTestCase3(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(env.File('t2.idl'), env, path)
+ headers = ['d1/f1.idl', 'f1.idl', 'd1/d2/f1.idl', 'f3.idl']
+ deps_match(self, deps, headers)
+
+class IDLScannerTestCase4(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(env.File('t2.idl'), env, path)
+ headers = ['d1/f1.idl', 'f1.idl', 'd1/d2/f1.idl', 'f3.idl']
+ deps_match(self, deps, headers)
+
+class IDLScannerTestCase5(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+
+ n = env.File('t3.idl')
+ def my_rexists(s=n):
+ s.rexists_called = 1
+ return s.old_rexists()
+ setattr(n, 'old_rexists', n.rexists)
+ setattr(n, 'rexists', my_rexists)
+
+ deps = s(n, env, path)
+
+ # Make sure rexists() got called on the file node being
+ # scanned, essential for cooperation with VariantDir functionality.
+ assert n.rexists_called
+
+ headers = ['d1/f1.idl', 'd1/f2.idl',
+ 'f1.idl', 'f2.idl', 'f3-test.idl',
+ 'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl']
+ deps_match(self, deps, headers)
+
+class IDLScannerTestCase6(unittest.TestCase):
+ def runTest(self):
+ env1 = DummyEnvironment([test.workpath("d1")])
+ env2 = DummyEnvironment([test.workpath("d1/d2")])
+ s = SCons.Scanner.IDL.IDLScan()
+ path1 = s.path(env1)
+ path2 = s.path(env2)
+ deps1 = s(env1.File('t1.idl'), env1, path1)
+ deps2 = s(env2.File('t1.idl'), env2, path2)
+ headers1 = ['f1.idl', 'f3.idl', 'd1/f2.idl']
+ headers2 = ['f1.idl', 'f3.idl', 'd1/d2/f2.idl']
+ deps_match(self, deps1, headers1)
+ deps_match(self, deps2, headers2)
+
+class IDLScannerTestCase7(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(["include"])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps1 = s(env.File('t4.idl'), env, path)
+ env.fs.chdir(env.Dir('subdir'))
+ dir = env.fs.getcwd()
+ env.fs.chdir(env.Dir(''))
+ path = s.path(env, dir)
+ deps2 = s(env.File('#t4.idl'), env, path)
+ headers1 = map(test.workpath, ['include/fa.idl', 'include/fb.idl'])
+ headers2 = ['include/fa.idl', 'include/fb.idl']
+ deps_match(self, deps1, headers1)
+ deps_match(self, deps2, headers2)
+
+class IDLScannerTestCase8(unittest.TestCase):
+ def runTest(self):
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
+ class TestOut:
+ def __call__(self, x):
+ self.out = x
+
+ to = TestOut()
+ to.out = None
+ SCons.Warnings._warningOut = to
+ test.write('fa.idl','\n')
+ env = DummyEnvironment([])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(env.File('t4.idl'), env, path)
+
+ # Did we catch the warning associated with not finding fb.idl?
+ assert to.out
+
+ deps_match(self, deps, [ 'fa.idl' ])
+ test.unlink('fa.idl')
+
+class IDLScannerTestCase9(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment([])
+ env.fs.chdir(env.Dir('include'))
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ test.write('include/t4.idl', test.read('t4.idl'))
+ deps = s(env.File('#include/t4.idl'), env, path)
+ env.fs.chdir(env.Dir(''))
+ deps_match(self, deps, [ 'fa.idl', 'fb.idl' ])
+ test.unlink('include/t4.idl')
+
+class IDLScannerTestCase10(unittest.TestCase):
+ def runTest(self):
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.Repository(test.workpath('repository'))
+
+ # Create a derived file in a directory that does not exist yet.
+ # This was a bug at one time.
+ env = DummyEnvironment(['include', 'include2'])
+ env.fs = fs
+ f1 = fs.File('include2/jjj.idl')
+ f1.builder = 1
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(fs.File('src/fff.c'), env, path)
+ deps_match(self, deps, [ test.workpath('repository/include/iii.idl'),
+ 'include2/jjj.idl' ])
+ os.chdir(test.workpath(''))
+
+class IDLScannerTestCase11(unittest.TestCase):
+ def runTest(self):
+ os.chdir(test.workpath('work'))
+ fs = SCons.Node.FS.FS(test.workpath('work'))
+ fs.VariantDir('build1', 'src', 1)
+ fs.VariantDir('build2', 'src', 0)
+ fs.Repository(test.workpath('repository'))
+ env = DummyEnvironment([])
+ env.fs = fs
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps1 = s(fs.File('build1/aaa.c'), env, path)
+ deps_match(self, deps1, [ 'build1/bbb.idl' ])
+ deps2 = s(fs.File('build2/aaa.c'), env, path)
+ deps_match(self, deps2, [ 'src/bbb.idl' ])
+ deps3 = s(fs.File('build1/ccc.c'), env, path)
+ deps_match(self, deps3, [ 'build1/ddd.idl' ])
+ deps4 = s(fs.File('build2/ccc.c'), env, path)
+ deps_match(self, deps4, [ test.workpath('repository/src/ddd.idl') ])
+ os.chdir(test.workpath(''))
+
+class IDLScannerTestCase12(unittest.TestCase):
+ def runTest(self):
+ class SubstEnvironment(DummyEnvironment):
+ def subst(self, arg, target=None, source=None, conv=None, test=test):
+ if arg == "$blah":
+ return test.workpath("d1")
+ else:
+ return arg
+ env = SubstEnvironment(["$blah"])
+ s = SCons.Scanner.IDL.IDLScan()
+ path = s.path(env)
+ deps = s(env.File('t1.idl'), env, path)
+ headers = ['f1.idl', 'f3.idl', 'd1/f2.idl']
+ deps_match(self, deps, headers)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(IDLScannerTestCase1())
+ suite.addTest(IDLScannerTestCase2())
+ suite.addTest(IDLScannerTestCase3())
+ suite.addTest(IDLScannerTestCase4())
+ suite.addTest(IDLScannerTestCase5())
+ suite.addTest(IDLScannerTestCase6())
+ suite.addTest(IDLScannerTestCase7())
+ suite.addTest(IDLScannerTestCase8())
+ suite.addTest(IDLScannerTestCase9())
+ suite.addTest(IDLScannerTestCase10())
+ suite.addTest(IDLScannerTestCase11())
+ suite.addTest(IDLScannerTestCase12())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py
new file mode 100644
index 0000000..0aca69d
--- /dev/null
+++ b/src/engine/SCons/Scanner/LaTeX.py
@@ -0,0 +1,345 @@
+"""SCons.Scanner.LaTeX
+
+This module implements the dependency scanner for LaTeX code.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/LaTeX.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+import re
+
+import SCons.Scanner
+import SCons.Util
+
+# list of graphics file extensions for TeX and LaTeX
+TexGraphics = ['.eps', '.ps']
+LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif']
+
+# Used as a return value of modify_env_var if the variable is not set.
+class _Null:
+ pass
+_null = _Null
+
+# The user specifies the paths in env[variable], similar to other builders.
+# They may be relative and must be converted to absolute, as expected
+# by LaTeX and Co. The environment may already have some paths in
+# env['ENV'][var]. These paths are honored, but the env[var] paths have
+# higher precedence. All changes are un-done on exit.
+def modify_env_var(env, var, abspath):
+ try:
+ save = env['ENV'][var]
+ except KeyError:
+ save = _null
+ env.PrependENVPath(var, abspath)
+ try:
+ if SCons.Util.is_List(env[var]):
+ #TODO(1.5)
+ #env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]])
+ env.PrependENVPath(var, map(lambda p: os.path.abspath(str(p)), env[var]))
+ else:
+ # Split at os.pathsep to convert into absolute path
+ #TODO(1.5) env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)])
+ env.PrependENVPath(var, map(lambda p: os.path.abspath(p), string.split(str(env[var]), os.pathsep)))
+ except KeyError:
+ pass
+
+ # Convert into a string explicitly to append ":" (without which it won't search system
+ # paths as well). The problem is that env.AppendENVPath(var, ":")
+ # does not work, refuses to append ":" (os.pathsep).
+
+ if SCons.Util.is_List(env['ENV'][var]):
+ # TODO(1.5)
+ #env['ENV'][var] = os.pathsep.join(env['ENV'][var])
+ env['ENV'][var] = string.join(env['ENV'][var], os.pathsep)
+ # Append the trailing os.pathsep character here to catch the case with no env[var]
+ env['ENV'][var] = env['ENV'][var] + os.pathsep
+
+ return save
+
+class FindENVPathDirs:
+ """A class to bind a specific *PATH variable name to a function that
+ will return all of the *path directories."""
+ def __init__(self, variable):
+ self.variable = variable
+ def __call__(self, env, dir=None, target=None, source=None, argument=None):
+ import SCons.PathList
+ try:
+ path = env['ENV'][self.variable]
+ except KeyError:
+ return ()
+
+ dir = dir or env.fs._cwd
+ path = SCons.PathList.PathList(path).subst_path(env, target, source)
+ return tuple(dir.Rfindalldirs(path))
+
+
+
+def LaTeXScanner():
+ """Return a prototype Scanner instance for scanning LaTeX source files
+ when built with latex.
+ """
+ ds = LaTeX(name = "LaTeXScanner",
+ suffixes = '$LATEXSUFFIXES',
+ # in the search order, see below in LaTeX class docstring
+ graphics_extensions = TexGraphics,
+ recursive = 0)
+ return ds
+
+def PDFLaTeXScanner():
+ """Return a prototype Scanner instance for scanning LaTeX source files
+ when built with pdflatex.
+ """
+ ds = LaTeX(name = "PDFLaTeXScanner",
+ suffixes = '$LATEXSUFFIXES',
+ # in the search order, see below in LaTeX class docstring
+ graphics_extensions = LatexGraphics,
+ recursive = 0)
+ return ds
+
+class LaTeX(SCons.Scanner.Base):
+ """Class for scanning LaTeX files for included files.
+
+ Unlike most scanners, which use regular expressions that just
+ return the included file name, this returns a tuple consisting
+ of the keyword for the inclusion ("include", "includegraphics",
+ "input", or "bibliography"), and then the file name itself.
+ Based on a quick look at LaTeX documentation, it seems that we
+ should append .tex suffix for the "include" keywords, append .tex if
+ there is no extension for the "input" keyword, and need to add .bib
+ for the "bibliography" keyword that does not accept extensions by itself.
+
+ Finally, if there is no extension for an "includegraphics" keyword
+ latex will append .ps or .eps to find the file, while pdftex may use .pdf,
+ .jpg, .tif, .mps, or .png.
+
+ The actual subset and search order may be altered by
+ DeclareGraphicsExtensions command. This complication is ignored.
+ The default order corresponds to experimentation with teTeX
+ $ latex --version
+ pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4)
+ kpathsea version 3.5.4
+ The order is:
+ ['.eps', '.ps'] for latex
+ ['.png', '.pdf', '.jpg', '.tif'].
+
+ Another difference is that the search path is determined by the type
+ of the file being searched:
+ env['TEXINPUTS'] for "input" and "include" keywords
+ env['TEXINPUTS'] for "includegraphics" keyword
+ env['TEXINPUTS'] for "lstinputlisting" keyword
+ env['BIBINPUTS'] for "bibliography" keyword
+ env['BSTINPUTS'] for "bibliographystyle" keyword
+
+ FIXME: also look for the class or style in document[class|style]{}
+ FIXME: also look for the argument of bibliographystyle{}
+ """
+ keyword_paths = {'include': 'TEXINPUTS',
+ 'input': 'TEXINPUTS',
+ 'includegraphics': 'TEXINPUTS',
+ 'bibliography': 'BIBINPUTS',
+ 'bibliographystyle': 'BSTINPUTS',
+ 'usepackage': 'TEXINPUTS',
+ 'lstinputlisting': 'TEXINPUTS'}
+ env_variables = SCons.Util.unique(keyword_paths.values())
+
+ def __init__(self, name, suffixes, graphics_extensions, *args, **kw):
+
+ # We have to include \n with the % we exclude from the first part
+ # part of the regex because the expression is compiled with re.M.
+ # Without the \n, the ^ could match the beginning of a *previous*
+ # line followed by one or more newline characters (i.e. blank
+ # lines), interfering with a match on the next line.
+ regex = r'^[^%\n]*\\(include|includegraphics(?:\[[^\]]+\])?|lstinputlisting(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}'
+ self.cre = re.compile(regex, re.M)
+ self.graphics_extensions = graphics_extensions
+
+ def _scan(node, env, path=(), self=self):
+ node = node.rfile()
+ if not node.exists():
+ return []
+ return self.scan(node, path)
+
+ class FindMultiPathDirs:
+ """The stock FindPathDirs function has the wrong granularity:
+ it is called once per target, while we need the path that depends
+ on what kind of included files is being searched. This wrapper
+ hides multiple instances of FindPathDirs, one per the LaTeX path
+ variable in the environment. When invoked, the function calculates
+ and returns all the required paths as a dictionary (converted into
+ a tuple to become hashable). Then the scan function converts it
+ back and uses a dictionary of tuples rather than a single tuple
+ of paths.
+ """
+ def __init__(self, dictionary):
+ self.dictionary = {}
+ for k,n in dictionary.items():
+ self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n),
+ FindENVPathDirs(n) )
+
+ def __call__(self, env, dir=None, target=None, source=None,
+ argument=None):
+ di = {}
+ for k,(c,cENV) in self.dictionary.items():
+ di[k] = ( c(env, dir=None, target=None, source=None,
+ argument=None) ,
+ cENV(env, dir=None, target=None, source=None,
+ argument=None) )
+ # To prevent "dict is not hashable error"
+ return tuple(di.items())
+
+ class LaTeXScanCheck:
+ """Skip all but LaTeX source files, i.e., do not scan *.eps,
+ *.pdf, *.jpg, etc.
+ """
+ def __init__(self, suffixes):
+ self.suffixes = suffixes
+ def __call__(self, node, env):
+ current = not node.has_builder() or node.is_up_to_date()
+ scannable = node.get_suffix() in env.subst_list(self.suffixes)[0]
+ # Returning false means that the file is not scanned.
+ return scannable and current
+
+ kw['function'] = _scan
+ kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths)
+ kw['recursive'] = 1
+ kw['skeys'] = suffixes
+ kw['scan_check'] = LaTeXScanCheck(suffixes)
+ kw['name'] = name
+
+ apply(SCons.Scanner.Base.__init__, (self,) + args, kw)
+
+ def _latex_names(self, include):
+ filename = include[1]
+ if include[0] == 'input':
+ base, ext = os.path.splitext( filename )
+ if ext == "":
+ return [filename + '.tex']
+ if (include[0] == 'include'):
+ return [filename + '.tex']
+ if include[0] == 'bibliography':
+ base, ext = os.path.splitext( filename )
+ if ext == "":
+ return [filename + '.bib']
+ if include[0] == 'usepackage':
+ base, ext = os.path.splitext( filename )
+ if ext == "":
+ return [filename + '.sty']
+ if include[0] == 'includegraphics':
+ base, ext = os.path.splitext( filename )
+ if ext == "":
+ #TODO(1.5) return [filename + e for e in self.graphics_extensions]
+ #return map(lambda e, f=filename: f+e, self.graphics_extensions + TexGraphics)
+ # use the line above to find dependency for PDF builder when only .eps figure is present
+ # Since it will be found if the user tell scons how to make the pdf figure leave it out for now.
+ return map(lambda e, f=filename: f+e, self.graphics_extensions)
+ return [filename]
+
+ def sort_key(self, include):
+ return SCons.Node.FS._my_normcase(str(include))
+
+ def find_include(self, include, source_dir, path):
+ try:
+ sub_path = path[include[0]]
+ except (IndexError, KeyError):
+ sub_path = ()
+ try_names = self._latex_names(include)
+ for n in try_names:
+ # see if we find it using the path in env[var]
+ i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0])
+ if i:
+ return i, include
+ # see if we find it using the path in env['ENV'][var]
+ i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1])
+ if i:
+ return i, include
+ return i, include
+
+ def scan(self, node, path=()):
+ # Modify the default scan function to allow for the regular
+ # expression to return a comma separated list of file names
+ # as can be the case with the bibliography keyword.
+
+ # Cache the includes list in node so we only scan it once:
+ path_dict = dict(list(path))
+ noopt_cre = re.compile('\[.*$')
+ if node.includes != None:
+ includes = node.includes
+ else:
+ includes = self.cre.findall(node.get_text_contents())
+ # 1. Split comma-separated lines, e.g.
+ # ('bibliography', 'phys,comp')
+ # should become two entries
+ # ('bibliography', 'phys')
+ # ('bibliography', 'comp')
+ # 2. Remove the options, e.g., such as
+ # ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps')
+ # should become
+ # ('includegraphics', 'picture.eps')
+ split_includes = []
+ for include in includes:
+ inc_type = noopt_cre.sub('', include[0])
+ inc_list = string.split(include[1],',')
+ for j in range(len(inc_list)):
+ split_includes.append( (inc_type, inc_list[j]) )
+ #
+ includes = split_includes
+ node.includes = includes
+
+ # This is a hand-coded DSU (decorate-sort-undecorate, or
+ # Schwartzian transform) pattern. The sort key is the raw name
+ # of the file as specifed on the \include, \input, etc. line.
+ # TODO: what about the comment in the original Classic scanner:
+ # """which lets
+ # us keep the sort order constant regardless of whether the file
+ # is actually found in a Repository or locally."""
+ nodes = []
+ source_dir = node.get_dir()
+ for include in includes:
+ #
+ # Handle multiple filenames in include[1]
+ #
+ n, i = self.find_include(include, source_dir, path_dict)
+ if n is None:
+ # Do not bother with 'usepackage' warnings, as they most
+ # likely refer to system-level files
+ if include[0] != 'usepackage':
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
+ else:
+ sortkey = self.sort_key(n)
+ nodes.append((sortkey, n))
+ #
+ nodes.sort()
+ nodes = map(lambda pair: pair[1], nodes)
+ return nodes
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/LaTeXTests.py b/src/engine/SCons/Scanner/LaTeXTests.py
new file mode 100644
index 0000000..012ad4b
--- /dev/null
+++ b/src/engine/SCons/Scanner/LaTeXTests.py
@@ -0,0 +1,162 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/LaTeXTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+import sys
+import types
+import unittest
+import UserDict
+
+import TestCmd
+import SCons.Node.FS
+import SCons.Scanner.LaTeX
+
+test = TestCmd.TestCmd(workdir = '')
+
+test.write('test1.latex',"""
+\include{inc1}
+\input{inc2}
+include{incNO}
+%\include{incNO}
+xyzzy \include{inc6}
+""")
+
+test.write('test2.latex',"""
+\include{inc1}
+\include{inc3}
+""")
+
+test.write('test3.latex',"""
+\includegraphics{inc4.eps}
+\includegraphics[width=60mm]{inc5.xyz}
+""")
+
+test.subdir('subdir')
+
+test.write('inc1.tex',"\n")
+test.write('inc2.tex',"\n")
+test.write(['subdir', 'inc3.tex'], "\n")
+test.write(['subdir', 'inc4.eps'], "\n")
+test.write('inc5.xyz', "\n")
+test.write('inc6.tex', "\n")
+test.write('incNO.tex', "\n")
+
+# define some helpers:
+# copied from CTest.py
+class DummyEnvironment(UserDict.UserDict):
+ def __init__(self, **kw):
+ UserDict.UserDict.__init__(self)
+ self.data.update(kw)
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ return self.data
+
+ def subst(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return self.data[strSubst[1:]]
+ return strSubst
+
+ def subst_list(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return [self.data[strSubst[1:]]]
+ return [[strSubst]]
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+else:
+ my_normpath = os.path.normpath
+
+def deps_match(self, deps, headers):
+ global my_normpath
+ scanned = map(my_normpath, map(str, deps))
+ expect = map(my_normpath, headers)
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+
+class LaTeXScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LATEXSUFFIXES = [".tex", ".ltx", ".latex"])
+ s = SCons.Scanner.LaTeX.LaTeXScanner()
+ path = s.path(env)
+ deps = s(env.File('test1.latex'), env, path)
+ headers = ['inc1.tex', 'inc2.tex', 'inc6.tex']
+ deps_match(self, deps, headers)
+
+class LaTeXScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")],LATEXSUFFIXES = [".tex", ".ltx", ".latex"])
+ s = SCons.Scanner.LaTeX.LaTeXScanner()
+ path = s.path(env)
+ deps = s(env.File('test2.latex'), env, path)
+ headers = ['inc1.tex', 'subdir/inc3.tex']
+ deps_match(self, deps, headers)
+
+class LaTeXScannerTestCase3(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")],LATEXSUFFIXES = [".tex", ".ltx", ".latex"])
+ s = SCons.Scanner.LaTeX.LaTeXScanner()
+ path = s.path(env)
+ deps = s(env.File('test3.latex'), env, path)
+ files = ['inc5.xyz', 'subdir/inc4.eps']
+ deps_match(self, deps, files)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(LaTeXScannerTestCase1())
+ suite.addTest(LaTeXScannerTestCase2())
+ suite.addTest(LaTeXScannerTestCase3())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py
new file mode 100644
index 0000000..bdc11c0
--- /dev/null
+++ b/src/engine/SCons/Scanner/Prog.py
@@ -0,0 +1,103 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/Prog.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+
+import SCons.Node
+import SCons.Node.FS
+import SCons.Scanner
+import SCons.Util
+
+# global, set by --debug=findlibs
+print_find_libs = None
+
+def ProgramScanner(**kw):
+ """Return a prototype Scanner instance for scanning executable
+ files for static-lib dependencies"""
+ kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH')
+ ps = apply(SCons.Scanner.Base, [scan, "ProgramScanner"], kw)
+ return ps
+
+def scan(node, env, libpath = ()):
+ """
+ This scanner scans program files for static-library
+ dependencies. It will search the LIBPATH environment variable
+ for libraries specified in the LIBS variable, returning any
+ files it finds as dependencies.
+ """
+ try:
+ libs = env['LIBS']
+ except KeyError:
+ # There are no LIBS in this environment, so just return a null list:
+ return []
+ if SCons.Util.is_String(libs):
+ libs = string.split(libs)
+ else:
+ libs = SCons.Util.flatten(libs)
+
+ try:
+ prefix = env['LIBPREFIXES']
+ if not SCons.Util.is_List(prefix):
+ prefix = [ prefix ]
+ except KeyError:
+ prefix = [ '' ]
+
+ try:
+ suffix = env['LIBSUFFIXES']
+ if not SCons.Util.is_List(suffix):
+ suffix = [ suffix ]
+ except KeyError:
+ suffix = [ '' ]
+
+ pairs = []
+ for suf in map(env.subst, suffix):
+ for pref in map(env.subst, prefix):
+ pairs.append((pref, suf))
+
+ result = []
+
+ if callable(libpath):
+ libpath = libpath()
+
+ find_file = SCons.Node.FS.find_file
+ adjustixes = SCons.Util.adjustixes
+ for lib in libs:
+ if SCons.Util.is_String(lib):
+ lib = env.subst(lib)
+ for pref, suf in pairs:
+ l = adjustixes(lib, pref, suf)
+ l = find_file(l, libpath, verbose=print_find_libs)
+ if l:
+ result.append(l)
+ else:
+ result.append(lib)
+
+ return result
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py
new file mode 100644
index 0000000..2a5761e
--- /dev/null
+++ b/src/engine/SCons/Scanner/ProgTests.py
@@ -0,0 +1,262 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/ProgTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+import sys
+import types
+import unittest
+
+import TestCmd
+import SCons.Node.FS
+import SCons.Scanner.Prog
+
+test = TestCmd.TestCmd(workdir = '')
+
+test.subdir('d1', ['d1', 'd2'], 'dir', ['dir', 'sub'])
+
+libs = [ 'l1.lib', 'd1/l2.lib', 'd1/d2/l3.lib',
+ 'dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']
+
+for h in libs:
+ test.write(h, "\n")
+
+# define some helpers:
+
+class DummyEnvironment:
+ def __init__(self, **kw):
+ self._dict = {'LIBSUFFIXES' : '.lib'}
+ self._dict.update(kw)
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ if not args:
+ return self._dict
+ elif len(args) == 1:
+ return self._dict[args[0]]
+ else:
+ return map(lambda x, s=self: s._dict[x], args)
+
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
+ def __getitem__(self,key):
+ return self.Dictionary()[key]
+
+ def __setitem__(self,key,value):
+ self.Dictionary()[key] = value
+
+ def __delitem__(self,key):
+ del self.Dictionary()[key]
+
+ def subst(self, s, target=None, source=None, conv=None):
+ try:
+ if s[0] == '$':
+ return self._dict[s[1:]]
+ except IndexError:
+ return ''
+ return s
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(test.workpath(filename))
+
+ def File(self, filename):
+ return self.fs.File(test.workpath(filename))
+
+class DummyNode:
+ def __init__(self, name):
+ self.name = name
+ def rexists(self):
+ return 1
+ def __str__(self):
+ return self.name
+
+def deps_match(deps, libs):
+ deps=map(str, deps)
+ deps.sort()
+ libs.sort()
+ return map(os.path.normpath, deps) == map(os.path.normpath, libs)
+
+# define some tests:
+
+class ProgramScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LIBPATH=[ test.workpath("") ],
+ LIBS=[ 'l1', 'l2', 'l3' ])
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['l1.lib']), map(str, deps)
+
+ env = DummyEnvironment(LIBPATH=[ test.workpath("") ],
+ LIBS='l1')
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['l1.lib']), map(str, deps)
+
+ f1 = env.fs.File(test.workpath('f1'))
+ env = DummyEnvironment(LIBPATH=[ test.workpath("") ],
+ LIBS=[f1])
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps[0] is f1, deps
+
+ f2 = env.fs.File(test.workpath('f1'))
+ env = DummyEnvironment(LIBPATH=[ test.workpath("") ],
+ LIBS=f2)
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps[0] is f2, deps
+
+
+class ProgramScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LIBPATH=map(test.workpath,
+ ["", "d1", "d1/d2" ]),
+ LIBS=[ 'l1', 'l2', 'l3' ])
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['l1.lib', 'd1/l2.lib', 'd1/d2/l3.lib' ]), map(str, deps)
+
+class ProgramScannerTestCase3(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LIBPATH=[test.workpath("d1/d2"),
+ test.workpath("d1")],
+ LIBS=string.split('l2 l3'))
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps)
+
+class ProgramScannerTestCase5(unittest.TestCase):
+ def runTest(self):
+ class SubstEnvironment(DummyEnvironment):
+ def subst(self, arg, target=None, source=None, conv=None, path=test.workpath("d1")):
+ if arg == "$blah":
+ return test.workpath("d1")
+ else:
+ return arg
+ env = SubstEnvironment(LIBPATH=[ "$blah" ],
+ LIBS=string.split('l2 l3'))
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, [ 'd1/l2.lib' ]), map(str, deps)
+
+class ProgramScannerTestCase6(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ],
+ LIBS=['foo', 'sub/libbar', 'xyz.other'],
+ LIBPREFIXES=['lib'],
+ LIBSUFFIXES=['.a'])
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']), map(str, deps)
+
+class ProgramScannerTestCase7(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ],
+ LIBS=['foo', '$LIBBAR', '$XYZ'],
+ LIBPREFIXES=['lib'],
+ LIBSUFFIXES=['.a'],
+ LIBBAR='sub/libbar',
+ XYZ='xyz.other')
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']), map(str, deps)
+
+class ProgramScannerTestCase8(unittest.TestCase):
+ def runTest(self):
+
+ n1 = DummyNode('n1')
+ env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ],
+ LIBS=[n1],
+ LIBPREFIXES=['p1-', 'p2-'],
+ LIBSUFFIXES=['.1', '2'])
+ s = SCons.Scanner.Prog.ProgramScanner(node_class = DummyNode)
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps == [n1], deps
+
+ n2 = DummyNode('n2')
+ env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ],
+ LIBS=[n1, [n2]],
+ LIBPREFIXES=['p1-', 'p2-'],
+ LIBSUFFIXES=['.1', '2'])
+ s = SCons.Scanner.Prog.ProgramScanner(node_class = DummyNode)
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps == [n1, n2], deps
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(ProgramScannerTestCase1())
+ suite.addTest(ProgramScannerTestCase2())
+ suite.addTest(ProgramScannerTestCase3())
+ suite.addTest(ProgramScannerTestCase5())
+ suite.addTest(ProgramScannerTestCase6())
+ suite.addTest(ProgramScannerTestCase7())
+ suite.addTest(ProgramScannerTestCase8())
+ if hasattr(types, 'UnicodeType'):
+ code = """if 1:
+ class ProgramScannerTestCase4(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(LIBPATH=[test.workpath("d1/d2"),
+ test.workpath("d1")],
+ LIBS=string.split(u'l2 l3'))
+ s = SCons.Scanner.Prog.ProgramScanner()
+ path = s.path(env)
+ deps = s(DummyNode('dummy'), env, path)
+ assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps)
+ suite.addTest(ProgramScannerTestCase4())
+ \n"""
+ exec code
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/RC.py b/src/engine/SCons/Scanner/RC.py
new file mode 100644
index 0000000..7d73125
--- /dev/null
+++ b/src/engine/SCons/Scanner/RC.py
@@ -0,0 +1,55 @@
+"""SCons.Scanner.RC
+
+This module implements the depenency scanner for RC (Interface
+Definition Language) files.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/RC.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Node.FS
+import SCons.Scanner
+import re
+
+def RCScan():
+ """Return a prototype Scanner instance for scanning RC source files"""
+
+ res_re= r'^(?:\s*#\s*(?:include)|' \
+ '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \
+ '\s*.*?)' \
+ '\s*(<|"| )([^>"\s]+)(?:[>" ])*$'
+ resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner",
+ "$RCSUFFIXES",
+ "CPPPATH",
+ res_re )
+
+ return resScanner
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/RCTests.py b/src/engine/SCons/Scanner/RCTests.py
new file mode 100644
index 0000000..ebf298b
--- /dev/null
+++ b/src/engine/SCons/Scanner/RCTests.py
@@ -0,0 +1,168 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/RCTests.py 4577 2009/12/27 19:44:43 scons"
+
+import TestCmd
+import SCons.Scanner.RC
+import unittest
+import sys
+import os
+import os.path
+import SCons.Node.FS
+import SCons.Warnings
+import UserDict
+
+test = TestCmd.TestCmd(workdir = '')
+
+os.chdir(test.workpath(''))
+
+# create some source files and headers:
+
+test.write('t1.rc','''
+#include "t1.h"
+''')
+
+test.write('t2.rc',"""
+#include "t1.h"
+ICO_TEST ICON DISCARDABLE "abc.ico"
+BMP_TEST BITMAP DISCARDABLE "def.bmp"
+cursor1 CURSOR "bullseye.cur"
+ID_RESPONSE_ERROR_PAGE HTML "responseerrorpage.htm"
+5 FONT "cmroman.fnt"
+1 MESSAGETABLE "MSG00409.bin"
+1 MESSAGETABLE MSG00410.bin
+1 TYPELIB "testtypelib.tlb"
+TEST_REGIS REGISTRY MOVEABLE PURE "testregis.rgs"
+TEST_D3DFX D3DFX DISCARDABLE "testEffect.fx"
+
+""")
+
+
+# Create dummy include files
+headers = ['t1.h',
+ 'abc.ico','def.bmp','bullseye.cur','responseerrorpage.htm','cmroman.fnt',
+ 'testEffect.fx',
+ 'MSG00409.bin','MSG00410.bin','testtypelib.tlb','testregis.rgs']
+
+for h in headers:
+ test.write(h, " ")
+
+
+# define some helpers:
+
+class DummyEnvironment(UserDict.UserDict):
+ def __init__(self,**kw):
+ UserDict.UserDict.__init__(self)
+ self.data.update(kw)
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ return self.data
+
+ def subst(self, arg, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return self.data[strSubst[1:]]
+ return strSubst
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+global my_normpath
+my_normpath = os.path.normpath
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+
+def deps_match(self, deps, headers):
+ scanned = map(my_normpath, map(str, deps))
+ expect = map(my_normpath, headers)
+ scanned.sort()
+ expect.sort()
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+# define some tests:
+
+class RCScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ path = []
+ env = DummyEnvironment(RCSUFFIXES=['.rc','.rc2'],
+ CPPPATH=path)
+ s = SCons.Scanner.RC.RCScan()
+ deps = s(env.File('t1.rc'), env, path)
+ headers = ['t1.h']
+ deps_match(self, deps, headers)
+
+class RCScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ path = []
+ env = DummyEnvironment(RCSUFFIXES=['.rc','.rc2'],
+ CPPPATH=path)
+ s = SCons.Scanner.RC.RCScan()
+ deps = s(env.File('t2.rc'), env, path)
+ headers = ['MSG00410.bin',
+ 'abc.ico','bullseye.cur',
+ 'cmroman.fnt','def.bmp',
+ 'MSG00409.bin',
+ 'responseerrorpage.htm',
+ 't1.h',
+ 'testEffect.fx',
+ 'testregis.rgs','testtypelib.tlb']
+ deps_match(self, deps, headers)
+
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(RCScannerTestCase1())
+ suite.addTest(RCScannerTestCase2())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
new file mode 100644
index 0000000..ae6cb79
--- /dev/null
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -0,0 +1,611 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/ScannerTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+import UserDict
+
+import SCons.Scanner
+
+class DummyFS:
+ def File(self, name):
+ return DummyNode(name)
+
+class DummyEnvironment(UserDict.UserDict):
+ def __init__(self, dict=None, **kw):
+ UserDict.UserDict.__init__(self, dict)
+ self.data.update(kw)
+ self.fs = DummyFS()
+ def subst(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return self.data[strSubst[1:]]
+ return strSubst
+ def subst_list(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return [self.data[strSubst[1:]]]
+ return [[strSubst]]
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+class DummyNode:
+ def __init__(self, name, search_result=()):
+ self.name = name
+ self.search_result = tuple(search_result)
+ def rexists(self):
+ return 1
+ def __str__(self):
+ return self.name
+ def Rfindalldirs(self, pathlist):
+ return self.search_result + pathlist
+
+class FindPathDirsTestCase(unittest.TestCase):
+ def test_FindPathDirs(self):
+ """Test the FindPathDirs callable class"""
+
+ env = DummyEnvironment(LIBPATH = [ 'foo' ])
+ env.fs = DummyFS()
+ env.fs._cwd = DummyNode('cwd')
+
+ dir = DummyNode('dir', ['xxx'])
+ fpd = SCons.Scanner.FindPathDirs('LIBPATH')
+ result = fpd(env)
+ assert str(result) == "('foo',)", result
+ result = fpd(env, dir)
+ assert str(result) == "('xxx', 'foo')", result
+
+class ScannerTestCase(unittest.TestCase):
+
+ def test_creation(self):
+ """Test creation of Scanner objects"""
+ def func(self):
+ pass
+ s = SCons.Scanner.Base(func)
+ assert isinstance(s, SCons.Scanner.Base), s
+ s = SCons.Scanner.Base({})
+ assert isinstance(s, SCons.Scanner.Base), s
+
+ s = SCons.Scanner.Base(func, name='fooscan')
+ assert str(s) == 'fooscan', str(s)
+ s = SCons.Scanner.Base({}, name='barscan')
+ assert str(s) == 'barscan', str(s)
+
+ s = SCons.Scanner.Base(func, name='fooscan', argument=9)
+ assert str(s) == 'fooscan', str(s)
+ assert s.argument == 9, s.argument
+ s = SCons.Scanner.Base({}, name='fooscan', argument=888)
+ assert str(s) == 'fooscan', str(s)
+ assert s.argument == 888, s.argument
+
+
+class BaseTestCase(unittest.TestCase):
+
+ class skey_node:
+ def __init__(self, key):
+ self.key = key
+ def scanner_key(self):
+ return self.key
+ def rexists(self):
+ return 1
+
+ def func(self, filename, env, target, *args):
+ self.filename = filename
+ self.env = env
+ self.target = target
+
+ if len(args) > 0:
+ self.arg = args[0]
+
+ return self.deps
+
+ def test(self, scanner, env, filename, deps, *args):
+ self.deps = deps
+ path = scanner.path(env)
+ scanned = scanner(filename, env, path)
+ scanned_strs = map(lambda x: str(x), scanned)
+
+ self.failUnless(self.filename == filename, "the filename was passed incorrectly")
+ self.failUnless(self.env == env, "the environment was passed incorrectly")
+ self.failUnless(scanned_strs == deps, "the dependencies were returned incorrectly")
+ for d in scanned:
+ self.failUnless(type(d) != type(""), "got a string in the dependencies")
+
+ if len(args) > 0:
+ self.failUnless(self.arg == args[0], "the argument was passed incorrectly")
+ else:
+ self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
+
+ def test___call__dict(self):
+ """Test calling Scanner.Base objects with a dictionary"""
+ called = []
+ def s1func(node, env, path, called=called):
+ called.append('s1func')
+ called.append(node)
+ return []
+ def s2func(node, env, path, called=called):
+ called.append('s2func')
+ called.append(node)
+ return []
+ s1 = SCons.Scanner.Base(s1func)
+ s2 = SCons.Scanner.Base(s2func)
+ selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2})
+ nx = self.skey_node('.x')
+ env = DummyEnvironment()
+ selector(nx, env, [])
+ assert called == ['s1func', nx], called
+ del called[:]
+ ny = self.skey_node('.y')
+ selector(ny, env, [])
+ assert called == ['s2func', ny], called
+
+ def test_path(self):
+ """Test the Scanner.Base path() method"""
+ def pf(env, cwd, target, source, argument=None):
+ return "pf: %s %s %s %s %s" % \
+ (env.VARIABLE, cwd, target[0], source[0], argument)
+
+ env = DummyEnvironment()
+ env.VARIABLE = 'v1'
+ target = DummyNode('target')
+ source = DummyNode('source')
+
+ s = SCons.Scanner.Base(self.func, path_function=pf)
+ p = s.path(env, 'here', [target], [source])
+ assert p == "pf: v1 here target source None", p
+
+ s = SCons.Scanner.Base(self.func, path_function=pf, argument="xyz")
+ p = s.path(env, 'here', [target], [source])
+ assert p == "pf: v1 here target source xyz", p
+
+ def test_positional(self):
+ """Test the Scanner.Base class using positional arguments"""
+ s = SCons.Scanner.Base(self.func, "Pos")
+ env = DummyEnvironment()
+ env.VARIABLE = "var1"
+ self.test(s, env, DummyNode('f1.cpp'), ['f1.h', 'f1.hpp'])
+
+ env = DummyEnvironment()
+ env.VARIABLE = "i1"
+ self.test(s, env, DummyNode('i1.cpp'), ['i1.h', 'i1.hpp'])
+
+ def test_keywords(self):
+ """Test the Scanner.Base class using keyword arguments"""
+ s = SCons.Scanner.Base(function = self.func, name = "Key")
+ env = DummyEnvironment()
+ env.VARIABLE = "var2"
+ self.test(s, env, DummyNode('f2.cpp'), ['f2.h', 'f2.hpp'])
+
+ env = DummyEnvironment()
+ env.VARIABLE = "i2"
+
+ self.test(s, env, DummyNode('i2.cpp'), ['i2.h', 'i2.hpp'])
+
+ def test_pos_opt(self):
+ """Test the Scanner.Base class using both position and optional arguments"""
+ arg = "this is the argument"
+ s = SCons.Scanner.Base(self.func, "PosArg", arg)
+ env = DummyEnvironment()
+ env.VARIABLE = "var3"
+ self.test(s, env, DummyNode('f3.cpp'), ['f3.h', 'f3.hpp'], arg)
+
+ env = DummyEnvironment()
+ env.VARIABLE = "i3"
+ self.test(s, env, DummyNode('i3.cpp'), ['i3.h', 'i3.hpp'], arg)
+
+ def test_key_opt(self):
+ """Test the Scanner.Base class using both keyword and optional arguments"""
+ arg = "this is another argument"
+ s = SCons.Scanner.Base(function = self.func, name = "KeyArg",
+ argument = arg)
+ env = DummyEnvironment()
+ env.VARIABLE = "var4"
+ self.test(s, env, DummyNode('f4.cpp'), ['f4.h', 'f4.hpp'], arg)
+
+ env = DummyEnvironment()
+ env.VARIABLE = "i4"
+ self.test(s, env, DummyNode('i4.cpp'), ['i4.h', 'i4.hpp'], arg)
+
+ def test___cmp__(self):
+ """Test the Scanner.Base class __cmp__() method"""
+ s = SCons.Scanner.Base(self.func, "Cmp")
+ assert cmp(s, None)
+
+ def test_hash(self):
+ """Test the Scanner.Base class __hash__() method"""
+ s = SCons.Scanner.Base(self.func, "Hash")
+ dict = {}
+ dict[s] = 777
+ i = hash(id(s))
+ h = hash(dict.keys()[0])
+ self.failUnless(h == i,
+ "hash Scanner base class expected %s, got %s" % (i, h))
+
+ def test_scan_check(self):
+ """Test the Scanner.Base class scan_check() method"""
+ def my_scan(filename, env, target, *args):
+ return []
+ def check(node, env, s=self):
+ s.checked[str(node)] = 1
+ return 1
+ env = DummyEnvironment()
+ s = SCons.Scanner.Base(my_scan, "Check", scan_check = check)
+ self.checked = {}
+ path = s.path(env)
+ scanned = s(DummyNode('x'), env, path)
+ self.failUnless(self.checked['x'] == 1,
+ "did not call check function")
+
+ def test_recursive(self):
+ """Test the Scanner.Base class recursive flag"""
+ nodes = [1, 2, 3, 4]
+
+ s = SCons.Scanner.Base(function = self.func)
+ n = s.recurse_nodes(nodes)
+ self.failUnless(n == [],
+ "default behavior returned nodes: %s" % n)
+
+ s = SCons.Scanner.Base(function = self.func, recursive = None)
+ n = s.recurse_nodes(nodes)
+ self.failUnless(n == [],
+ "recursive = None returned nodes: %s" % n)
+
+ s = SCons.Scanner.Base(function = self.func, recursive = 1)
+ n = s.recurse_nodes(nodes)
+ self.failUnless(n == n,
+ "recursive = 1 didn't return all nodes: %s" % n)
+
+ def odd_only(nodes):
+ return filter(lambda n: n % 2, nodes)
+ s = SCons.Scanner.Base(function = self.func, recursive = odd_only)
+ n = s.recurse_nodes(nodes)
+ self.failUnless(n == [1, 3],
+ "recursive = 1 didn't return all nodes: %s" % n)
+
+ def test_get_skeys(self):
+ """Test the Scanner.Base get_skeys() method"""
+ s = SCons.Scanner.Base(function = self.func)
+ sk = s.get_skeys()
+ self.failUnless(sk == [],
+ "did not initialize to expected []")
+
+ s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2'])
+ sk = s.get_skeys()
+ self.failUnless(sk == ['.1', '.2'],
+ "sk was %s, not ['.1', '.2']")
+
+ s = SCons.Scanner.Base(function = self.func, skeys = '$LIST')
+ env = DummyEnvironment(LIST = ['.3', '.4'])
+ sk = s.get_skeys(env)
+ self.failUnless(sk == ['.3', '.4'],
+ "sk was %s, not ['.3', '.4']")
+
+ def test_select(self):
+ """Test the Scanner.Base select() method"""
+ scanner = SCons.Scanner.Base(function = self.func)
+ s = scanner.select('.x')
+ assert s is scanner, s
+
+ selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.x'))
+ assert s == 1, s
+ s = selector.select(self.skey_node('.y'))
+ assert s == 2, s
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+
+ def test_add_scanner(self):
+ """Test the Scanner.Base add_scanner() method"""
+ selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+ selector.add_scanner('.z', 3)
+ s = selector.select(self.skey_node('.z'))
+ assert s == 3, s
+
+ def test___str__(self):
+ """Test the Scanner.Base __str__() method"""
+ scanner = SCons.Scanner.Base(function = self.func)
+ s = str(scanner)
+ assert s == 'NONE', s
+ scanner = SCons.Scanner.Base(function = self.func, name = 'xyzzy')
+ s = str(scanner)
+ assert s == 'xyzzy', s
+
+class SelectorTestCase(unittest.TestCase):
+ class skey_node:
+ def __init__(self, key):
+ self.key = key
+ def scanner_key(self):
+ return self.key
+ def rexists(self):
+ return 1
+
+ def test___init__(self):
+ """Test creation of Scanner.Selector object"""
+ s = SCons.Scanner.Selector({})
+ assert isinstance(s, SCons.Scanner.Selector), s
+ assert s.dict == {}, s.dict
+
+ def test___call__(self):
+ """Test calling Scanner.Selector objects"""
+ called = []
+ def s1func(node, env, path, called=called):
+ called.append('s1func')
+ called.append(node)
+ return []
+ def s2func(node, env, path, called=called):
+ called.append('s2func')
+ called.append(node)
+ return []
+ s1 = SCons.Scanner.Base(s1func)
+ s2 = SCons.Scanner.Base(s2func)
+ selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2})
+ nx = self.skey_node('.x')
+ env = DummyEnvironment()
+ selector(nx, env, [])
+ assert called == ['s1func', nx], called
+ del called[:]
+ ny = self.skey_node('.y')
+ selector(ny, env, [])
+ assert called == ['s2func', ny], called
+
+ def test_select(self):
+ """Test the Scanner.Selector select() method"""
+ selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.x'))
+ assert s == 1, s
+ s = selector.select(self.skey_node('.y'))
+ assert s == 2, s
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+
+ def test_add_scanner(self):
+ """Test the Scanner.Selector add_scanner() method"""
+ selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+ selector.add_scanner('.z', 3)
+ s = selector.select(self.skey_node('.z'))
+ assert s == 3, s
+
+class CurrentTestCase(unittest.TestCase):
+ def test_class(self):
+ """Test the Scanner.Current class"""
+ class MyNode:
+ def __init__(self):
+ self.called_has_builder = None
+ self.called_is_up_to_date = None
+ self.func_called = None
+ def rexists(self):
+ return 1
+ class HasNoBuilder(MyNode):
+ def has_builder(self):
+ self.called_has_builder = 1
+ return None
+ class IsNotCurrent(MyNode):
+ def has_builder(self):
+ self.called_has_builder = 1
+ return 1
+ def is_up_to_date(self):
+ self.called_is_up_to_date = 1
+ return None
+ class IsCurrent(MyNode):
+ def has_builder(self):
+ self.called_has_builder = 1
+ return 1
+ def is_up_to_date(self):
+ self.called_is_up_to_date = 1
+ return 1
+ def func(node, env, path):
+ node.func_called = 1
+ return []
+ env = DummyEnvironment()
+ s = SCons.Scanner.Current(func)
+ path = s.path(env)
+ hnb = HasNoBuilder()
+ s(hnb, env, path)
+ self.failUnless(hnb.called_has_builder, "did not call has_builder()")
+ self.failUnless(not hnb.called_is_up_to_date, "did call is_up_to_date()")
+ self.failUnless(hnb.func_called, "did not call func()")
+ inc = IsNotCurrent()
+ s(inc, env, path)
+ self.failUnless(inc.called_has_builder, "did not call has_builder()")
+ self.failUnless(inc.called_is_up_to_date, "did not call is_up_to_date()")
+ self.failUnless(not inc.func_called, "did call func()")
+ ic = IsCurrent()
+ s(ic, env, path)
+ self.failUnless(ic.called_has_builder, "did not call has_builder()")
+ self.failUnless(ic.called_is_up_to_date, "did not call is_up_to_date()")
+ self.failUnless(ic.func_called, "did not call func()")
+
+class ClassicTestCase(unittest.TestCase):
+ def test_find_include(self):
+ """Test the Scanner.Classic find_include() method"""
+ env = DummyEnvironment()
+ s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
+
+ def _find_file(filename, paths):
+ return paths[0]+'/'+filename
+
+ save = SCons.Node.FS.find_file
+ SCons.Node.FS.find_file = _find_file
+
+ try:
+ n, i = s.find_include('aaa', 'foo', ('path',))
+ assert n == 'foo/aaa', n
+ assert i == 'aaa', i
+
+ finally:
+ SCons.Node.FS.find_file = save
+
+ def test_name(self):
+ """Test setting the Scanner.Classic name"""
+ s = SCons.Scanner.Classic("my_name", ['.s'], 'MYPATH', '^my_inc (\S+)')
+ assert s.name == "my_name", s.name
+
+ def test_scan(self):
+ """Test the Scanner.Classic scan() method"""
+ class MyNode:
+ def __init__(self, name):
+ self.name = name
+ self._rfile = self
+ self.includes = None
+ def rfile(self):
+ return self._rfile
+ def exists(self):
+ return self._exists
+ def get_contents(self):
+ return self._contents
+ def get_text_contents(self):
+ return self._contents
+ def get_dir(self):
+ return self._dir
+
+ class MyScanner(SCons.Scanner.Classic):
+ def find_include(self, include, source_dir, path):
+ return include, include
+
+ env = DummyEnvironment()
+ s = MyScanner("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
+
+ # This set of tests is intended to test the scanning operation
+ # of the Classic scanner.
+
+ # Note that caching has been added for not just the includes
+ # but the entire scan call. The caching is based on the
+ # arguments, so we will fiddle with the path parameter to
+ # defeat this caching for the purposes of these tests.
+
+ # If the node doesn't exist, scanning turns up nothing.
+ n1 = MyNode("n1")
+ n1._exists = None
+ ret = s.function(n1, env)
+ assert ret == [], ret
+
+ # Verify that it finds includes from the contents.
+ n = MyNode("n")
+ n._exists = 1
+ n._dir = MyNode("n._dir")
+ n._contents = 'my_inc abc\n'
+ ret = s.function(n, env, ('foo',))
+ assert ret == ['abc'], ret
+
+ # Verify that it uses the cached include info.
+ n._contents = 'my_inc def\n'
+ ret = s.function(n, env, ('foo2',))
+ assert ret == ['abc'], ret
+
+ # Verify that if we wipe the cache, it uses the new contents.
+ n.includes = None
+ ret = s.function(n, env, ('foo3',))
+ assert ret == ['def'], ret
+
+ # We no longer cache overall scan results, which would be returned
+ # if individual results are de-cached. If we ever restore that
+ # functionality, this test goes back here.
+ #ret = s.function(n, env, ('foo2',))
+ #assert ret == ['abc'], 'caching inactive; got: %s'%ret
+
+ # Verify that it sorts what it finds.
+ n.includes = ['xyz', 'uvw']
+ ret = s.function(n, env, ('foo4',))
+ assert ret == ['uvw', 'xyz'], ret
+
+ # Verify that we use the rfile() node.
+ nr = MyNode("nr")
+ nr._exists = 1
+ nr._dir = MyNode("nr._dir")
+ nr.includes = ['jkl', 'mno']
+ n._rfile = nr
+ ret = s.function(n, env, ('foo5',))
+ assert ret == ['jkl', 'mno'], ret
+
+
+
+class ClassicCPPTestCase(unittest.TestCase):
+ def test_find_include(self):
+ """Test the Scanner.ClassicCPP find_include() method"""
+ env = DummyEnvironment()
+ s = SCons.Scanner.ClassicCPP("Test", [], None, "")
+
+ def _find_file(filename, paths):
+ return paths[0]+'/'+filename
+
+ save = SCons.Node.FS.find_file
+ SCons.Node.FS.find_file = _find_file
+
+ try:
+ n, i = s.find_include(('"', 'aaa'), 'foo', ('path',))
+ assert n == 'foo/aaa', n
+ assert i == 'aaa', i
+
+ n, i = s.find_include(('<', 'bbb'), 'foo', ('path',))
+ assert n == 'path/bbb', n
+ assert i == 'bbb', i
+
+ # TODO(1.5): remove when 2.2 is minimal; replace ccc
+ # variable in find_include() call below with in-line u'ccc'.
+ try:
+ ccc = eval("u'ccc'")
+ except SyntaxError:
+ ccc = 'ccc'
+
+ n, i = s.find_include(('<', ccc), 'foo', ('path',))
+ assert n == 'path/ccc', n
+ assert i == 'ccc', i
+
+ finally:
+ SCons.Node.FS.find_file = save
+
+def suite():
+ suite = unittest.TestSuite()
+ tclasses = [
+ FindPathDirsTestCase,
+ ScannerTestCase,
+ BaseTestCase,
+ SelectorTestCase,
+ CurrentTestCase,
+ ClassicTestCase,
+ ClassicCPPTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py
new file mode 100644
index 0000000..76cd536
--- /dev/null
+++ b/src/engine/SCons/Scanner/__init__.py
@@ -0,0 +1,415 @@
+"""SCons.Scanner
+
+The Scanner package for the SCons software construction utility.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Scanner/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import re
+import string
+
+import SCons.Node.FS
+import SCons.Util
+
+
+class _Null:
+ pass
+
+# This is used instead of None as a default argument value so None can be
+# used as an actual argument value.
+_null = _Null
+
+def Scanner(function, *args, **kw):
+ """
+ Public interface factory function for creating different types
+ of Scanners based on the different types of "functions" that may
+ be supplied.
+
+ TODO: Deprecate this some day. We've moved the functionality
+ inside the Base class and really don't need this factory function
+ any more. It was, however, used by some of our Tool modules, so
+ the call probably ended up in various people's custom modules
+ patterned on SCons code.
+ """
+ if SCons.Util.is_Dict(function):
+ return apply(Selector, (function,) + args, kw)
+ else:
+ return apply(Base, (function,) + args, kw)
+
+
+
+class FindPathDirs:
+ """A class to bind a specific *PATH variable name to a function that
+ will return all of the *path directories."""
+ def __init__(self, variable):
+ self.variable = variable
+ def __call__(self, env, dir=None, target=None, source=None, argument=None):
+ import SCons.PathList
+ try:
+ path = env[self.variable]
+ except KeyError:
+ return ()
+
+ dir = dir or env.fs._cwd
+ path = SCons.PathList.PathList(path).subst_path(env, target, source)
+ return tuple(dir.Rfindalldirs(path))
+
+
+
+class Base:
+ """
+ The base class for dependency scanners. This implements
+ straightforward, single-pass scanning of a single file.
+ """
+
+ def __init__(self,
+ function,
+ name = "NONE",
+ argument = _null,
+ skeys = _null,
+ path_function = None,
+ node_class = SCons.Node.FS.Entry,
+ node_factory = None,
+ scan_check = None,
+ recursive = None):
+ """
+ Construct a new scanner object given a scanner function.
+
+ 'function' - a scanner function taking two or three
+ arguments and returning a list of strings.
+
+ 'name' - a name for identifying this scanner object.
+
+ 'argument' - an optional argument that, if specified, will be
+ passed to both the scanner function and the path_function.
+
+ 'skeys' - an optional list argument that can be used to determine
+ which scanner should be used for a given Node. In the case of File
+ nodes, for example, the 'skeys' would be file suffixes.
+
+ 'path_function' - a function that takes four or five arguments
+ (a construction environment, Node for the directory containing
+ the SConscript file that defined the primary target, list of
+ target nodes, list of source nodes, and optional argument for
+ this instance) and returns a tuple of the directories that can
+ be searched for implicit dependency files. May also return a
+ callable() which is called with no args and returns the tuple
+ (supporting Bindable class).
+
+ 'node_class' - the class of Nodes which this scan will return.
+ If node_class is None, then this scanner will not enforce any
+ Node conversion and will return the raw results from the
+ underlying scanner function.
+
+ 'node_factory' - the factory function to be called to translate
+ the raw results returned by the scanner function into the
+ expected node_class objects.
+
+ 'scan_check' - a function to be called to first check whether
+ this node really needs to be scanned.
+
+ 'recursive' - specifies that this scanner should be invoked
+ recursively on all of the implicit dependencies it returns
+ (the canonical example being #include lines in C source files).
+ May be a callable, which will be called to filter the list
+ of nodes found to select a subset for recursive scanning
+ (the canonical example being only recursively scanning
+ subdirectories within a directory).
+
+ The scanner function's first argument will be a Node that should
+ be scanned for dependencies, the second argument will be an
+ Environment object, the third argument will be the tuple of paths
+ returned by the path_function, and the fourth argument will be
+ the value passed into 'argument', and the returned list should
+ contain the Nodes for all the direct dependencies of the file.
+
+ Examples:
+
+ s = Scanner(my_scanner_function)
+
+ s = Scanner(function = my_scanner_function)
+
+ s = Scanner(function = my_scanner_function, argument = 'foo')
+
+ """
+
+ # Note: this class could easily work with scanner functions that take
+ # something other than a filename as an argument (e.g. a database
+ # node) and a dependencies list that aren't file names. All that
+ # would need to be changed is the documentation.
+
+ self.function = function
+ self.path_function = path_function
+ self.name = name
+ self.argument = argument
+
+ if skeys is _null:
+ if SCons.Util.is_Dict(function):
+ skeys = function.keys()
+ else:
+ skeys = []
+ self.skeys = skeys
+
+ self.node_class = node_class
+ self.node_factory = node_factory
+ self.scan_check = scan_check
+ if callable(recursive):
+ self.recurse_nodes = recursive
+ elif recursive:
+ self.recurse_nodes = self._recurse_all_nodes
+ else:
+ self.recurse_nodes = self._recurse_no_nodes
+
+ def path(self, env, dir=None, target=None, source=None):
+ if not self.path_function:
+ return ()
+ if not self.argument is _null:
+ return self.path_function(env, dir, target, source, self.argument)
+ else:
+ return self.path_function(env, dir, target, source)
+
+ def __call__(self, node, env, path = ()):
+ """
+ This method scans a single object. 'node' is the node
+ that will be passed to the scanner function, and 'env' is the
+ environment that will be passed to the scanner function. A list of
+ direct dependency nodes for the specified node will be returned.
+ """
+ if self.scan_check and not self.scan_check(node, env):
+ return []
+
+ self = self.select(node)
+
+ if not self.argument is _null:
+ list = self.function(node, env, path, self.argument)
+ else:
+ list = self.function(node, env, path)
+
+ kw = {}
+ if hasattr(node, 'dir'):
+ kw['directory'] = node.dir
+ node_factory = env.get_factory(self.node_factory)
+ nodes = []
+ for l in list:
+ if self.node_class and not isinstance(l, self.node_class):
+ l = apply(node_factory, (l,), kw)
+ nodes.append(l)
+ return nodes
+
+ def __cmp__(self, other):
+ try:
+ return cmp(self.__dict__, other.__dict__)
+ except AttributeError:
+ # other probably doesn't have a __dict__
+ return cmp(self.__dict__, other)
+
+ def __hash__(self):
+ return id(self)
+
+ def __str__(self):
+ return self.name
+
+ def add_skey(self, skey):
+ """Add a skey to the list of skeys"""
+ self.skeys.append(skey)
+
+ def get_skeys(self, env=None):
+ if env and SCons.Util.is_String(self.skeys):
+ return env.subst_list(self.skeys)[0]
+ return self.skeys
+
+ def select(self, node):
+ if SCons.Util.is_Dict(self.function):
+ key = node.scanner_key()
+ try:
+ return self.function[key]
+ except KeyError:
+ return None
+ else:
+ return self
+
+ def _recurse_all_nodes(self, nodes):
+ return nodes
+
+ def _recurse_no_nodes(self, nodes):
+ return []
+
+ recurse_nodes = _recurse_no_nodes
+
+ def add_scanner(self, skey, scanner):
+ self.function[skey] = scanner
+ self.add_skey(skey)
+
+
+class Selector(Base):
+ """
+ A class for selecting a more specific scanner based on the
+ scanner_key() (suffix) for a specific Node.
+
+ TODO: This functionality has been moved into the inner workings of
+ the Base class, and this class will be deprecated at some point.
+ (It was never exposed directly as part of the public interface,
+ although it is used by the Scanner() factory function that was
+ used by various Tool modules and therefore was likely a template
+ for custom modules that may be out there.)
+ """
+ def __init__(self, dict, *args, **kw):
+ apply(Base.__init__, (self, None,)+args, kw)
+ self.dict = dict
+ self.skeys = dict.keys()
+
+ def __call__(self, node, env, path = ()):
+ return self.select(node)(node, env, path)
+
+ def select(self, node):
+ try:
+ return self.dict[node.scanner_key()]
+ except KeyError:
+ return None
+
+ def add_scanner(self, skey, scanner):
+ self.dict[skey] = scanner
+ self.add_skey(skey)
+
+
+class Current(Base):
+ """
+ A class for scanning files that are source files (have no builder)
+ or are derived files and are current (which implies that they exist,
+ either locally or in a repository).
+ """
+
+ def __init__(self, *args, **kw):
+ def current_check(node, env):
+ return not node.has_builder() or node.is_up_to_date()
+ kw['scan_check'] = current_check
+ apply(Base.__init__, (self,) + args, kw)
+
+class Classic(Current):
+ """
+ A Scanner subclass to contain the common logic for classic CPP-style
+ include scanning, but which can be customized to use different
+ regular expressions to find the includes.
+
+ Note that in order for this to work "out of the box" (without
+ overriding the find_include() and sort_key() methods), the regular
+ expression passed to the constructor must return the name of the
+ include file in group 0.
+ """
+
+ def __init__(self, name, suffixes, path_variable, regex, *args, **kw):
+
+ self.cre = re.compile(regex, re.M)
+
+ def _scan(node, env, path=(), self=self):
+ node = node.rfile()
+ if not node.exists():
+ return []
+ return self.scan(node, path)
+
+ kw['function'] = _scan
+ kw['path_function'] = FindPathDirs(path_variable)
+ kw['recursive'] = 1
+ kw['skeys'] = suffixes
+ kw['name'] = name
+
+ apply(Current.__init__, (self,) + args, kw)
+
+ def find_include(self, include, source_dir, path):
+ n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path))
+ return n, include
+
+ def sort_key(self, include):
+ return SCons.Node.FS._my_normcase(include)
+
+ def find_include_names(self, node):
+ return self.cre.findall(node.get_text_contents())
+
+ def scan(self, node, path=()):
+
+ # cache the includes list in node so we only scan it once:
+ if node.includes is not None:
+ includes = node.includes
+ else:
+ includes = self.find_include_names (node)
+ # Intern the names of the include files. Saves some memory
+ # if the same header is included many times.
+ node.includes = map(SCons.Util.silent_intern, includes)
+
+ # This is a hand-coded DSU (decorate-sort-undecorate, or
+ # Schwartzian transform) pattern. The sort key is the raw name
+ # of the file as specifed on the #include line (including the
+ # " or <, since that may affect what file is found), which lets
+ # us keep the sort order constant regardless of whether the file
+ # is actually found in a Repository or locally.
+ nodes = []
+ source_dir = node.get_dir()
+ if callable(path):
+ path = path()
+ for include in includes:
+ n, i = self.find_include(include, source_dir, path)
+
+ if n is None:
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
+ else:
+ sortkey = self.sort_key(include)
+ nodes.append((sortkey, n))
+
+ nodes.sort()
+ nodes = map(lambda pair: pair[1], nodes)
+ return nodes
+
+class ClassicCPP(Classic):
+ """
+ A Classic Scanner subclass which takes into account the type of
+ bracketing used to include the file, and uses classic CPP rules
+ for searching for the files based on the bracketing.
+
+ Note that in order for this to work, the regular expression passed
+ to the constructor must return the leading bracket in group 0, and
+ the contained filename in group 1.
+ """
+ def find_include(self, include, source_dir, path):
+ if include[0] == '"':
+ paths = (source_dir,) + tuple(path)
+ else:
+ paths = tuple(path) + (source_dir,)
+
+ n = SCons.Node.FS.find_file(include[1], paths)
+
+ i = SCons.Util.silent_intern(include[1])
+ return n, i
+
+ def sort_key(self, include):
+ return SCons.Node.FS._my_normcase(string.join(include))
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/Interactive.py b/src/engine/SCons/Script/Interactive.py
new file mode 100644
index 0000000..26711d8
--- /dev/null
+++ b/src/engine/SCons/Script/Interactive.py
@@ -0,0 +1,386 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/Interactive.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+SCons interactive mode
+"""
+
+# TODO:
+#
+# This has the potential to grow into something with a really big life
+# of its own, which might or might not be a good thing. Nevertheless,
+# here are some enhancements that will probably be requested some day
+# and are worth keeping in mind (assuming this takes off):
+#
+# - A command to re-read / re-load the SConscript files. This may
+# involve allowing people to specify command-line options (e.g. -f,
+# -I, --no-site-dir) that affect how the SConscript files are read.
+#
+# - Additional command-line options on the "build" command.
+#
+# Of the supported options that seemed to make sense (after a quick
+# pass through the list), the ones that seemed likely enough to be
+# used are listed in the man page and have explicit test scripts.
+#
+# These had code changed in Script/Main.py to support them, but didn't
+# seem likely to be used regularly, so had no test scripts added:
+#
+# build --diskcheck=*
+# build --implicit-cache=*
+# build --implicit-deps-changed=*
+# build --implicit-deps-unchanged=*
+#
+# These look like they should "just work" with no changes to the
+# existing code, but like those above, look unlikely to be used and
+# therefore had no test scripts added:
+#
+# build --random
+#
+# These I'm not sure about. They might be useful for individual
+# "build" commands, and may even work, but they seem unlikely enough
+# that we'll wait until they're requested before spending any time on
+# writing test scripts for them, or investigating whether they work.
+#
+# build -q [??? is there a useful analog to the exit status?]
+# build --duplicate=
+# build --profile=
+# build --max-drift=
+# build --warn=*
+# build --Y
+#
+# - Most of the SCons command-line options that the "build" command
+# supports should be settable as default options that apply to all
+# subsequent "build" commands. Maybe a "set {option}" command that
+# maps to "SetOption('{option}')".
+#
+# - Need something in the 'help' command that prints the -h output.
+#
+# - A command to run the configure subsystem separately (must see how
+# this interacts with the new automake model).
+#
+# - Command-line completion of target names; maybe even of SCons options?
+# Completion is something that's supported by the Python cmd module,
+# so this should be doable without too much trouble.
+#
+
+import cmd
+import copy
+import os
+import re
+import shlex
+import string
+import sys
+
+try:
+ import readline
+except ImportError:
+ pass
+
+class SConsInteractiveCmd(cmd.Cmd):
+ """\
+ build [TARGETS] Build the specified TARGETS and their dependencies.
+ 'b' is a synonym.
+ clean [TARGETS] Clean (remove) the specified TARGETS and their
+ dependencies. 'c' is a synonym.
+ exit Exit SCons interactive mode.
+ help [COMMAND] Prints help for the specified COMMAND. 'h' and
+ '?' are synonyms.
+ shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!'
+ are synonyms.
+ version Prints SCons version information.
+ """
+
+ synonyms = {
+ 'b' : 'build',
+ 'c' : 'clean',
+ 'h' : 'help',
+ 'scons' : 'build',
+ 'sh' : 'shell',
+ }
+
+ def __init__(self, **kw):
+ cmd.Cmd.__init__(self)
+ for key, val in kw.items():
+ setattr(self, key, val)
+
+ if sys.platform == 'win32':
+ self.shell_variable = 'COMSPEC'
+ else:
+ self.shell_variable = 'SHELL'
+
+ def default(self, argv):
+ print "*** Unknown command: %s" % argv[0]
+
+ def onecmd(self, line):
+ line = string.strip(line)
+ if not line:
+ print self.lastcmd
+ return self.emptyline()
+ self.lastcmd = line
+ if line[0] == '!':
+ line = 'shell ' + line[1:]
+ elif line[0] == '?':
+ line = 'help ' + line[1:]
+ if os.sep == '\\':
+ line = string.replace(line, '\\', '\\\\')
+ argv = shlex.split(line)
+ argv[0] = self.synonyms.get(argv[0], argv[0])
+ if not argv[0]:
+ return self.default(line)
+ else:
+ try:
+ func = getattr(self, 'do_' + argv[0])
+ except AttributeError:
+ return self.default(argv)
+ return func(argv)
+
+ def do_build(self, argv):
+ """\
+ build [TARGETS] Build the specified TARGETS and their
+ dependencies. 'b' is a synonym.
+ """
+ import SCons.Node
+ import SCons.SConsign
+ import SCons.Script.Main
+
+ options = copy.deepcopy(self.options)
+
+ options, targets = self.parser.parse_args(argv[1:], values=options)
+
+ SCons.Script.COMMAND_LINE_TARGETS = targets
+
+ if targets:
+ SCons.Script.BUILD_TARGETS = targets
+ else:
+ # If the user didn't specify any targets on the command line,
+ # use the list of default targets.
+ SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default
+
+ nodes = SCons.Script.Main._build_targets(self.fs,
+ options,
+ targets,
+ self.target_top)
+
+ if not nodes:
+ return
+
+ # Call each of the Node's alter_targets() methods, which may
+ # provide additional targets that ended up as part of the build
+ # (the canonical example being a VariantDir() when we're building
+ # from a source directory) and which we therefore need their
+ # state cleared, too.
+ x = []
+ for n in nodes:
+ x.extend(n.alter_targets()[0])
+ nodes.extend(x)
+
+ # Clean up so that we can perform the next build correctly.
+ #
+ # We do this by walking over all the children of the targets,
+ # and clearing their state.
+ #
+ # We currently have to re-scan each node to find their
+ # children, because built nodes have already been partially
+ # cleared and don't remember their children. (In scons
+ # 0.96.1 and earlier, this wasn't the case, and we didn't
+ # have to re-scan the nodes.)
+ #
+ # Because we have to re-scan each node, we can't clear the
+ # nodes as we walk over them, because we may end up rescanning
+ # a cleared node as we scan a later node. Therefore, only
+ # store the list of nodes that need to be cleared as we walk
+ # the tree, and clear them in a separate pass.
+ #
+ # XXX: Someone more familiar with the inner workings of scons
+ # may be able to point out a more efficient way to do this.
+
+ SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
+
+ seen_nodes = {}
+
+ def get_unseen_children(node, parent, seen_nodes=seen_nodes):
+ def is_unseen(node, seen_nodes=seen_nodes):
+ return not seen_nodes.has_key(node)
+ return filter(is_unseen, node.children(scan=1))
+
+ def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
+ seen_nodes[node] = 1
+
+ # If this file is in a VariantDir and has a
+ # corresponding source file in the source tree, remember the
+ # node in the source tree, too. This is needed in
+ # particular to clear cached implicit dependencies on the
+ # source file, since the scanner will scan it if the
+ # VariantDir was created with duplicate=0.
+ try:
+ rfile_method = node.rfile
+ except AttributeError:
+ return
+ else:
+ rfile = rfile_method()
+ if rfile != node:
+ seen_nodes[rfile] = 1
+
+ for node in nodes:
+ walker = SCons.Node.Walker(node,
+ kids_func=get_unseen_children,
+ eval_func=add_to_seen_nodes)
+ n = walker.next()
+ while n:
+ n = walker.next()
+
+ for node in seen_nodes.keys():
+ # Call node.clear() to clear most of the state
+ node.clear()
+ # node.clear() doesn't reset node.state, so call
+ # node.set_state() to reset it manually
+ node.set_state(SCons.Node.no_state)
+ node.implicit = None
+
+ # Debug: Uncomment to verify that all Taskmaster reference
+ # counts have been reset to zero.
+ #if node.ref_count != 0:
+ # from SCons.Debug import Trace
+ # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
+
+ SCons.SConsign.Reset()
+ SCons.Script.Main.progress_display("scons: done clearing node information.")
+
+ def do_clean(self, argv):
+ """\
+ clean [TARGETS] Clean (remove) the specified TARGETS
+ and their dependencies. 'c' is a synonym.
+ """
+ return self.do_build(['build', '--clean'] + argv[1:])
+
+ def do_EOF(self, argv):
+ print
+ self.do_exit(argv)
+
+ def _do_one_help(self, arg):
+ try:
+ # If help_<arg>() exists, then call it.
+ func = getattr(self, 'help_' + arg)
+ except AttributeError:
+ try:
+ func = getattr(self, 'do_' + arg)
+ except AttributeError:
+ doc = None
+ else:
+ doc = self._doc_to_help(func)
+ if doc:
+ sys.stdout.write(doc + '\n')
+ sys.stdout.flush()
+ else:
+ doc = self.strip_initial_spaces(func())
+ if doc:
+ sys.stdout.write(doc + '\n')
+ sys.stdout.flush()
+
+ def _doc_to_help(self, obj):
+ doc = obj.__doc__
+ if doc is None:
+ return ''
+ return self._strip_initial_spaces(doc)
+
+ def _strip_initial_spaces(self, s):
+ #lines = s.split('\n')
+ lines = string.split(s, '\n')
+ spaces = re.match(' *', lines[0]).group(0)
+ #def strip_spaces(l):
+ # if l.startswith(spaces):
+ # l = l[len(spaces):]
+ # return l
+ #return '\n'.join([ strip_spaces(l) for l in lines ])
+ def strip_spaces(l, spaces=spaces):
+ if l[:len(spaces)] == spaces:
+ l = l[len(spaces):]
+ return l
+ lines = map(strip_spaces, lines)
+ return string.join(lines, '\n')
+
+ def do_exit(self, argv):
+ """\
+ exit Exit SCons interactive mode.
+ """
+ sys.exit(0)
+
+ def do_help(self, argv):
+ """\
+ help [COMMAND] Prints help for the specified COMMAND. 'h'
+ and '?' are synonyms.
+ """
+ if argv[1:]:
+ for arg in argv[1:]:
+ if self._do_one_help(arg):
+ break
+ else:
+ # If bare 'help' is called, print this class's doc
+ # string (if it has one).
+ doc = self._doc_to_help(self.__class__)
+ if doc:
+ sys.stdout.write(doc + '\n')
+ sys.stdout.flush()
+
+ def do_shell(self, argv):
+ """\
+ shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
+ '!' are synonyms.
+ """
+ import subprocess
+ argv = argv[1:]
+ if not argv:
+ argv = os.environ[self.shell_variable]
+ try:
+ # Per "[Python-Dev] subprocess insufficiently platform-independent?"
+ # http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+
+ # Doing the right thing with an argument list currently
+ # requires different shell= values on Windows and Linux.
+ p = subprocess.Popen(argv, shell=(sys.platform=='win32'))
+ except EnvironmentError, e:
+ sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror))
+ else:
+ p.wait()
+
+ def do_version(self, argv):
+ """\
+ version Prints SCons version information.
+ """
+ sys.stdout.write(self.parser.version + '\n')
+
+def interact(fs, parser, options, targets, target_top):
+ c = SConsInteractiveCmd(prompt = 'scons>>> ',
+ fs = fs,
+ parser = parser,
+ options = options,
+ targets = targets,
+ target_top = target_top)
+ c.cmdloop()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
new file mode 100644
index 0000000..c7cbae8
--- /dev/null
+++ b/src/engine/SCons/Script/Main.py
@@ -0,0 +1,1360 @@
+"""SCons.Script
+
+This file implements the main() function used by the scons script.
+
+Architecturally, this *is* the scons script, and will likely only be
+called from the external "scons" wrapper. Consequently, anything here
+should not be, or be considered, part of the build engine. If it's
+something that we expect other software to want to use, it should go in
+some other module. If it's specific to the "scons" script invocation,
+it goes here.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/Main.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+import sys
+import time
+import traceback
+
+# Strip the script directory from sys.path() so on case-insensitive
+# (Windows) systems Python doesn't think that the "scons" script is the
+# "SCons" package. Replace it with our own version directory so, if
+# if they're there, we pick up the right version of the build engine
+# modules.
+#sys.path = [os.path.join(sys.prefix,
+# 'lib',
+# 'scons-%d' % SCons.__version__)] + sys.path[1:]
+
+import SCons.CacheDir
+import SCons.Debug
+import SCons.Defaults
+import SCons.Environment
+import SCons.Errors
+import SCons.Job
+import SCons.Node
+import SCons.Node.FS
+import SCons.SConf
+import SCons.Script
+import SCons.Taskmaster
+import SCons.Util
+import SCons.Warnings
+
+import SCons.Script.Interactive
+
+def fetch_win32_parallel_msg():
+ # A subsidiary function that exists solely to isolate this import
+ # so we don't have to pull it in on all platforms, and so that an
+ # in-line "import" statement in the _main() function below doesn't
+ # cause warnings about local names shadowing use of the 'SCons'
+ # globl in nest scopes and UnboundLocalErrors and the like in some
+ # versions (2.1) of Python.
+ import SCons.Platform.win32
+ return SCons.Platform.win32.parallel_msg
+
+#
+
+class SConsPrintHelpException(Exception):
+ pass
+
+display = SCons.Util.display
+progress_display = SCons.Util.DisplayEngine()
+
+first_command_start = None
+last_command_end = None
+
+class Progressor:
+ prev = ''
+ count = 0
+ target_string = '$TARGET'
+
+ def __init__(self, obj, interval=1, file=None, overwrite=False):
+ if file is None:
+ file = sys.stdout
+
+ self.obj = obj
+ self.file = file
+ self.interval = interval
+ self.overwrite = overwrite
+
+ if callable(obj):
+ self.func = obj
+ elif SCons.Util.is_List(obj):
+ self.func = self.spinner
+ elif string.find(obj, self.target_string) != -1:
+ self.func = self.replace_string
+ else:
+ self.func = self.string
+
+ def write(self, s):
+ self.file.write(s)
+ self.file.flush()
+ self.prev = s
+
+ def erase_previous(self):
+ if self.prev:
+ length = len(self.prev)
+ if self.prev[-1] in ('\n', '\r'):
+ length = length - 1
+ self.write(' ' * length + '\r')
+ self.prev = ''
+
+ def spinner(self, node):
+ self.write(self.obj[self.count % len(self.obj)])
+
+ def string(self, node):
+ self.write(self.obj)
+
+ def replace_string(self, node):
+ self.write(string.replace(self.obj, self.target_string, str(node)))
+
+ def __call__(self, node):
+ self.count = self.count + 1
+ if (self.count % self.interval) == 0:
+ if self.overwrite:
+ self.erase_previous()
+ self.func(node)
+
+ProgressObject = SCons.Util.Null()
+
+def Progress(*args, **kw):
+ global ProgressObject
+ ProgressObject = apply(Progressor, args, kw)
+
+# Task control.
+#
+
+_BuildFailures = []
+
+def GetBuildFailures():
+ return _BuildFailures
+
+class BuildTask(SCons.Taskmaster.OutOfDateTask):
+ """An SCons build task."""
+ progress = ProgressObject
+
+ def display(self, message):
+ display('scons: ' + message)
+
+ def prepare(self):
+ self.progress(self.targets[0])
+ return SCons.Taskmaster.OutOfDateTask.prepare(self)
+
+ def needs_execute(self):
+ if SCons.Taskmaster.OutOfDateTask.needs_execute(self):
+ return True
+ if self.top and self.targets[0].has_builder():
+ display("scons: `%s' is up to date." % str(self.node))
+ return False
+
+ def execute(self):
+ if print_time:
+ start_time = time.time()
+ global first_command_start
+ if first_command_start is None:
+ first_command_start = start_time
+ SCons.Taskmaster.OutOfDateTask.execute(self)
+ if print_time:
+ global cumulative_command_time
+ global last_command_end
+ finish_time = time.time()
+ last_command_end = finish_time
+ cumulative_command_time = cumulative_command_time+finish_time-start_time
+ sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time))
+
+ def do_failed(self, status=2):
+ _BuildFailures.append(self.exception[1])
+ global exit_status
+ global this_build_status
+ if self.options.ignore_errors:
+ SCons.Taskmaster.OutOfDateTask.executed(self)
+ elif self.options.keep_going:
+ SCons.Taskmaster.OutOfDateTask.fail_continue(self)
+ exit_status = status
+ this_build_status = status
+ else:
+ SCons.Taskmaster.OutOfDateTask.fail_stop(self)
+ exit_status = status
+ this_build_status = status
+
+ def executed(self):
+ t = self.targets[0]
+ if self.top and not t.has_builder() and not t.side_effect:
+ if not t.exists():
+ def classname(obj):
+ return string.split(str(obj.__class__), '.')[-1]
+ if classname(t) in ('File', 'Dir', 'Entry'):
+ errstr="Do not know how to make %s target `%s' (%s)." % (classname(t), t, t.abspath)
+ else: # Alias or Python or ...
+ errstr="Do not know how to make %s target `%s'." % (classname(t), t)
+ sys.stderr.write("scons: *** " + errstr)
+ if not self.options.keep_going:
+ sys.stderr.write(" Stop.")
+ sys.stderr.write("\n")
+ try:
+ raise SCons.Errors.BuildError(t, errstr)
+ except KeyboardInterrupt:
+ raise
+ except:
+ self.exception_set()
+ self.do_failed()
+ else:
+ print "scons: Nothing to be done for `%s'." % t
+ SCons.Taskmaster.OutOfDateTask.executed(self)
+ else:
+ SCons.Taskmaster.OutOfDateTask.executed(self)
+
+ def failed(self):
+ # Handle the failure of a build task. The primary purpose here
+ # is to display the various types of Errors and Exceptions
+ # appropriately.
+ exc_info = self.exc_info()
+ try:
+ t, e, tb = exc_info
+ except ValueError:
+ t, e = exc_info
+ tb = None
+
+ if t is None:
+ # The Taskmaster didn't record an exception for this Task;
+ # see if the sys module has one.
+ try:
+ t, e, tb = sys.exc_info()[:]
+ except ValueError:
+ t, e = exc_info
+ tb = None
+
+ # Deprecated string exceptions will have their string stored
+ # in the first entry of the tuple.
+ if e is None:
+ e = t
+
+ buildError = SCons.Errors.convert_to_BuildError(e)
+ if not buildError.node:
+ buildError.node = self.node
+
+ node = buildError.node
+ if not SCons.Util.is_List(node):
+ node = [ node ]
+ nodename = string.join(map(str, node), ', ')
+
+ errfmt = "scons: *** [%s] %s\n"
+ sys.stderr.write(errfmt % (nodename, buildError))
+
+ if (buildError.exc_info[2] and buildError.exc_info[1] and
+ # TODO(1.5)
+ #not isinstance(
+ # buildError.exc_info[1],
+ # (EnvironmentError, SCons.Errors.StopError, SCons.Errors.UserError))):
+ not isinstance(buildError.exc_info[1], EnvironmentError) and
+ not isinstance(buildError.exc_info[1], SCons.Errors.StopError) and
+ not isinstance(buildError.exc_info[1], SCons.Errors.UserError)):
+ type, value, trace = buildError.exc_info
+ traceback.print_exception(type, value, trace)
+ elif tb and print_stacktrace:
+ sys.stderr.write("scons: internal stack trace:\n")
+ traceback.print_tb(tb, file=sys.stderr)
+
+ self.exception = (e, buildError, tb) # type, value, traceback
+ self.do_failed(buildError.exitstatus)
+
+ self.exc_clear()
+
+ def postprocess(self):
+ if self.top:
+ t = self.targets[0]
+ for tp in self.options.tree_printers:
+ tp.display(t)
+ if self.options.debug_includes:
+ tree = t.render_include_tree()
+ if tree:
+ print
+ print tree
+ SCons.Taskmaster.OutOfDateTask.postprocess(self)
+
+ def make_ready(self):
+ """Make a task ready for execution"""
+ SCons.Taskmaster.OutOfDateTask.make_ready(self)
+ if self.out_of_date and self.options.debug_explain:
+ explanation = self.out_of_date[0].explain()
+ if explanation:
+ sys.stdout.write("scons: " + explanation)
+
+class CleanTask(SCons.Taskmaster.AlwaysTask):
+ """An SCons clean task."""
+ def fs_delete(self, path, pathstr, remove=1):
+ try:
+ if os.path.lexists(path):
+ if os.path.isfile(path) or os.path.islink(path):
+ if remove: os.unlink(path)
+ display("Removed " + pathstr)
+ elif os.path.isdir(path) and not os.path.islink(path):
+ # delete everything in the dir
+ entries = os.listdir(path)
+ # Sort for deterministic output (os.listdir() Can
+ # return entries in a random order).
+ entries.sort()
+ for e in entries:
+ p = os.path.join(path, e)
+ s = os.path.join(pathstr, e)
+ if os.path.isfile(p):
+ if remove: os.unlink(p)
+ display("Removed " + s)
+ else:
+ self.fs_delete(p, s, remove)
+ # then delete dir itself
+ if remove: os.rmdir(path)
+ display("Removed directory " + pathstr)
+ else:
+ errstr = "Path '%s' exists but isn't a file or directory."
+ raise SCons.Errors.UserError(errstr % (pathstr))
+ except SCons.Errors.UserError, e:
+ print e
+ except (IOError, OSError), e:
+ print "scons: Could not remove '%s':" % pathstr, e.strerror
+
+ def show(self):
+ target = self.targets[0]
+ if (target.has_builder() or target.side_effect) and not target.noclean:
+ for t in self.targets:
+ if not t.isdir():
+ display("Removed " + str(t))
+ if SCons.Environment.CleanTargets.has_key(target):
+ files = SCons.Environment.CleanTargets[target]
+ for f in files:
+ self.fs_delete(f.abspath, str(f), 0)
+
+ def remove(self):
+ target = self.targets[0]
+ if (target.has_builder() or target.side_effect) and not target.noclean:
+ for t in self.targets:
+ try:
+ removed = t.remove()
+ except OSError, e:
+ # An OSError may indicate something like a permissions
+ # issue, an IOError would indicate something like
+ # the file not existing. In either case, print a
+ # message and keep going to try to remove as many
+ # targets aa possible.
+ print "scons: Could not remove '%s':" % str(t), e.strerror
+ else:
+ if removed:
+ display("Removed " + str(t))
+ if SCons.Environment.CleanTargets.has_key(target):
+ files = SCons.Environment.CleanTargets[target]
+ for f in files:
+ self.fs_delete(f.abspath, str(f))
+
+ execute = remove
+
+ # We want the Taskmaster to update the Node states (and therefore
+ # handle reference counts, etc.), but we don't want to call
+ # back to the Node's post-build methods, which would do things
+ # we don't want, like store .sconsign information.
+ executed = SCons.Taskmaster.Task.executed_without_callbacks
+
+ # Have the taskmaster arrange to "execute" all of the targets, because
+ # we'll figure out ourselves (in remove() or show() above) whether
+ # anything really needs to be done.
+ make_ready = SCons.Taskmaster.Task.make_ready_all
+
+ def prepare(self):
+ pass
+
+class QuestionTask(SCons.Taskmaster.AlwaysTask):
+ """An SCons task for the -q (question) option."""
+ def prepare(self):
+ pass
+
+ def execute(self):
+ if self.targets[0].get_state() != SCons.Node.up_to_date or \
+ (self.top and not self.targets[0].exists()):
+ global exit_status
+ global this_build_status
+ exit_status = 1
+ this_build_status = 1
+ self.tm.stop()
+
+ def executed(self):
+ pass
+
+
+class TreePrinter:
+ def __init__(self, derived=False, prune=False, status=False):
+ self.derived = derived
+ self.prune = prune
+ self.status = status
+ def get_all_children(self, node):
+ return node.all_children()
+ def get_derived_children(self, node):
+ children = node.all_children(None)
+ return filter(lambda x: x.has_builder(), children)
+ def display(self, t):
+ if self.derived:
+ func = self.get_derived_children
+ else:
+ func = self.get_all_children
+ s = self.status and 2 or 0
+ SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
+
+
+def python_version_string():
+ return string.split(sys.version)[0]
+
+def python_version_unsupported(version=sys.version_info):
+ return version < (1, 5, 2)
+
+def python_version_deprecated(version=sys.version_info):
+ return version < (2, 4, 0)
+
+
+# Global variables
+
+print_objects = 0
+print_memoizer = 0
+print_stacktrace = 0
+print_time = 0
+sconscript_time = 0
+cumulative_command_time = 0
+exit_status = 0 # final exit status, assume success by default
+this_build_status = 0 # "exit status" of an individual build
+num_jobs = None
+delayed_warnings = []
+
+class FakeOptionParser:
+ """
+ A do-nothing option parser, used for the initial OptionsParser variable.
+
+ During normal SCons operation, the OptionsParser is created right
+ away by the main() function. Certain tests scripts however, can
+ introspect on different Tool modules, the initialization of which
+ can try to add a new, local option to an otherwise uninitialized
+ OptionsParser object. This allows that introspection to happen
+ without blowing up.
+
+ """
+ class FakeOptionValues:
+ def __getattr__(self, attr):
+ return None
+ values = FakeOptionValues()
+ def add_local_option(self, *args, **kw):
+ pass
+
+OptionsParser = FakeOptionParser()
+
+def AddOption(*args, **kw):
+ if not kw.has_key('default'):
+ kw['default'] = None
+ result = apply(OptionsParser.add_local_option, args, kw)
+ return result
+
+def GetOption(name):
+ return getattr(OptionsParser.values, name)
+
+def SetOption(name, value):
+ return OptionsParser.values.set_option(name, value)
+
+#
+class Stats:
+ def __init__(self):
+ self.stats = []
+ self.labels = []
+ self.append = self.do_nothing
+ self.print_stats = self.do_nothing
+ def enable(self, outfp):
+ self.outfp = outfp
+ self.append = self.do_append
+ self.print_stats = self.do_print
+ def do_nothing(self, *args, **kw):
+ pass
+
+class CountStats(Stats):
+ def do_append(self, label):
+ self.labels.append(label)
+ self.stats.append(SCons.Debug.fetchLoggedInstances())
+ def do_print(self):
+ stats_table = {}
+ for s in self.stats:
+ for n in map(lambda t: t[0], s):
+ stats_table[n] = [0, 0, 0, 0]
+ i = 0
+ for s in self.stats:
+ for n, c in s:
+ stats_table[n][i] = c
+ i = i + 1
+ keys = stats_table.keys()
+ keys.sort()
+ self.outfp.write("Object counts:\n")
+ pre = [" "]
+ post = [" %s\n"]
+ l = len(self.stats)
+ fmt1 = string.join(pre + [' %7s']*l + post, '')
+ fmt2 = string.join(pre + [' %7d']*l + post, '')
+ labels = self.labels[:l]
+ labels.append(("", "Class"))
+ self.outfp.write(fmt1 % tuple(map(lambda x: x[0], labels)))
+ self.outfp.write(fmt1 % tuple(map(lambda x: x[1], labels)))
+ for k in keys:
+ r = stats_table[k][:l] + [k]
+ self.outfp.write(fmt2 % tuple(r))
+
+count_stats = CountStats()
+
+class MemStats(Stats):
+ def do_append(self, label):
+ self.labels.append(label)
+ self.stats.append(SCons.Debug.memory())
+ def do_print(self):
+ fmt = 'Memory %-32s %12d\n'
+ for label, stats in map(None, self.labels, self.stats):
+ self.outfp.write(fmt % (label, stats))
+
+memory_stats = MemStats()
+
+# utility functions
+
+def _scons_syntax_error(e):
+ """Handle syntax errors. Print out a message and show where the error
+ occurred.
+ """
+ etype, value, tb = sys.exc_info()
+ lines = traceback.format_exception_only(etype, value)
+ for line in lines:
+ sys.stderr.write(line+'\n')
+ sys.exit(2)
+
+def find_deepest_user_frame(tb):
+ """
+ Find the deepest stack frame that is not part of SCons.
+
+ Input is a "pre-processed" stack trace in the form
+ returned by traceback.extract_tb() or traceback.extract_stack()
+ """
+
+ tb.reverse()
+
+ # find the deepest traceback frame that is not part
+ # of SCons:
+ for frame in tb:
+ filename = frame[0]
+ if string.find(filename, os.sep+'SCons'+os.sep) == -1:
+ return frame
+ return tb[0]
+
+def _scons_user_error(e):
+ """Handle user errors. Print out a message and a description of the
+ error, along with the line number and routine where it occured.
+ The file and line number will be the deepest stack frame that is
+ not part of SCons itself.
+ """
+ global print_stacktrace
+ etype, value, tb = sys.exc_info()
+ if print_stacktrace:
+ traceback.print_exception(etype, value, tb)
+ filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
+ sys.stderr.write("\nscons: *** %s\n" % value)
+ sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
+ sys.exit(2)
+
+def _scons_user_warning(e):
+ """Handle user warnings. Print out a message and a description of
+ the warning, along with the line number and routine where it occured.
+ The file and line number will be the deepest stack frame that is
+ not part of SCons itself.
+ """
+ etype, value, tb = sys.exc_info()
+ filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
+ sys.stderr.write("\nscons: warning: %s\n" % e)
+ sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
+
+def _scons_internal_warning(e):
+ """Slightly different from _scons_user_warning in that we use the
+ *current call stack* rather than sys.exc_info() to get our stack trace.
+ This is used by the warnings framework to print warnings."""
+ filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
+ sys.stderr.write("\nscons: warning: %s\n" % e[0])
+ sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
+
+def _scons_internal_error():
+ """Handle all errors but user errors. Print out a message telling
+ the user what to do in this case and print a normal trace.
+ """
+ print 'internal error'
+ traceback.print_exc()
+ sys.exit(2)
+
+def _SConstruct_exists(dirname='', repositories=[], filelist=None):
+ """This function checks that an SConstruct file exists in a directory.
+ If so, it returns the path of the file. By default, it checks the
+ current directory.
+ """
+ if not filelist:
+ filelist = ['SConstruct', 'Sconstruct', 'sconstruct']
+ for file in filelist:
+ sfile = os.path.join(dirname, file)
+ if os.path.isfile(sfile):
+ return sfile
+ if not os.path.isabs(sfile):
+ for rep in repositories:
+ if os.path.isfile(os.path.join(rep, sfile)):
+ return sfile
+ return None
+
+def _set_debug_values(options):
+ global print_memoizer, print_objects, print_stacktrace, print_time
+
+ debug_values = options.debug
+
+ if "count" in debug_values:
+ # All of the object counts are within "if __debug__:" blocks,
+ # which get stripped when running optimized (with python -O or
+ # from compiled *.pyo files). Provide a warning if __debug__ is
+ # stripped, so it doesn't just look like --debug=count is broken.
+ enable_count = False
+ if __debug__: enable_count = True
+ if enable_count:
+ count_stats.enable(sys.stdout)
+ else:
+ msg = "--debug=count is not supported when running SCons\n" + \
+ "\twith the python -O option or optimized (.pyo) modules."
+ SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg)
+ if "dtree" in debug_values:
+ options.tree_printers.append(TreePrinter(derived=True))
+ options.debug_explain = ("explain" in debug_values)
+ if "findlibs" in debug_values:
+ SCons.Scanner.Prog.print_find_libs = "findlibs"
+ options.debug_includes = ("includes" in debug_values)
+ print_memoizer = ("memoizer" in debug_values)
+ if "memory" in debug_values:
+ memory_stats.enable(sys.stdout)
+ print_objects = ("objects" in debug_values)
+ if "presub" in debug_values:
+ SCons.Action.print_actions_presub = 1
+ if "stacktrace" in debug_values:
+ print_stacktrace = 1
+ if "stree" in debug_values:
+ options.tree_printers.append(TreePrinter(status=True))
+ if "time" in debug_values:
+ print_time = 1
+ if "tree" in debug_values:
+ options.tree_printers.append(TreePrinter())
+
+def _create_path(plist):
+ path = '.'
+ for d in plist:
+ if os.path.isabs(d):
+ path = d
+ else:
+ path = path + '/' + d
+ return path
+
+def _load_site_scons_dir(topdir, site_dir_name=None):
+ """Load the site_scons dir under topdir.
+ Adds site_scons to sys.path, imports site_scons/site_init.py,
+ and adds site_scons/site_tools to default toolpath."""
+ if site_dir_name:
+ err_if_not_found = True # user specified: err if missing
+ else:
+ site_dir_name = "site_scons"
+ err_if_not_found = False
+
+ site_dir = os.path.join(topdir.path, site_dir_name)
+ if not os.path.exists(site_dir):
+ if err_if_not_found:
+ raise SCons.Errors.UserError, "site dir %s not found."%site_dir
+ return
+
+ site_init_filename = "site_init.py"
+ site_init_modname = "site_init"
+ site_tools_dirname = "site_tools"
+ sys.path = [os.path.abspath(site_dir)] + sys.path
+ site_init_file = os.path.join(site_dir, site_init_filename)
+ site_tools_dir = os.path.join(site_dir, site_tools_dirname)
+ if os.path.exists(site_init_file):
+ import imp
+ # TODO(2.4): turn this into try:-except:-finally:
+ try:
+ try:
+ fp, pathname, description = imp.find_module(site_init_modname,
+ [site_dir])
+ # Load the file into SCons.Script namespace. This is
+ # opaque and clever; m is the module object for the
+ # SCons.Script module, and the exec ... in call executes a
+ # file (or string containing code) in the context of the
+ # module's dictionary, so anything that code defines ends
+ # up adding to that module. This is really short, but all
+ # the error checking makes it longer.
+ try:
+ m = sys.modules['SCons.Script']
+ except Exception, e:
+ fmt = 'cannot import site_init.py: missing SCons.Script module %s'
+ raise SCons.Errors.InternalError, fmt % repr(e)
+ try:
+ # This is the magic.
+ exec fp in m.__dict__
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ fmt = '*** Error loading site_init file %s:\n'
+ sys.stderr.write(fmt % repr(site_init_file))
+ raise
+ except KeyboardInterrupt:
+ raise
+ except ImportError, e:
+ fmt = '*** cannot import site init file %s:\n'
+ sys.stderr.write(fmt % repr(site_init_file))
+ raise
+ finally:
+ if fp:
+ fp.close()
+ if os.path.exists(site_tools_dir):
+ SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir))
+
+def version_string(label, module):
+ version = module.__version__
+ build = module.__build__
+ if build:
+ if build[0] != '.':
+ build = '.' + build
+ version = version + build
+ fmt = "\t%s: v%s, %s, by %s on %s\n"
+ return fmt % (label,
+ version,
+ module.__date__,
+ module.__developer__,
+ module.__buildsys__)
+
+def _main(parser):
+ global exit_status
+ global this_build_status
+
+ options = parser.values
+
+ # Here's where everything really happens.
+
+ # First order of business: set up default warnings and then
+ # handle the user's warning options, so that we can issue (or
+ # suppress) appropriate warnings about anything that might happen,
+ # as configured by the user.
+
+ default_warnings = [ SCons.Warnings.CorruptSConsignWarning,
+ SCons.Warnings.DeprecatedWarning,
+ SCons.Warnings.DuplicateEnvironmentWarning,
+ SCons.Warnings.FutureReservedVariableWarning,
+ SCons.Warnings.LinkWarning,
+ SCons.Warnings.MissingSConscriptWarning,
+ SCons.Warnings.NoMD5ModuleWarning,
+ SCons.Warnings.NoMetaclassSupportWarning,
+ SCons.Warnings.NoObjectCountWarning,
+ SCons.Warnings.NoParallelSupportWarning,
+ SCons.Warnings.MisleadingKeywordsWarning,
+ SCons.Warnings.ReservedVariableWarning,
+ SCons.Warnings.StackSizeWarning,
+ SCons.Warnings.VisualVersionMismatch,
+ SCons.Warnings.VisualCMissingWarning,
+ ]
+
+ for warning in default_warnings:
+ SCons.Warnings.enableWarningClass(warning)
+ SCons.Warnings._warningOut = _scons_internal_warning
+ SCons.Warnings.process_warn_strings(options.warn)
+
+ # Now that we have the warnings configuration set up, we can actually
+ # issue (or suppress) any warnings about warning-worthy things that
+ # occurred while the command-line options were getting parsed.
+ try:
+ dw = options.delayed_warnings
+ except AttributeError:
+ pass
+ else:
+ delayed_warnings.extend(dw)
+ for warning_type, message in delayed_warnings:
+ SCons.Warnings.warn(warning_type, message)
+
+ if options.diskcheck:
+ SCons.Node.FS.set_diskcheck(options.diskcheck)
+
+ # Next, we want to create the FS object that represents the outside
+ # world's file system, as that's central to a lot of initialization.
+ # To do this, however, we need to be in the directory from which we
+ # want to start everything, which means first handling any relevant
+ # options that might cause us to chdir somewhere (-C, -D, -U, -u).
+ if options.directory:
+ script_dir = os.path.abspath(_create_path(options.directory))
+ else:
+ script_dir = os.getcwd()
+
+ target_top = None
+ if options.climb_up:
+ target_top = '.' # directory to prepend to targets
+ while script_dir and not _SConstruct_exists(script_dir,
+ options.repository,
+ options.file):
+ script_dir, last_part = os.path.split(script_dir)
+ if last_part:
+ target_top = os.path.join(last_part, target_top)
+ else:
+ script_dir = ''
+
+ if script_dir and script_dir != os.getcwd():
+ display("scons: Entering directory `%s'" % script_dir)
+ try:
+ os.chdir(script_dir)
+ except OSError:
+ sys.stderr.write("Could not change directory to %s\n" % script_dir)
+
+ # Now that we're in the top-level SConstruct directory, go ahead
+ # and initialize the FS object that represents the file system,
+ # and make it the build engine default.
+ fs = SCons.Node.FS.get_default_fs()
+
+ for rep in options.repository:
+ fs.Repository(rep)
+
+ # Now that we have the FS object, the next order of business is to
+ # check for an SConstruct file (or other specified config file).
+ # If there isn't one, we can bail before doing any more work.
+ scripts = []
+ if options.file:
+ scripts.extend(options.file)
+ if not scripts:
+ sfile = _SConstruct_exists(repositories=options.repository,
+ filelist=options.file)
+ if sfile:
+ scripts.append(sfile)
+
+ if not scripts:
+ if options.help:
+ # There's no SConstruct, but they specified -h.
+ # Give them the options usage now, before we fail
+ # trying to read a non-existent SConstruct file.
+ raise SConsPrintHelpException
+ raise SCons.Errors.UserError, "No SConstruct file found."
+
+ if scripts[0] == "-":
+ d = fs.getcwd()
+ else:
+ d = fs.File(scripts[0]).dir
+ fs.set_SConstruct_dir(d)
+
+ _set_debug_values(options)
+ SCons.Node.implicit_cache = options.implicit_cache
+ SCons.Node.implicit_deps_changed = options.implicit_deps_changed
+ SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
+
+ if options.no_exec:
+ SCons.SConf.dryrun = 1
+ SCons.Action.execute_actions = None
+ if options.question:
+ SCons.SConf.dryrun = 1
+ if options.clean:
+ SCons.SConf.SetBuildType('clean')
+ if options.help:
+ SCons.SConf.SetBuildType('help')
+ SCons.SConf.SetCacheMode(options.config)
+ SCons.SConf.SetProgressDisplay(progress_display)
+
+ if options.no_progress or options.silent:
+ progress_display.set_mode(0)
+
+ if options.site_dir:
+ _load_site_scons_dir(d, options.site_dir)
+ elif not options.no_site_dir:
+ _load_site_scons_dir(d)
+
+ if options.include_dir:
+ sys.path = options.include_dir + sys.path
+
+ # That should cover (most of) the options. Next, set up the variables
+ # that hold command-line arguments, so the SConscript files that we
+ # read and execute have access to them.
+ targets = []
+ xmit_args = []
+ for a in parser.largs:
+ if a[:1] == '-':
+ continue
+ if '=' in a:
+ xmit_args.append(a)
+ else:
+ targets.append(a)
+ SCons.Script._Add_Targets(targets + parser.rargs)
+ SCons.Script._Add_Arguments(xmit_args)
+
+ # If stdout is not a tty, replace it with a wrapper object to call flush
+ # after every write.
+ #
+ # Tty devices automatically flush after every newline, so the replacement
+ # isn't necessary. Furthermore, if we replace sys.stdout, the readline
+ # module will no longer work. This affects the behavior during
+ # --interactive mode. --interactive should only be used when stdin and
+ # stdout refer to a tty.
+ if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
+ sys.stdout = SCons.Util.Unbuffered(sys.stdout)
+ if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty():
+ sys.stderr = SCons.Util.Unbuffered(sys.stderr)
+
+ memory_stats.append('before reading SConscript files:')
+ count_stats.append(('pre-', 'read'))
+
+ # And here's where we (finally) read the SConscript files.
+
+ progress_display("scons: Reading SConscript files ...")
+
+ start_time = time.time()
+ try:
+ for script in scripts:
+ SCons.Script._SConscript._SConscript(fs, script)
+ except SCons.Errors.StopError, e:
+ # We had problems reading an SConscript file, such as it
+ # couldn't be copied in to the VariantDir. Since we're just
+ # reading SConscript files and haven't started building
+ # things yet, stop regardless of whether they used -i or -k
+ # or anything else.
+ sys.stderr.write("scons: *** %s Stop.\n" % e)
+ exit_status = 2
+ sys.exit(exit_status)
+ global sconscript_time
+ sconscript_time = time.time() - start_time
+
+ progress_display("scons: done reading SConscript files.")
+
+ memory_stats.append('after reading SConscript files:')
+ count_stats.append(('post-', 'read'))
+
+ # Re-{enable,disable} warnings in case they disabled some in
+ # the SConscript file.
+ #
+ # We delay enabling the PythonVersionWarning class until here so that,
+ # if they explicity disabled it in either in the command line or in
+ # $SCONSFLAGS, or in the SConscript file, then the search through
+ # the list of deprecated warning classes will find that disabling
+ # first and not issue the warning.
+ SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning)
+ SCons.Warnings.process_warn_strings(options.warn)
+
+ # Now that we've read the SConscript files, we can check for the
+ # warning about deprecated Python versions--delayed until here
+ # in case they disabled the warning in the SConscript files.
+ if python_version_deprecated():
+ msg = "Support for pre-2.4 Python (%s) is deprecated.\n" + \
+ " If this will cause hardship, contact dev@scons.tigris.org."
+ SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
+ msg % python_version_string())
+
+ if not options.help:
+ SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
+
+ # Now re-parse the command-line options (any to the left of a '--'
+ # argument, that is) with any user-defined command-line options that
+ # the SConscript files may have added to the parser object. This will
+ # emit the appropriate error message and exit if any unknown option
+ # was specified on the command line.
+
+ parser.preserve_unknown_options = False
+ parser.parse_args(parser.largs, options)
+
+ if options.help:
+ help_text = SCons.Script.help_text
+ if help_text is None:
+ # They specified -h, but there was no Help() inside the
+ # SConscript files. Give them the options usage.
+ raise SConsPrintHelpException
+ else:
+ print help_text
+ print "Use scons -H for help about command-line options."
+ exit_status = 0
+ return
+
+ # Change directory to the top-level SConstruct directory, then tell
+ # the Node.FS subsystem that we're all done reading the SConscript
+ # files and calling Repository() and VariantDir() and changing
+ # directories and the like, so it can go ahead and start memoizing
+ # the string values of file system nodes.
+
+ fs.chdir(fs.Top)
+
+ SCons.Node.FS.save_strings(1)
+
+ # Now that we've read the SConscripts we can set the options
+ # that are SConscript settable:
+ SCons.Node.implicit_cache = options.implicit_cache
+ SCons.Node.FS.set_duplicate(options.duplicate)
+ fs.set_max_drift(options.max_drift)
+
+ SCons.Job.explicit_stack_size = options.stack_size
+
+ if options.md5_chunksize:
+ SCons.Node.FS.File.md5_chunksize = options.md5_chunksize
+
+ platform = SCons.Platform.platform_module()
+
+ if options.interactive:
+ SCons.Script.Interactive.interact(fs, OptionsParser, options,
+ targets, target_top)
+
+ else:
+
+ # Build the targets
+ nodes = _build_targets(fs, options, targets, target_top)
+ if not nodes:
+ exit_status = 2
+
+def _build_targets(fs, options, targets, target_top):
+
+ global this_build_status
+ this_build_status = 0
+
+ progress_display.set_mode(not (options.no_progress or options.silent))
+ display.set_mode(not options.silent)
+ SCons.Action.print_actions = not options.silent
+ SCons.Action.execute_actions = not options.no_exec
+ SCons.Node.FS.do_store_info = not options.no_exec
+ SCons.SConf.dryrun = options.no_exec
+
+ if options.diskcheck:
+ SCons.Node.FS.set_diskcheck(options.diskcheck)
+
+ SCons.CacheDir.cache_enabled = not options.cache_disable
+ SCons.CacheDir.cache_debug = options.cache_debug
+ SCons.CacheDir.cache_force = options.cache_force
+ SCons.CacheDir.cache_show = options.cache_show
+
+ if options.no_exec:
+ CleanTask.execute = CleanTask.show
+ else:
+ CleanTask.execute = CleanTask.remove
+
+ lookup_top = None
+ if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default:
+ # They specified targets on the command line or modified
+ # BUILD_TARGETS in the SConscript file(s), so if they used -u,
+ # -U or -D, we have to look up targets relative to the top,
+ # but we build whatever they specified.
+ if target_top:
+ lookup_top = fs.Dir(target_top)
+ target_top = None
+
+ targets = SCons.Script.BUILD_TARGETS
+ else:
+ # There are no targets specified on the command line,
+ # so if they used -u, -U or -D, we may have to restrict
+ # what actually gets built.
+ d = None
+ if target_top:
+ if options.climb_up == 1:
+ # -u, local directory and below
+ target_top = fs.Dir(target_top)
+ lookup_top = target_top
+ elif options.climb_up == 2:
+ # -D, all Default() targets
+ target_top = None
+ lookup_top = None
+ elif options.climb_up == 3:
+ # -U, local SConscript Default() targets
+ target_top = fs.Dir(target_top)
+ def check_dir(x, target_top=target_top):
+ if hasattr(x, 'cwd') and not x.cwd is None:
+ cwd = x.cwd.srcnode()
+ return cwd == target_top
+ else:
+ # x doesn't have a cwd, so it's either not a target,
+ # or not a file, so go ahead and keep it as a default
+ # target and let the engine sort it out:
+ return 1
+ d = filter(check_dir, SCons.Script.DEFAULT_TARGETS)
+ SCons.Script.DEFAULT_TARGETS[:] = d
+ target_top = None
+ lookup_top = None
+
+ targets = SCons.Script._Get_Default_Targets(d, fs)
+
+ if not targets:
+ sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
+ return None
+
+ def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
+ if isinstance(x, SCons.Node.Node):
+ node = x
+ else:
+ node = None
+ # Why would ltop be None? Unfortunately this happens.
+ if ltop is None: ltop = ''
+ # Curdir becomes important when SCons is called with -u, -C,
+ # or similar option that changes directory, and so the paths
+ # of targets given on the command line need to be adjusted.
+ curdir = os.path.join(os.getcwd(), str(ltop))
+ for lookup in SCons.Node.arg2nodes_lookups:
+ node = lookup(x, curdir=curdir)
+ if node is not None:
+ break
+ if node is None:
+ node = fs.Entry(x, directory=ltop, create=1)
+ if ttop and not node.is_under(ttop):
+ if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
+ node = ttop
+ else:
+ node = None
+ return node
+
+ nodes = filter(None, map(Entry, targets))
+
+ task_class = BuildTask # default action is to build targets
+ opening_message = "Building targets ..."
+ closing_message = "done building targets."
+ if options.keep_going:
+ failure_message = "done building targets (errors occurred during build)."
+ else:
+ failure_message = "building terminated because of errors."
+ if options.question:
+ task_class = QuestionTask
+ try:
+ if options.clean:
+ task_class = CleanTask
+ opening_message = "Cleaning targets ..."
+ closing_message = "done cleaning targets."
+ if options.keep_going:
+ failure_message = "done cleaning targets (errors occurred during clean)."
+ else:
+ failure_message = "cleaning terminated because of errors."
+ except AttributeError:
+ pass
+
+ task_class.progress = ProgressObject
+
+ if options.random:
+ def order(dependencies):
+ """Randomize the dependencies."""
+ import random
+ # This is cribbed from the implementation of
+ # random.shuffle() in Python 2.X.
+ d = dependencies
+ for i in xrange(len(d)-1, 0, -1):
+ j = int(random.random() * (i+1))
+ d[i], d[j] = d[j], d[i]
+ return d
+ else:
+ def order(dependencies):
+ """Leave the order of dependencies alone."""
+ return dependencies
+
+ if options.taskmastertrace_file == '-':
+ tmtrace = sys.stdout
+ elif options.taskmastertrace_file:
+ tmtrace = open(options.taskmastertrace_file, 'wb')
+ else:
+ tmtrace = None
+ taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace)
+
+ # Let the BuildTask objects get at the options to respond to the
+ # various print_* settings, tree_printer list, etc.
+ BuildTask.options = options
+
+ global num_jobs
+ num_jobs = options.num_jobs
+ jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ if num_jobs > 1:
+ msg = None
+ if jobs.num_jobs == 1:
+ msg = "parallel builds are unsupported by this version of Python;\n" + \
+ "\tignoring -j or num_jobs option.\n"
+ elif sys.platform == 'win32':
+ msg = fetch_win32_parallel_msg()
+ if msg:
+ SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
+
+ memory_stats.append('before building targets:')
+ count_stats.append(('pre-', 'build'))
+
+ def jobs_postfunc(
+ jobs=jobs,
+ options=options,
+ closing_message=closing_message,
+ failure_message=failure_message
+ ):
+ if jobs.were_interrupted():
+ if not options.no_progress and not options.silent:
+ sys.stderr.write("scons: Build interrupted.\n")
+ global exit_status
+ global this_build_status
+ exit_status = 2
+ this_build_status = 2
+
+ if this_build_status:
+ progress_display("scons: " + failure_message)
+ else:
+ progress_display("scons: " + closing_message)
+ if not options.no_exec:
+ if jobs.were_interrupted():
+ progress_display("scons: writing .sconsign file.")
+ SCons.SConsign.write()
+
+ progress_display("scons: " + opening_message)
+ jobs.run(postfunc = jobs_postfunc)
+
+ memory_stats.append('after building targets:')
+ count_stats.append(('post-', 'build'))
+
+ return nodes
+
+def _exec_main(parser, values):
+ sconsflags = os.environ.get('SCONSFLAGS', '')
+ all_args = string.split(sconsflags) + sys.argv[1:]
+
+ options, args = parser.parse_args(all_args, values)
+
+ if type(options.debug) == type([]) and "pdb" in options.debug:
+ import pdb
+ pdb.Pdb().runcall(_main, parser)
+ elif options.profile_file:
+ try:
+ from cProfile import Profile
+ except ImportError, e:
+ from profile import Profile
+
+ # Some versions of Python 2.4 shipped a profiler that had the
+ # wrong 'c_exception' entry in its dispatch table. Make sure
+ # we have the right one. (This may put an unnecessary entry
+ # in the table in earlier versions of Python, but its presence
+ # shouldn't hurt anything).
+ try:
+ dispatch = Profile.dispatch
+ except AttributeError:
+ pass
+ else:
+ dispatch['c_exception'] = Profile.trace_dispatch_return
+
+ prof = Profile()
+ try:
+ prof.runcall(_main, parser)
+ except SConsPrintHelpException, e:
+ prof.dump_stats(options.profile_file)
+ raise e
+ except SystemExit:
+ pass
+ prof.dump_stats(options.profile_file)
+ else:
+ _main(parser)
+
+def main():
+ global OptionsParser
+ global exit_status
+ global first_command_start
+
+ # Check up front for a Python version we do not support. We
+ # delay the check for deprecated Python versions until later,
+ # after the SConscript files have been read, in case they
+ # disable that warning.
+ if python_version_unsupported():
+ msg = "scons: *** SCons version %s does not run under Python version %s.\n"
+ sys.stderr.write(msg % (SCons.__version__, python_version_string()))
+ sys.exit(1)
+
+ parts = ["SCons by Steven Knight et al.:\n"]
+ try:
+ import __main__
+ parts.append(version_string("script", __main__))
+ except (ImportError, AttributeError):
+ # On Windows there is no scons.py, so there is no
+ # __main__.__version__, hence there is no script version.
+ pass
+ parts.append(version_string("engine", SCons))
+ parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation")
+ version = string.join(parts, '')
+
+ import SConsOptions
+ parser = SConsOptions.Parser(version)
+ values = SConsOptions.SConsValues(parser.get_default_values())
+
+ OptionsParser = parser
+
+ try:
+ _exec_main(parser, values)
+ except SystemExit, s:
+ if s:
+ exit_status = s
+ except KeyboardInterrupt:
+ print("scons: Build interrupted.")
+ sys.exit(2)
+ except SyntaxError, e:
+ _scons_syntax_error(e)
+ except SCons.Errors.InternalError:
+ _scons_internal_error()
+ except SCons.Errors.UserError, e:
+ _scons_user_error(e)
+ except SConsPrintHelpException:
+ parser.print_help()
+ exit_status = 0
+ except SCons.Errors.BuildError, e:
+ exit_status = e.exitstatus
+ except:
+ # An exception here is likely a builtin Python exception Python
+ # code in an SConscript file. Show them precisely what the
+ # problem was and where it happened.
+ SCons.Script._SConscript.SConscript_exception()
+ sys.exit(2)
+
+ memory_stats.print_stats()
+ count_stats.print_stats()
+
+ if print_objects:
+ SCons.Debug.listLoggedInstances('*')
+ #SCons.Debug.dumpLoggedInstances('*')
+
+ if print_memoizer:
+ SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:")
+
+ # Dump any development debug info that may have been enabled.
+ # These are purely for internal debugging during development, so
+ # there's no need to control them with --debug= options; they're
+ # controlled by changing the source code.
+ SCons.Debug.dump_caller_counts()
+ SCons.Taskmaster.dump_stats()
+
+ if print_time:
+ total_time = time.time() - SCons.Script.start_time
+ if num_jobs == 1:
+ ct = cumulative_command_time
+ else:
+ if last_command_end is None or first_command_start is None:
+ ct = 0.0
+ else:
+ ct = last_command_end - first_command_start
+ scons_time = total_time - sconscript_time - ct
+ print "Total build time: %f seconds"%total_time
+ print "Total SConscript file execution time: %f seconds"%sconscript_time
+ print "Total SCons execution time: %f seconds"%scons_time
+ print "Total command execution time: %f seconds"%ct
+
+ sys.exit(exit_status)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/MainTests.py b/src/engine/SCons/Script/MainTests.py
new file mode 100644
index 0000000..5dbd4db
--- /dev/null
+++ b/src/engine/SCons/Script/MainTests.py
@@ -0,0 +1,53 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/MainTests.py 4577 2009/12/27 19:44:43 scons"
+
+import unittest
+import SCons.Errors
+import SCons.Script.Main
+
+# Unit tests of various classes within SCons.Script.Main.py.
+#
+# Most of the tests of this functionality are actually end-to-end scripts
+# in the test/ hierarchy.
+#
+# This module is for specific bits of functionality that we can test
+# more effectively here, instead of in an end-to-end test that would
+# have to reach into SCons.Script.Main for various classes or other bits
+# of private functionality.
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = []
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py
new file mode 100644
index 0000000..585e5ef
--- /dev/null
+++ b/src/engine/SCons/Script/SConsOptions.py
@@ -0,0 +1,944 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/SConsOptions.py 4577 2009/12/27 19:44:43 scons"
+
+import optparse
+import re
+import string
+import sys
+import textwrap
+
+try:
+ no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')
+except re.error:
+ # Pre-2.0 Python versions don't have the (?<= negative
+ # look-behind assertion.
+ no_hyphen_re = re.compile(r'(\s+|-*\w{2,}-(?=\w{2,}))')
+
+try:
+ from gettext import gettext
+except ImportError:
+ def gettext(message):
+ return message
+_ = gettext
+
+import SCons.Node.FS
+import SCons.Warnings
+
+OptionValueError = optparse.OptionValueError
+SUPPRESS_HELP = optparse.SUPPRESS_HELP
+
+diskcheck_all = SCons.Node.FS.diskcheck_types()
+
+def diskcheck_convert(value):
+ if value is None:
+ return []
+ if not SCons.Util.is_List(value):
+ value = string.split(value, ',')
+ result = []
+ for v in map(string.lower, value):
+ if v == 'all':
+ result = diskcheck_all
+ elif v == 'none':
+ result = []
+ elif v in diskcheck_all:
+ result.append(v)
+ else:
+ raise ValueError, v
+ return result
+
+class SConsValues(optparse.Values):
+ """
+ Holder class for uniform access to SCons options, regardless
+ of whether or not they can be set on the command line or in the
+ SConscript files (using the SetOption() function).
+
+ A SCons option value can originate three different ways:
+
+ 1) set on the command line;
+ 2) set in an SConscript file;
+ 3) the default setting (from the the op.add_option()
+ calls in the Parser() function, below).
+
+ The command line always overrides a value set in a SConscript file,
+ which in turn always overrides default settings. Because we want
+ to support user-specified options in the SConscript file itself,
+ though, we may not know about all of the options when the command
+ line is first parsed, so we can't make all the necessary precedence
+ decisions at the time the option is configured.
+
+ The solution implemented in this class is to keep these different sets
+ of settings separate (command line, SConscript file, and default)
+ and to override the __getattr__() method to check them in turn.
+ This should allow the rest of the code to just fetch values as
+ attributes of an instance of this class, without having to worry
+ about where they came from.
+
+ Note that not all command line options are settable from SConscript
+ files, and the ones that are must be explicitly added to the
+ "settable" list in this class, and optionally validated and coerced
+ in the set_option() method.
+ """
+
+ def __init__(self, defaults):
+ self.__dict__['__defaults__'] = defaults
+ self.__dict__['__SConscript_settings__'] = {}
+
+ def __getattr__(self, attr):
+ """
+ Fetches an options value, checking first for explicit settings
+ from the command line (which are direct attributes), then the
+ SConscript file settings, then the default values.
+ """
+ try:
+ return self.__dict__[attr]
+ except KeyError:
+ try:
+ return self.__dict__['__SConscript_settings__'][attr]
+ except KeyError:
+ return getattr(self.__dict__['__defaults__'], attr)
+
+ settable = [
+ 'clean',
+ 'diskcheck',
+ 'duplicate',
+ 'help',
+ 'implicit_cache',
+ 'max_drift',
+ 'md5_chunksize',
+ 'no_exec',
+ 'num_jobs',
+ 'random',
+ 'stack_size',
+ 'warn',
+ ]
+
+ def set_option(self, name, value):
+ """
+ Sets an option from an SConscript file.
+ """
+ if not name in self.settable:
+ raise SCons.Errors.UserError, "This option is not settable from a SConscript file: %s"%name
+
+ if name == 'num_jobs':
+ try:
+ value = int(value)
+ if value < 1:
+ raise ValueError
+ except ValueError:
+ raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value)
+ elif name == 'max_drift':
+ try:
+ value = int(value)
+ except ValueError:
+ raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
+ elif name == 'duplicate':
+ try:
+ value = str(value)
+ except ValueError:
+ raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
+ if not value in SCons.Node.FS.Valid_Duplicates:
+ raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
+ # Set the duplicate style right away so it can affect linking
+ # of SConscript files.
+ SCons.Node.FS.set_duplicate(value)
+ elif name == 'diskcheck':
+ try:
+ value = diskcheck_convert(value)
+ except ValueError, v:
+ raise SCons.Errors.UserError, "Not a valid diskcheck value: %s"%v
+ if not self.__dict__.has_key('diskcheck'):
+ # No --diskcheck= option was specified on the command line.
+ # Set this right away so it can affect the rest of the
+ # file/Node lookups while processing the SConscript files.
+ SCons.Node.FS.set_diskcheck(value)
+ elif name == 'stack_size':
+ try:
+ value = int(value)
+ except ValueError:
+ raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
+ elif name == 'md5_chunksize':
+ try:
+ value = int(value)
+ except ValueError:
+ raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
+ elif name == 'warn':
+ if SCons.Util.is_String(value):
+ value = [value]
+ value = self.__SConscript_settings__.get(name, []) + value
+ SCons.Warnings.process_warn_strings(value)
+
+ self.__SConscript_settings__[name] = value
+
+class SConsOption(optparse.Option):
+ def convert_value(self, opt, value):
+ if value is not None:
+ if self.nargs in (1, '?'):
+ return self.check_value(opt, value)
+ else:
+ return tuple(map(lambda v, o=opt, s=self: s.check_value(o, v), value))
+
+ def process(self, opt, value, values, parser):
+
+ # First, convert the value(s) to the right type. Howl if any
+ # value(s) are bogus.
+ value = self.convert_value(opt, value)
+
+ # And then take whatever action is expected of us.
+ # This is a separate method to make life easier for
+ # subclasses to add new actions.
+ return self.take_action(
+ self.action, self.dest, opt, value, values, parser)
+
+ def _check_nargs_optional(self):
+ if self.nargs == '?' and self._short_opts:
+ fmt = "option %s: nargs='?' is incompatible with short options"
+ raise SCons.Errors.UserError, fmt % self._short_opts[0]
+
+ try:
+ _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS
+
+ _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS
+
+ except AttributeError:
+ # optparse.Option had no CONST_ACTIONS before Python 2.5.
+
+ _orig_CONST_ACTIONS = ("store_const",)
+
+ def _check_const(self):
+ if self.action not in self.CONST_ACTIONS and self.const is not None:
+ raise OptionError(
+ "'const' must not be supplied for action %r" % self.action,
+ self)
+
+ # optparse.Option collects its list of unbound check functions
+ # up front. This sucks because it means we can't just override
+ # the _check_const() function like a normal method, we have to
+ # actually replace it in the list. This seems to be the most
+ # straightforward way to do that.
+
+ _orig_CHECK_METHODS = [optparse.Option._check_action,
+ optparse.Option._check_type,
+ optparse.Option._check_choice,
+ optparse.Option._check_dest,
+ _check_const,
+ optparse.Option._check_nargs,
+ optparse.Option._check_callback]
+
+ CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional]
+
+ CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS
+
+class SConsOptionGroup(optparse.OptionGroup):
+ """
+ A subclass for SCons-specific option groups.
+
+ The only difference between this and the base class is that we print
+ the group's help text flush left, underneath their own title but
+ lined up with the normal "SCons Options".
+ """
+ def format_help(self, formatter):
+ """
+ Format an option group's help text, outdenting the title so it's
+ flush with the "SCons Options" title we print at the top.
+ """
+ formatter.dedent()
+ result = formatter.format_heading(self.title)
+ formatter.indent()
+ result = result + optparse.OptionContainer.format_help(self, formatter)
+ return result
+
+class SConsOptionParser(optparse.OptionParser):
+ preserve_unknown_options = False
+
+ def error(self, msg):
+ self.print_usage(sys.stderr)
+ sys.stderr.write("SCons error: %s\n" % msg)
+ sys.exit(2)
+
+ def _process_long_opt(self, rargs, values):
+ """
+ SCons-specific processing of long options.
+
+ This is copied directly from the normal
+ optparse._process_long_opt() method, except that, if configured
+ to do so, we catch the exception thrown when an unknown option
+ is encountered and just stick it back on the "leftover" arguments
+ for later (re-)processing.
+ """
+ arg = rargs.pop(0)
+
+ # Value explicitly attached to arg? Pretend it's the next
+ # argument.
+ if "=" in arg:
+ (opt, next_arg) = string.split(arg, "=", 1)
+ rargs.insert(0, next_arg)
+ had_explicit_value = True
+ else:
+ opt = arg
+ had_explicit_value = False
+
+ try:
+ opt = self._match_long_opt(opt)
+ except optparse.BadOptionError:
+ if self.preserve_unknown_options:
+ # SCons-specific: if requested, add unknown options to
+ # the "leftover arguments" list for later processing.
+ self.largs.append(arg)
+ if had_explicit_value:
+ # The unknown option will be re-processed later,
+ # so undo the insertion of the explicit value.
+ rargs.pop(0)
+ return
+ raise
+
+ option = self._long_opt[opt]
+ if option.takes_value():
+ nargs = option.nargs
+ if nargs == '?':
+ if had_explicit_value:
+ value = rargs.pop(0)
+ else:
+ value = option.const
+ elif len(rargs) < nargs:
+ if nargs == 1:
+ self.error(_("%s option requires an argument") % opt)
+ else:
+ self.error(_("%s option requires %d arguments")
+ % (opt, nargs))
+ elif nargs == 1:
+ value = rargs.pop(0)
+ else:
+ value = tuple(rargs[0:nargs])
+ del rargs[0:nargs]
+
+ elif had_explicit_value:
+ self.error(_("%s option does not take a value") % opt)
+
+ else:
+ value = None
+
+ option.process(opt, value, values, self)
+
+ def add_local_option(self, *args, **kw):
+ """
+ Adds a local option to the parser.
+
+ This is initiated by a SetOption() call to add a user-defined
+ command-line option. We add the option to a separate option
+ group for the local options, creating the group if necessary.
+ """
+ try:
+ group = self.local_option_group
+ except AttributeError:
+ group = SConsOptionGroup(self, 'Local Options')
+ group = self.add_option_group(group)
+ self.local_option_group = group
+
+ result = apply(group.add_option, args, kw)
+
+ if result:
+ # The option was added succesfully. We now have to add the
+ # default value to our object that holds the default values
+ # (so that an attempt to fetch the option's attribute will
+ # yield the default value when not overridden) and then
+ # we re-parse the leftover command-line options, so that
+ # any value overridden on the command line is immediately
+ # available if the user turns around and does a GetOption()
+ # right away.
+ setattr(self.values.__defaults__, result.dest, result.default)
+ self.parse_args(self.largs, self.values)
+
+ return result
+
+class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
+ def format_usage(self, usage):
+ return "usage: %s\n" % usage
+
+ def format_heading(self, heading):
+ """
+ This translates any heading of "options" or "Options" into
+ "SCons Options." Unfortunately, we have to do this here,
+ because those titles are hard-coded in the optparse calls.
+ """
+ if heading == 'options':
+ # The versions of optparse.py shipped with Pythons 2.3 and
+ # 2.4 pass this in uncapitalized; override that so we get
+ # consistent output on all versions.
+ heading = "Options"
+ if heading == 'Options':
+ heading = "SCons Options"
+ return optparse.IndentedHelpFormatter.format_heading(self, heading)
+
+ def format_option(self, option):
+ """
+ A copy of the normal optparse.IndentedHelpFormatter.format_option()
+ method. This has been snarfed so we can modify text wrapping to
+ out liking:
+
+ -- add our own regular expression that doesn't break on hyphens
+ (so things like --no-print-directory don't get broken);
+
+ -- wrap the list of options themselves when it's too long
+ (the wrapper.fill(opts) call below);
+
+ -- set the subsequent_indent when wrapping the help_text.
+ """
+ # The help for each option consists of two parts:
+ # * the opt strings and metavars
+ # eg. ("-x", or "-fFILENAME, --file=FILENAME")
+ # * the user-supplied help string
+ # eg. ("turn on expert mode", "read data from FILENAME")
+ #
+ # If possible, we write both of these on the same line:
+ # -x turn on expert mode
+ #
+ # But if the opt string list is too long, we put the help
+ # string on a second line, indented to the same column it would
+ # start in if it fit on the first line.
+ # -fFILENAME, --file=FILENAME
+ # read data from FILENAME
+ result = []
+
+ try:
+ opts = self.option_strings[option]
+ except AttributeError:
+ # The Python 2.3 version of optparse attaches this to
+ # to the option argument, not to this object.
+ opts = option.option_strings
+
+ opt_width = self.help_position - self.current_indent - 2
+ if len(opts) > opt_width:
+ wrapper = textwrap.TextWrapper(width=self.width,
+ initial_indent = ' ',
+ subsequent_indent = ' ')
+ wrapper.wordsep_re = no_hyphen_re
+ opts = wrapper.fill(opts) + '\n'
+ indent_first = self.help_position
+ else: # start help on same line as opts
+ opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
+ indent_first = 0
+ result.append(opts)
+ if option.help:
+
+ try:
+ expand_default = self.expand_default
+ except AttributeError:
+ # The HelpFormatter base class in the Python 2.3 version
+ # of optparse has no expand_default() method.
+ help_text = option.help
+ else:
+ help_text = expand_default(option)
+
+ # SCons: indent every line of the help text but the first.
+ wrapper = textwrap.TextWrapper(width=self.help_width,
+ subsequent_indent = ' ')
+ wrapper.wordsep_re = no_hyphen_re
+ help_lines = wrapper.wrap(help_text)
+ result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
+ for line in help_lines[1:]:
+ result.append("%*s%s\n" % (self.help_position, "", line))
+ elif opts[-1] != "\n":
+ result.append("\n")
+ return string.join(result, "")
+
+ # For consistent help output across Python versions, we provide a
+ # subclass copy of format_option_strings() and these two variables.
+ # This is necessary (?) for Python2.3, which otherwise concatenates
+ # a short option with its metavar.
+ _short_opt_fmt = "%s %s"
+ _long_opt_fmt = "%s=%s"
+
+ def format_option_strings(self, option):
+ """Return a comma-separated list of option strings & metavariables."""
+ if option.takes_value():
+ metavar = option.metavar or string.upper(option.dest)
+ short_opts = []
+ for sopt in option._short_opts:
+ short_opts.append(self._short_opt_fmt % (sopt, metavar))
+ long_opts = []
+ for lopt in option._long_opts:
+ long_opts.append(self._long_opt_fmt % (lopt, metavar))
+ else:
+ short_opts = option._short_opts
+ long_opts = option._long_opts
+
+ if self.short_first:
+ opts = short_opts + long_opts
+ else:
+ opts = long_opts + short_opts
+
+ return string.join(opts, ", ")
+
+def Parser(version):
+ """
+ Returns an options parser object initialized with the standard
+ SCons options.
+ """
+
+ formatter = SConsIndentedHelpFormatter(max_help_position=30)
+
+ op = SConsOptionParser(option_class=SConsOption,
+ add_help_option=False,
+ formatter=formatter,
+ usage="usage: scons [OPTION] [TARGET] ...",)
+
+ op.preserve_unknown_options = True
+ op.version = version
+
+ # Add the options to the parser we just created.
+ #
+ # These are in the order we want them to show up in the -H help
+ # text, basically alphabetical. Each op.add_option() call below
+ # should have a consistent format:
+ #
+ # op.add_option("-L", "--long-option-name",
+ # nargs=1, type="string",
+ # dest="long_option_name", default='foo',
+ # action="callback", callback=opt_long_option,
+ # help="help text goes here",
+ # metavar="VAR")
+ #
+ # Even though the optparse module constructs reasonable default
+ # destination names from the long option names, we're going to be
+ # explicit about each one for easier readability and so this code
+ # will at least show up when grepping the source for option attribute
+ # names, or otherwise browsing the source code.
+
+ # options ignored for compatibility
+ def opt_ignore(option, opt, value, parser):
+ sys.stderr.write("Warning: ignoring %s option\n" % opt)
+ op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w",
+ "--environment-overrides",
+ "--no-keep-going",
+ "--no-print-directory",
+ "--print-directory",
+ "--stop",
+ "--touch",
+ action="callback", callback=opt_ignore,
+ help="Ignored for compatibility.")
+
+ op.add_option('-c', '--clean', '--remove',
+ dest="clean", default=False,
+ action="store_true",
+ help="Remove specified targets and dependencies.")
+
+ op.add_option('-C', '--directory',
+ nargs=1, type="string",
+ dest="directory", default=[],
+ action="append",
+ help="Change to DIR before doing anything.",
+ metavar="DIR")
+
+ op.add_option('--cache-debug',
+ nargs=1,
+ dest="cache_debug", default=None,
+ action="store",
+ help="Print CacheDir debug info to FILE.",
+ metavar="FILE")
+
+ op.add_option('--cache-disable', '--no-cache',
+ dest='cache_disable', default=False,
+ action="store_true",
+ help="Do not retrieve built targets from CacheDir.")
+
+ op.add_option('--cache-force', '--cache-populate',
+ dest='cache_force', default=False,
+ action="store_true",
+ help="Copy already-built targets into the CacheDir.")
+
+ op.add_option('--cache-show',
+ dest='cache_show', default=False,
+ action="store_true",
+ help="Print build actions for files from CacheDir.")
+
+ config_options = ["auto", "force" ,"cache"]
+
+ def opt_config(option, opt, value, parser, c_options=config_options):
+ if not value in c_options:
+ raise OptionValueError("Warning: %s is not a valid config type" % value)
+ setattr(parser.values, option.dest, value)
+ opt_config_help = "Controls Configure subsystem: %s." \
+ % string.join(config_options, ", ")
+ op.add_option('--config',
+ nargs=1, type="string",
+ dest="config", default="auto",
+ action="callback", callback=opt_config,
+ help = opt_config_help,
+ metavar="MODE")
+
+ op.add_option('-D',
+ dest="climb_up", default=None,
+ action="store_const", const=2,
+ help="Search up directory tree for SConstruct, "
+ "build all Default() targets.")
+
+ deprecated_debug_options = {
+ "dtree" : '; please use --tree=derived instead',
+ "nomemoizer" : ' and has no effect',
+ "stree" : '; please use --tree=all,status instead',
+ "tree" : '; please use --tree=all instead',
+ }
+
+ debug_options = ["count", "explain", "findlibs",
+ "includes", "memoizer", "memory", "objects",
+ "pdb", "presub", "stacktrace",
+ "time"] + deprecated_debug_options.keys()
+
+ def opt_debug(option, opt, value, parser,
+ debug_options=debug_options,
+ deprecated_debug_options=deprecated_debug_options):
+ if value in debug_options:
+ parser.values.debug.append(value)
+ if value in deprecated_debug_options.keys():
+ try:
+ parser.values.delayed_warnings
+ except AttributeError:
+ parser.values.delayed_warnings = []
+ msg = deprecated_debug_options[value]
+ w = "The --debug=%s option is deprecated%s." % (value, msg)
+ t = (SCons.Warnings.DeprecatedWarning, w)
+ parser.values.delayed_warnings.append(t)
+ else:
+ raise OptionValueError("Warning: %s is not a valid debug type" % value)
+ opt_debug_help = "Print various types of debugging information: %s." \
+ % string.join(debug_options, ", ")
+ op.add_option('--debug',
+ nargs=1, type="string",
+ dest="debug", default=[],
+ action="callback", callback=opt_debug,
+ help=opt_debug_help,
+ metavar="TYPE")
+
+ def opt_diskcheck(option, opt, value, parser):
+ try:
+ diskcheck_value = diskcheck_convert(value)
+ except ValueError, e:
+ raise OptionValueError("Warning: `%s' is not a valid diskcheck type" % e)
+ setattr(parser.values, option.dest, diskcheck_value)
+
+ op.add_option('--diskcheck',
+ nargs=1, type="string",
+ dest='diskcheck', default=None,
+ action="callback", callback=opt_diskcheck,
+ help="Enable specific on-disk checks.",
+ metavar="TYPE")
+
+ def opt_duplicate(option, opt, value, parser):
+ if not value in SCons.Node.FS.Valid_Duplicates:
+ raise OptionValueError("`%s' is not a valid duplication style." % value)
+ setattr(parser.values, option.dest, value)
+ # Set the duplicate style right away so it can affect linking
+ # of SConscript files.
+ SCons.Node.FS.set_duplicate(value)
+
+ opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \
+ + string.join(SCons.Node.FS.Valid_Duplicates, ", ")
+
+ op.add_option('--duplicate',
+ nargs=1, type="string",
+ dest="duplicate", default='hard-soft-copy',
+ action="callback", callback=opt_duplicate,
+ help=opt_duplicate_help)
+
+ op.add_option('-f', '--file', '--makefile', '--sconstruct',
+ nargs=1, type="string",
+ dest="file", default=[],
+ action="append",
+ help="Read FILE as the top-level SConstruct file.")
+
+ op.add_option('-h', '--help',
+ dest="help", default=False,
+ action="store_true",
+ help="Print defined help message, or this one.")
+
+ op.add_option("-H", "--help-options",
+ action="help",
+ help="Print this message and exit.")
+
+ op.add_option('-i', '--ignore-errors',
+ dest='ignore_errors', default=False,
+ action="store_true",
+ help="Ignore errors from build actions.")
+
+ op.add_option('-I', '--include-dir',
+ nargs=1,
+ dest='include_dir', default=[],
+ action="append",
+ help="Search DIR for imported Python modules.",
+ metavar="DIR")
+
+ op.add_option('--implicit-cache',
+ dest='implicit_cache', default=False,
+ action="store_true",
+ help="Cache implicit dependencies")
+
+ def opt_implicit_deps(option, opt, value, parser):
+ setattr(parser.values, 'implicit_cache', True)
+ setattr(parser.values, option.dest, True)
+
+ op.add_option('--implicit-deps-changed',
+ dest="implicit_deps_changed", default=False,
+ action="callback", callback=opt_implicit_deps,
+ help="Ignore cached implicit dependencies.")
+
+ op.add_option('--implicit-deps-unchanged',
+ dest="implicit_deps_unchanged", default=False,
+ action="callback", callback=opt_implicit_deps,
+ help="Ignore changes in implicit dependencies.")
+
+ op.add_option('--interact', '--interactive',
+ dest='interactive', default=False,
+ action="store_true",
+ help="Run in interactive mode.")
+
+ op.add_option('-j', '--jobs',
+ nargs=1, type="int",
+ dest="num_jobs", default=1,
+ action="store",
+ help="Allow N jobs at once.",
+ metavar="N")
+
+ op.add_option('-k', '--keep-going',
+ dest='keep_going', default=False,
+ action="store_true",
+ help="Keep going when a target can't be made.")
+
+ op.add_option('--max-drift',
+ nargs=1, type="int",
+ dest='max_drift', default=SCons.Node.FS.default_max_drift,
+ action="store",
+ help="Set maximum system clock drift to N seconds.",
+ metavar="N")
+
+ op.add_option('--md5-chunksize',
+ nargs=1, type="int",
+ dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize,
+ action="store",
+ help="Set chunk-size for MD5 signature computation to N kilobytes.",
+ metavar="N")
+
+ op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon',
+ dest='no_exec', default=False,
+ action="store_true",
+ help="Don't build; just print commands.")
+
+ op.add_option('--no-site-dir',
+ dest='no_site_dir', default=False,
+ action="store_true",
+ help="Don't search or use the usual site_scons dir.")
+
+ op.add_option('--profile',
+ nargs=1,
+ dest="profile_file", default=None,
+ action="store",
+ help="Profile SCons and put results in FILE.",
+ metavar="FILE")
+
+ op.add_option('-q', '--question',
+ dest="question", default=False,
+ action="store_true",
+ help="Don't build; exit status says if up to date.")
+
+ op.add_option('-Q',
+ dest='no_progress', default=False,
+ action="store_true",
+ help="Suppress \"Reading/Building\" progress messages.")
+
+ op.add_option('--random',
+ dest="random", default=False,
+ action="store_true",
+ help="Build dependencies in random order.")
+
+ op.add_option('-s', '--silent', '--quiet',
+ dest="silent", default=False,
+ action="store_true",
+ help="Don't print commands.")
+
+ op.add_option('--site-dir',
+ nargs=1,
+ dest='site_dir', default=None,
+ action="store",
+ help="Use DIR instead of the usual site_scons dir.",
+ metavar="DIR")
+
+ op.add_option('--stack-size',
+ nargs=1, type="int",
+ dest='stack_size',
+ action="store",
+ help="Set the stack size of the threads used to run jobs to N kilobytes.",
+ metavar="N")
+
+ op.add_option('--taskmastertrace',
+ nargs=1,
+ dest="taskmastertrace_file", default=None,
+ action="store",
+ help="Trace Node evaluation to FILE.",
+ metavar="FILE")
+
+ tree_options = ["all", "derived", "prune", "status"]
+
+ def opt_tree(option, opt, value, parser, tree_options=tree_options):
+ import Main
+ tp = Main.TreePrinter()
+ for o in string.split(value, ','):
+ if o == 'all':
+ tp.derived = False
+ elif o == 'derived':
+ tp.derived = True
+ elif o == 'prune':
+ tp.prune = True
+ elif o == 'status':
+ tp.status = True
+ else:
+ raise OptionValueError("Warning: %s is not a valid --tree option" % o)
+ parser.values.tree_printers.append(tp)
+
+ opt_tree_help = "Print a dependency tree in various formats: %s." \
+ % string.join(tree_options, ", ")
+
+ op.add_option('--tree',
+ nargs=1, type="string",
+ dest="tree_printers", default=[],
+ action="callback", callback=opt_tree,
+ help=opt_tree_help,
+ metavar="OPTIONS")
+
+ op.add_option('-u', '--up', '--search-up',
+ dest="climb_up", default=0,
+ action="store_const", const=1,
+ help="Search up directory tree for SConstruct, "
+ "build targets at or below current directory.")
+
+ op.add_option('-U',
+ dest="climb_up", default=0,
+ action="store_const", const=3,
+ help="Search up directory tree for SConstruct, "
+ "build Default() targets from local SConscript.")
+
+ def opt_version(option, opt, value, parser):
+ sys.stdout.write(parser.version + '\n')
+ sys.exit(0)
+ op.add_option("-v", "--version",
+ action="callback", callback=opt_version,
+ help="Print the SCons version number and exit.")
+
+ def opt_warn(option, opt, value, parser, tree_options=tree_options):
+ if SCons.Util.is_String(value):
+ value = string.split(value, ',')
+ parser.values.warn.extend(value)
+
+ op.add_option('--warn', '--warning',
+ nargs=1, type="string",
+ dest="warn", default=[],
+ action="callback", callback=opt_warn,
+ help="Enable or disable warnings.",
+ metavar="WARNING-SPEC")
+
+ op.add_option('-Y', '--repository', '--srcdir',
+ nargs=1,
+ dest="repository", default=[],
+ action="append",
+ help="Search REPOSITORY for source and target files.")
+
+ # Options from Make and Cons classic that we do not yet support,
+ # but which we may support someday and whose (potential) meanings
+ # we don't want to change. These all get a "the -X option is not
+ # yet implemented" message and don't show up in the help output.
+
+ def opt_not_yet(option, opt, value, parser):
+ msg = "Warning: the %s option is not yet implemented\n" % opt
+ sys.stderr.write(msg)
+
+ op.add_option('-l', '--load-average', '--max-load',
+ nargs=1, type="int",
+ dest="load_average", default=0,
+ action="callback", callback=opt_not_yet,
+ # action="store",
+ # help="Don't start multiple jobs unless load is below "
+ # "LOAD-AVERAGE."
+ help=SUPPRESS_HELP)
+ op.add_option('--list-actions',
+ dest="list_actions",
+ action="callback", callback=opt_not_yet,
+ # help="Don't build; list files and build actions."
+ help=SUPPRESS_HELP)
+ op.add_option('--list-derived',
+ dest="list_derived",
+ action="callback", callback=opt_not_yet,
+ # help="Don't build; list files that would be built."
+ help=SUPPRESS_HELP)
+ op.add_option('--list-where',
+ dest="list_where",
+ action="callback", callback=opt_not_yet,
+ # help="Don't build; list files and where defined."
+ help=SUPPRESS_HELP)
+ op.add_option('-o', '--old-file', '--assume-old',
+ nargs=1, type="string",
+ dest="old_file", default=[],
+ action="callback", callback=opt_not_yet,
+ # action="append",
+ # help = "Consider FILE to be old; don't rebuild it."
+ help=SUPPRESS_HELP)
+ op.add_option('--override',
+ nargs=1, type="string",
+ action="callback", callback=opt_not_yet,
+ dest="override",
+ # help="Override variables as specified in FILE."
+ help=SUPPRESS_HELP)
+ op.add_option('-p',
+ action="callback", callback=opt_not_yet,
+ dest="p",
+ # help="Print internal environments/objects."
+ help=SUPPRESS_HELP)
+ op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables',
+ action="callback", callback=opt_not_yet,
+ dest="no_builtin_rules",
+ # help="Clear default environments and variables."
+ help=SUPPRESS_HELP)
+ op.add_option('--write-filenames',
+ nargs=1, type="string",
+ dest="write_filenames",
+ action="callback", callback=opt_not_yet,
+ # help="Write all filenames examined into FILE."
+ help=SUPPRESS_HELP)
+ op.add_option('-W', '--new-file', '--assume-new', '--what-if',
+ nargs=1, type="string",
+ dest="new_file",
+ action="callback", callback=opt_not_yet,
+ # help="Consider FILE to be changed."
+ help=SUPPRESS_HELP)
+ op.add_option('--warn-undefined-variables',
+ dest="warn_undefined_variables",
+ action="callback", callback=opt_not_yet,
+ # help="Warn when an undefined variable is referenced."
+ help=SUPPRESS_HELP)
+
+ return op
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
new file mode 100644
index 0000000..31c10ad
--- /dev/null
+++ b/src/engine/SCons/Script/SConscript.py
@@ -0,0 +1,642 @@
+"""SCons.Script.SConscript
+
+This module defines the Python API provided to SConscript and SConstruct
+files.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/SConscript.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Environment
+import SCons.Errors
+import SCons.Node
+import SCons.Node.Alias
+import SCons.Node.FS
+import SCons.Platform
+import SCons.SConf
+import SCons.Script.Main
+import SCons.Tool
+import SCons.Util
+
+import os
+import os.path
+import re
+import string
+import sys
+import traceback
+import types
+import UserList
+
+# The following variables used to live in this module. Some
+# SConscript files out there may have referred to them directly as
+# SCons.Script.SConscript.*. This is now supported by some special
+# handling towards the bottom of the SConscript.__init__.py module.
+#Arguments = {}
+#ArgList = []
+#BuildTargets = TargetList()
+#CommandLineTargets = []
+#DefaultTargets = []
+
+class SConscriptReturn(Exception):
+ pass
+
+launch_dir = os.path.abspath(os.curdir)
+
+GlobalDict = None
+
+# global exports set by Export():
+global_exports = {}
+
+# chdir flag
+sconscript_chdir = 1
+
+def get_calling_namespaces():
+ """Return the locals and globals for the function that called
+ into this module in the current call stack."""
+ try: 1/0
+ except ZeroDivisionError:
+ # Don't start iterating with the current stack-frame to
+ # prevent creating reference cycles (f_back is safe).
+ frame = sys.exc_info()[2].tb_frame.f_back
+
+ # Find the first frame that *isn't* from this file. This means
+ # that we expect all of the SCons frames that implement an Export()
+ # or SConscript() call to be in this file, so that we can identify
+ # the first non-Script.SConscript frame as the user's local calling
+ # environment, and the locals and globals dictionaries from that
+ # frame as the calling namespaces. See the comment below preceding
+ # the DefaultEnvironmentCall block for even more explanation.
+ while frame.f_globals.get("__name__") == __name__:
+ frame = frame.f_back
+
+ return frame.f_locals, frame.f_globals
+
+
+def compute_exports(exports):
+ """Compute a dictionary of exports given one of the parameters
+ to the Export() function or the exports argument to SConscript()."""
+
+ loc, glob = get_calling_namespaces()
+
+ retval = {}
+ try:
+ for export in exports:
+ if SCons.Util.is_Dict(export):
+ retval.update(export)
+ else:
+ try:
+ retval[export] = loc[export]
+ except KeyError:
+ retval[export] = glob[export]
+ except KeyError, x:
+ raise SCons.Errors.UserError, "Export of non-existent variable '%s'"%x
+
+ return retval
+
+class Frame:
+ """A frame on the SConstruct/SConscript call stack"""
+ def __init__(self, fs, exports, sconscript):
+ self.globals = BuildDefaultGlobals()
+ self.retval = None
+ self.prev_dir = fs.getcwd()
+ self.exports = compute_exports(exports) # exports from the calling SConscript
+ # make sure the sconscript attr is a Node.
+ if isinstance(sconscript, SCons.Node.Node):
+ self.sconscript = sconscript
+ elif sconscript == '-':
+ self.sconscript = None
+ else:
+ self.sconscript = fs.File(str(sconscript))
+
+# the SConstruct/SConscript call stack:
+call_stack = []
+
+# For documentation on the methods in this file, see the scons man-page
+
+def Return(*vars, **kw):
+ retval = []
+ try:
+ fvars = SCons.Util.flatten(vars)
+ for var in fvars:
+ for v in string.split(var):
+ retval.append(call_stack[-1].globals[v])
+ except KeyError, x:
+ raise SCons.Errors.UserError, "Return of non-existent variable '%s'"%x
+
+ if len(retval) == 1:
+ call_stack[-1].retval = retval[0]
+ else:
+ call_stack[-1].retval = tuple(retval)
+
+ stop = kw.get('stop', True)
+
+ if stop:
+ raise SConscriptReturn
+
+
+stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :)
+
+def _SConscript(fs, *files, **kw):
+ top = fs.Top
+ sd = fs.SConstruct_dir.rdir()
+ exports = kw.get('exports', [])
+
+ # evaluate each SConscript file
+ results = []
+ for fn in files:
+ call_stack.append(Frame(fs, exports, fn))
+ old_sys_path = sys.path
+ try:
+ SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1
+ if fn == "-":
+ exec sys.stdin in call_stack[-1].globals
+ else:
+ if isinstance(fn, SCons.Node.Node):
+ f = fn
+ else:
+ f = fs.File(str(fn))
+ _file_ = None
+
+ # Change directory to the top of the source
+ # tree to make sure the os's cwd and the cwd of
+ # fs match so we can open the SConscript.
+ fs.chdir(top, change_os_dir=1)
+ if f.rexists():
+ actual = f.rfile()
+ _file_ = open(actual.get_abspath(), "r")
+ elif f.srcnode().rexists():
+ actual = f.srcnode().rfile()
+ _file_ = open(actual.get_abspath(), "r")
+ elif f.has_src_builder():
+ # The SConscript file apparently exists in a source
+ # code management system. Build it, but then clear
+ # the builder so that it doesn't get built *again*
+ # during the actual build phase.
+ f.build()
+ f.built()
+ f.builder_set(None)
+ if f.exists():
+ _file_ = open(f.get_abspath(), "r")
+ if _file_:
+ # Chdir to the SConscript directory. Use a path
+ # name relative to the SConstruct file so that if
+ # we're using the -f option, we're essentially
+ # creating a parallel SConscript directory structure
+ # in our local directory tree.
+ #
+ # XXX This is broken for multiple-repository cases
+ # where the SConstruct and SConscript files might be
+ # in different Repositories. For now, cross that
+ # bridge when someone comes to it.
+ try:
+ src_dir = kw['src_dir']
+ except KeyError:
+ ldir = fs.Dir(f.dir.get_path(sd))
+ else:
+ ldir = fs.Dir(src_dir)
+ if not ldir.is_under(f.dir):
+ # They specified a source directory, but
+ # it's above the SConscript directory.
+ # Do the sensible thing and just use the
+ # SConcript directory.
+ ldir = fs.Dir(f.dir.get_path(sd))
+ try:
+ fs.chdir(ldir, change_os_dir=sconscript_chdir)
+ except OSError:
+ # There was no local directory, so we should be
+ # able to chdir to the Repository directory.
+ # Note that we do this directly, not through
+ # fs.chdir(), because we still need to
+ # interpret the stuff within the SConscript file
+ # relative to where we are logically.
+ fs.chdir(ldir, change_os_dir=0)
+ os.chdir(actual.dir.get_abspath())
+
+ # Append the SConscript directory to the beginning
+ # of sys.path so Python modules in the SConscript
+ # directory can be easily imported.
+ sys.path = [ f.dir.get_abspath() ] + sys.path
+
+ # This is the magic line that actually reads up
+ # and executes the stuff in the SConscript file.
+ # The locals for this frame contain the special
+ # bottom-of-the-stack marker so that any
+ # exceptions that occur when processing this
+ # SConscript can base the printed frames at this
+ # level and not show SCons internals as well.
+ call_stack[-1].globals.update({stack_bottom:1})
+ old_file = call_stack[-1].globals.get('__file__')
+ try:
+ del call_stack[-1].globals['__file__']
+ except KeyError:
+ pass
+ try:
+ try:
+ exec _file_ in call_stack[-1].globals
+ except SConscriptReturn:
+ pass
+ finally:
+ if old_file is not None:
+ call_stack[-1].globals.update({__file__:old_file})
+ else:
+ SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
+ "Ignoring missing SConscript '%s'" % f.path)
+
+ finally:
+ SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1
+ sys.path = old_sys_path
+ frame = call_stack.pop()
+ try:
+ fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir)
+ except OSError:
+ # There was no local directory, so chdir to the
+ # Repository directory. Like above, we do this
+ # directly.
+ fs.chdir(frame.prev_dir, change_os_dir=0)
+ rdir = frame.prev_dir.rdir()
+ rdir._create() # Make sure there's a directory there.
+ try:
+ os.chdir(rdir.get_abspath())
+ except OSError, e:
+ # We still couldn't chdir there, so raise the error,
+ # but only if actions are being executed.
+ #
+ # If the -n option was used, the directory would *not*
+ # have been created and we should just carry on and
+ # let things muddle through. This isn't guaranteed
+ # to work if the SConscript files are reading things
+ # from disk (for example), but it should work well
+ # enough for most configurations.
+ if SCons.Action.execute_actions:
+ raise e
+
+ results.append(frame.retval)
+
+ # if we only have one script, don't return a tuple
+ if len(results) == 1:
+ return results[0]
+ else:
+ return tuple(results)
+
+def SConscript_exception(file=sys.stderr):
+ """Print an exception stack trace just for the SConscript file(s).
+ This will show users who have Python errors where the problem is,
+ without cluttering the output with all of the internal calls leading
+ up to where we exec the SConscript."""
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ tb = exc_tb
+ while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
+ tb = tb.tb_next
+ if not tb:
+ # We did not find our exec statement, so this was actually a bug
+ # in SCons itself. Show the whole stack.
+ tb = exc_tb
+ stack = traceback.extract_tb(tb)
+ try:
+ type = exc_type.__name__
+ except AttributeError:
+ type = str(exc_type)
+ if type[:11] == "exceptions.":
+ type = type[11:]
+ file.write('%s: %s:\n' % (type, exc_value))
+ for fname, line, func, text in stack:
+ file.write(' File "%s", line %d:\n' % (fname, line))
+ file.write(' %s\n' % text)
+
+def annotate(node):
+ """Annotate a node with the stack frame describing the
+ SConscript file and line number that created it."""
+ tb = sys.exc_info()[2]
+ while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
+ tb = tb.tb_next
+ if not tb:
+ # We did not find any exec of an SConscript file: what?!
+ raise SCons.Errors.InternalError, "could not find SConscript stack frame"
+ node.creator = traceback.extract_stack(tb)[0]
+
+# The following line would cause each Node to be annotated using the
+# above function. Unfortunately, this is a *huge* performance hit, so
+# leave this disabled until we find a more efficient mechanism.
+#SCons.Node.Annotate = annotate
+
+class SConsEnvironment(SCons.Environment.Base):
+ """An Environment subclass that contains all of the methods that
+ are particular to the wrapper SCons interface and which aren't
+ (or shouldn't be) part of the build engine itself.
+
+ Note that not all of the methods of this class have corresponding
+ global functions, there are some private methods.
+ """
+
+ #
+ # Private methods of an SConsEnvironment.
+ #
+ def _exceeds_version(self, major, minor, v_major, v_minor):
+ """Return 1 if 'major' and 'minor' are greater than the version
+ in 'v_major' and 'v_minor', and 0 otherwise."""
+ return (major > v_major or (major == v_major and minor > v_minor))
+
+ def _get_major_minor_revision(self, version_string):
+ """Split a version string into major, minor and (optionally)
+ revision parts.
+
+ This is complicated by the fact that a version string can be
+ something like 3.2b1."""
+ version = string.split(string.split(version_string, ' ')[0], '.')
+ v_major = int(version[0])
+ v_minor = int(re.match('\d+', version[1]).group())
+ if len(version) >= 3:
+ v_revision = int(re.match('\d+', version[2]).group())
+ else:
+ v_revision = 0
+ return v_major, v_minor, v_revision
+
+ def _get_SConscript_filenames(self, ls, kw):
+ """
+ Convert the parameters passed to # SConscript() calls into a list
+ of files and export variables. If the parameters are invalid,
+ throws SCons.Errors.UserError. Returns a tuple (l, e) where l
+ is a list of SConscript filenames and e is a list of exports.
+ """
+ exports = []
+
+ if len(ls) == 0:
+ try:
+ dirs = kw["dirs"]
+ except KeyError:
+ raise SCons.Errors.UserError, \
+ "Invalid SConscript usage - no parameters"
+
+ if not SCons.Util.is_List(dirs):
+ dirs = [ dirs ]
+ dirs = map(str, dirs)
+
+ name = kw.get('name', 'SConscript')
+
+ files = map(lambda n, name = name: os.path.join(n, name), dirs)
+
+ elif len(ls) == 1:
+
+ files = ls[0]
+
+ elif len(ls) == 2:
+
+ files = ls[0]
+ exports = self.Split(ls[1])
+
+ else:
+
+ raise SCons.Errors.UserError, \
+ "Invalid SConscript() usage - too many arguments"
+
+ if not SCons.Util.is_List(files):
+ files = [ files ]
+
+ if kw.get('exports'):
+ exports.extend(self.Split(kw['exports']))
+
+ variant_dir = kw.get('variant_dir') or kw.get('build_dir')
+ if variant_dir:
+ if len(files) != 1:
+ raise SCons.Errors.UserError, \
+ "Invalid SConscript() usage - can only specify one SConscript with a variant_dir"
+ duplicate = kw.get('duplicate', 1)
+ src_dir = kw.get('src_dir')
+ if not src_dir:
+ src_dir, fname = os.path.split(str(files[0]))
+ files = [os.path.join(str(variant_dir), fname)]
+ else:
+ if not isinstance(src_dir, SCons.Node.Node):
+ src_dir = self.fs.Dir(src_dir)
+ fn = files[0]
+ if not isinstance(fn, SCons.Node.Node):
+ fn = self.fs.File(fn)
+ if fn.is_under(src_dir):
+ # Get path relative to the source directory.
+ fname = fn.get_path(src_dir)
+ files = [os.path.join(str(variant_dir), fname)]
+ else:
+ files = [fn.abspath]
+ kw['src_dir'] = variant_dir
+ self.fs.VariantDir(variant_dir, src_dir, duplicate)
+
+ return (files, exports)
+
+ #
+ # Public methods of an SConsEnvironment. These get
+ # entry points in the global name space so they can be called
+ # as global functions.
+ #
+
+ def Configure(self, *args, **kw):
+ if not SCons.Script.sconscript_reading:
+ raise SCons.Errors.UserError, "Calling Configure from Builders is not supported."
+ kw['_depth'] = kw.get('_depth', 0) + 1
+ return apply(SCons.Environment.Base.Configure, (self,)+args, kw)
+
+ def Default(self, *targets):
+ SCons.Script._Set_Default_Targets(self, targets)
+
+ def EnsureSConsVersion(self, major, minor, revision=0):
+ """Exit abnormally if the SCons version is not late enough."""
+ scons_ver = self._get_major_minor_revision(SCons.__version__)
+ if scons_ver < (major, minor, revision):
+ if revision:
+ scons_ver_string = '%d.%d.%d' % (major, minor, revision)
+ else:
+ scons_ver_string = '%d.%d' % (major, minor)
+ print "SCons %s or greater required, but you have SCons %s" % \
+ (scons_ver_string, SCons.__version__)
+ sys.exit(2)
+
+ def EnsurePythonVersion(self, major, minor):
+ """Exit abnormally if the Python version is not late enough."""
+ try:
+ v_major, v_minor, v_micro, release, serial = sys.version_info
+ python_ver = (v_major, v_minor)
+ except AttributeError:
+ python_ver = self._get_major_minor_revision(sys.version)[:2]
+ if python_ver < (major, minor):
+ v = string.split(sys.version, " ", 1)[0]
+ print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v)
+ sys.exit(2)
+
+ def Exit(self, value=0):
+ sys.exit(value)
+
+ def Export(self, *vars, **kw):
+ for var in vars:
+ global_exports.update(compute_exports(self.Split(var)))
+ global_exports.update(kw)
+
+ def GetLaunchDir(self):
+ global launch_dir
+ return launch_dir
+
+ def GetOption(self, name):
+ name = self.subst(name)
+ return SCons.Script.Main.GetOption(name)
+
+ def Help(self, text):
+ text = self.subst(text, raw=1)
+ SCons.Script.HelpFunction(text)
+
+ def Import(self, *vars):
+ try:
+ frame = call_stack[-1]
+ globals = frame.globals
+ exports = frame.exports
+ for var in vars:
+ var = self.Split(var)
+ for v in var:
+ if v == '*':
+ globals.update(global_exports)
+ globals.update(exports)
+ else:
+ if exports.has_key(v):
+ globals[v] = exports[v]
+ else:
+ globals[v] = global_exports[v]
+ except KeyError,x:
+ raise SCons.Errors.UserError, "Import of non-existent variable '%s'"%x
+
+ def SConscript(self, *ls, **kw):
+ def subst_element(x, subst=self.subst):
+ if SCons.Util.is_List(x):
+ x = map(subst, x)
+ else:
+ x = subst(x)
+ return x
+ ls = map(subst_element, ls)
+ subst_kw = {}
+ for key, val in kw.items():
+ if SCons.Util.is_String(val):
+ val = self.subst(val)
+ elif SCons.Util.is_List(val):
+ result = []
+ for v in val:
+ if SCons.Util.is_String(v):
+ v = self.subst(v)
+ result.append(v)
+ val = result
+ subst_kw[key] = val
+
+ files, exports = self._get_SConscript_filenames(ls, subst_kw)
+ subst_kw['exports'] = exports
+ return apply(_SConscript, [self.fs,] + files, subst_kw)
+
+ def SConscriptChdir(self, flag):
+ global sconscript_chdir
+ sconscript_chdir = flag
+
+ def SetOption(self, name, value):
+ name = self.subst(name)
+ SCons.Script.Main.SetOption(name, value)
+
+#
+#
+#
+SCons.Environment.Environment = SConsEnvironment
+
+def Configure(*args, **kw):
+ if not SCons.Script.sconscript_reading:
+ raise SCons.Errors.UserError, "Calling Configure from Builders is not supported."
+ kw['_depth'] = 1
+ return apply(SCons.SConf.SConf, args, kw)
+
+# It's very important that the DefaultEnvironmentCall() class stay in this
+# file, with the get_calling_namespaces() function, the compute_exports()
+# function, the Frame class and the SConsEnvironment.Export() method.
+# These things make up the calling stack leading up to the actual global
+# Export() or SConscript() call that the user issued. We want to allow
+# users to export local variables that they define, like so:
+#
+# def func():
+# x = 1
+# Export('x')
+#
+# To support this, the get_calling_namespaces() function assumes that
+# the *first* stack frame that's not from this file is the local frame
+# for the Export() or SConscript() call.
+
+_DefaultEnvironmentProxy = None
+
+def get_DefaultEnvironmentProxy():
+ global _DefaultEnvironmentProxy
+ if not _DefaultEnvironmentProxy:
+ default_env = SCons.Defaults.DefaultEnvironment()
+ _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env)
+ return _DefaultEnvironmentProxy
+
+class DefaultEnvironmentCall:
+ """A class that implements "global function" calls of
+ Environment methods by fetching the specified method from the
+ DefaultEnvironment's class. Note that this uses an intermediate
+ proxy class instead of calling the DefaultEnvironment method
+ directly so that the proxy can override the subst() method and
+ thereby prevent expansion of construction variables (since from
+ the user's point of view this was called as a global function,
+ with no associated construction environment)."""
+ def __init__(self, method_name, subst=0):
+ self.method_name = method_name
+ if subst:
+ self.factory = SCons.Defaults.DefaultEnvironment
+ else:
+ self.factory = get_DefaultEnvironmentProxy
+ def __call__(self, *args, **kw):
+ env = self.factory()
+ method = getattr(env, self.method_name)
+ return apply(method, args, kw)
+
+
+def BuildDefaultGlobals():
+ """
+ Create a dictionary containing all the default globals for
+ SConstruct and SConscript files.
+ """
+
+ global GlobalDict
+ if GlobalDict is None:
+ GlobalDict = {}
+
+ import SCons.Script
+ d = SCons.Script.__dict__
+ def not_a_module(m, d=d, mtype=type(SCons.Script)):
+ return type(d[m]) != mtype
+ for m in filter(not_a_module, dir(SCons.Script)):
+ GlobalDict[m] = d[m]
+
+ return GlobalDict.copy()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/SConscriptTests.py b/src/engine/SCons/Script/SConscriptTests.py
new file mode 100644
index 0000000..0958dc9
--- /dev/null
+++ b/src/engine/SCons/Script/SConscriptTests.py
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/SConscriptTests.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Script.SConscript
+
+# all of the SConscript.py tests are in test/SConscript.py
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
new file mode 100644
index 0000000..1e535ab
--- /dev/null
+++ b/src/engine/SCons/Script/__init__.py
@@ -0,0 +1,414 @@
+"""SCons.Script
+
+This file implements the main() function used by the scons script.
+
+Architecturally, this *is* the scons script, and will likely only be
+called from the external "scons" wrapper. Consequently, anything here
+should not be, or be considered, part of the build engine. If it's
+something that we expect other software to want to use, it should go in
+some other module. If it's specific to the "scons" script invocation,
+it goes here.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Script/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import time
+start_time = time.time()
+
+import os
+import string
+import sys
+import UserList
+
+# Special chicken-and-egg handling of the "--debug=memoizer" flag:
+#
+# SCons.Memoize contains a metaclass implementation that affects how
+# the other classes are instantiated. The Memoizer may add shim methods
+# to classes that have methods that cache computed values in order to
+# count and report the hits and misses.
+#
+# If we wait to enable the Memoization until after we've parsed the
+# command line options normally, it will be too late, because the Memoizer
+# will have already analyzed the classes that it's Memoizing and decided
+# to not add the shims. So we use a special-case, up-front check for
+# the "--debug=memoizer" flag and enable Memoizer before we import any
+# of the other modules that use it.
+
+_args = sys.argv + string.split(os.environ.get('SCONSFLAGS', ''))
+if "--debug=memoizer" in _args:
+ import SCons.Memoize
+ import SCons.Warnings
+ try:
+ SCons.Memoize.EnableMemoization()
+ except SCons.Warnings.Warning:
+ # Some warning was thrown (inability to --debug=memoizer on
+ # Python 1.5.2 because it doesn't have metaclasses). Arrange
+ # for it to be displayed or not after warnings are configured.
+ import Main
+ exc_type, exc_value, tb = sys.exc_info()
+ Main.delayed_warnings.append((exc_type, exc_value))
+del _args
+
+import SCons.Action
+import SCons.Builder
+import SCons.Environment
+import SCons.Node.FS
+import SCons.Options
+import SCons.Platform
+import SCons.Scanner
+import SCons.SConf
+import SCons.Subst
+import SCons.Tool
+import SCons.Util
+import SCons.Variables
+import SCons.Defaults
+
+import Main
+
+main = Main.main
+
+# The following are global class definitions and variables that used to
+# live directly in this module back before 0.96.90, when it contained
+# a lot of code. Some SConscript files in widely-distributed packages
+# (Blender is the specific example) actually reached into SCons.Script
+# directly to use some of these. Rather than break those SConscript
+# files, we're going to propagate these names into the SCons.Script
+# namespace here.
+#
+# Some of these are commented out because it's *really* unlikely anyone
+# used them, but we're going to leave the comment here to try to make
+# it obvious what to do if the situation arises.
+BuildTask = Main.BuildTask
+CleanTask = Main.CleanTask
+QuestionTask = Main.QuestionTask
+#PrintHelp = Main.PrintHelp
+#SConscriptSettableOptions = Main.SConscriptSettableOptions
+
+AddOption = Main.AddOption
+GetOption = Main.GetOption
+SetOption = Main.SetOption
+Progress = Main.Progress
+GetBuildFailures = Main.GetBuildFailures
+
+#keep_going_on_error = Main.keep_going_on_error
+#print_dtree = Main.print_dtree
+#print_explanations = Main.print_explanations
+#print_includes = Main.print_includes
+#print_objects = Main.print_objects
+#print_time = Main.print_time
+#print_tree = Main.print_tree
+#memory_stats = Main.memory_stats
+#ignore_errors = Main.ignore_errors
+#sconscript_time = Main.sconscript_time
+#command_time = Main.command_time
+#exit_status = Main.exit_status
+#profiling = Main.profiling
+#repositories = Main.repositories
+
+#
+import SConscript
+_SConscript = SConscript
+
+call_stack = _SConscript.call_stack
+
+#
+Action = SCons.Action.Action
+AddMethod = SCons.Util.AddMethod
+AllowSubstExceptions = SCons.Subst.SetAllowableExceptions
+Builder = SCons.Builder.Builder
+Configure = _SConscript.Configure
+Environment = SCons.Environment.Environment
+#OptParser = SCons.SConsOptions.OptParser
+FindPathDirs = SCons.Scanner.FindPathDirs
+Platform = SCons.Platform.Platform
+Return = _SConscript.Return
+Scanner = SCons.Scanner.Base
+Tool = SCons.Tool.Tool
+WhereIs = SCons.Util.WhereIs
+
+#
+BoolVariable = SCons.Variables.BoolVariable
+EnumVariable = SCons.Variables.EnumVariable
+ListVariable = SCons.Variables.ListVariable
+PackageVariable = SCons.Variables.PackageVariable
+PathVariable = SCons.Variables.PathVariable
+
+# Deprecated names that will go away some day.
+BoolOption = SCons.Options.BoolOption
+EnumOption = SCons.Options.EnumOption
+ListOption = SCons.Options.ListOption
+PackageOption = SCons.Options.PackageOption
+PathOption = SCons.Options.PathOption
+
+# Action factories.
+Chmod = SCons.Defaults.Chmod
+Copy = SCons.Defaults.Copy
+Delete = SCons.Defaults.Delete
+Mkdir = SCons.Defaults.Mkdir
+Move = SCons.Defaults.Move
+Touch = SCons.Defaults.Touch
+
+# Pre-made, public scanners.
+CScanner = SCons.Tool.CScanner
+DScanner = SCons.Tool.DScanner
+DirScanner = SCons.Defaults.DirScanner
+ProgramScanner = SCons.Tool.ProgramScanner
+SourceFileScanner = SCons.Tool.SourceFileScanner
+
+# Functions we might still convert to Environment methods.
+CScan = SCons.Defaults.CScan
+DefaultEnvironment = SCons.Defaults.DefaultEnvironment
+
+# Other variables we provide.
+class TargetList(UserList.UserList):
+ def _do_nothing(self, *args, **kw):
+ pass
+ def _add_Default(self, list):
+ self.extend(list)
+ def _clear(self):
+ del self[:]
+
+ARGUMENTS = {}
+ARGLIST = []
+BUILD_TARGETS = TargetList()
+COMMAND_LINE_TARGETS = []
+DEFAULT_TARGETS = []
+
+# BUILD_TARGETS can be modified in the SConscript files. If so, we
+# want to treat the modified BUILD_TARGETS list as if they specified
+# targets on the command line. To do that, though, we need to know if
+# BUILD_TARGETS was modified through "official" APIs or by hand. We do
+# this by updating two lists in parallel, the documented BUILD_TARGETS
+# list, above, and this internal _build_plus_default targets list which
+# should only have "official" API changes. Then Script/Main.py can
+# compare these two afterwards to figure out if the user added their
+# own targets to BUILD_TARGETS.
+_build_plus_default = TargetList()
+
+def _Add_Arguments(alist):
+ for arg in alist:
+ a, b = string.split(arg, '=', 1)
+ ARGUMENTS[a] = b
+ ARGLIST.append((a, b))
+
+def _Add_Targets(tlist):
+ if tlist:
+ COMMAND_LINE_TARGETS.extend(tlist)
+ BUILD_TARGETS.extend(tlist)
+ BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing
+ BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing
+ _build_plus_default.extend(tlist)
+ _build_plus_default._add_Default = _build_plus_default._do_nothing
+ _build_plus_default._clear = _build_plus_default._do_nothing
+
+def _Set_Default_Targets_Has_Been_Called(d, fs):
+ return DEFAULT_TARGETS
+
+def _Set_Default_Targets_Has_Not_Been_Called(d, fs):
+ if d is None:
+ d = [fs.Dir('.')]
+ return d
+
+_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called
+
+def _Set_Default_Targets(env, tlist):
+ global DEFAULT_TARGETS
+ global _Get_Default_Targets
+ _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called
+ for t in tlist:
+ if t is None:
+ # Delete the elements from the list in-place, don't
+ # reassign an empty list to DEFAULT_TARGETS, so that the
+ # variables will still point to the same object we point to.
+ del DEFAULT_TARGETS[:]
+ BUILD_TARGETS._clear()
+ _build_plus_default._clear()
+ elif isinstance(t, SCons.Node.Node):
+ DEFAULT_TARGETS.append(t)
+ BUILD_TARGETS._add_Default([t])
+ _build_plus_default._add_Default([t])
+ else:
+ nodes = env.arg2nodes(t, env.fs.Entry)
+ DEFAULT_TARGETS.extend(nodes)
+ BUILD_TARGETS._add_Default(nodes)
+ _build_plus_default._add_Default(nodes)
+
+#
+help_text = None
+
+def HelpFunction(text):
+ global help_text
+ if SCons.Script.help_text is None:
+ SCons.Script.help_text = text
+ else:
+ help_text = help_text + text
+
+#
+# Will be non-zero if we are reading an SConscript file.
+sconscript_reading = 0
+
+#
+def Variables(files=[], args=ARGUMENTS):
+ return SCons.Variables.Variables(files, args)
+
+def Options(files=[], args=ARGUMENTS):
+ return SCons.Options.Options(files, args)
+
+# The list of global functions to add to the SConscript name space
+# that end up calling corresponding methods or Builders in the
+# DefaultEnvironment().
+GlobalDefaultEnvironmentFunctions = [
+ # Methods from the SConsEnvironment class, above.
+ 'Default',
+ 'EnsurePythonVersion',
+ 'EnsureSConsVersion',
+ 'Exit',
+ 'Export',
+ 'GetLaunchDir',
+ 'Help',
+ 'Import',
+ #'SConscript', is handled separately, below.
+ 'SConscriptChdir',
+
+ # Methods from the Environment.Base class.
+ 'AddPostAction',
+ 'AddPreAction',
+ 'Alias',
+ 'AlwaysBuild',
+ 'BuildDir',
+ 'CacheDir',
+ 'Clean',
+ #The Command() method is handled separately, below.
+ 'Decider',
+ 'Depends',
+ 'Dir',
+ 'NoClean',
+ 'NoCache',
+ 'Entry',
+ 'Execute',
+ 'File',
+ 'FindFile',
+ 'FindInstalledFiles',
+ 'FindSourceFiles',
+ 'Flatten',
+ 'GetBuildPath',
+ 'Glob',
+ 'Ignore',
+ 'Install',
+ 'InstallAs',
+ 'Literal',
+ 'Local',
+ 'ParseDepends',
+ 'Precious',
+ 'Repository',
+ 'Requires',
+ 'SConsignFile',
+ 'SideEffect',
+ 'SourceCode',
+ 'SourceSignatures',
+ 'Split',
+ 'Tag',
+ 'TargetSignatures',
+ 'Value',
+ 'VariantDir',
+]
+
+GlobalDefaultBuilders = [
+ # Supported builders.
+ 'CFile',
+ 'CXXFile',
+ 'DVI',
+ 'Jar',
+ 'Java',
+ 'JavaH',
+ 'Library',
+ 'M4',
+ 'MSVSProject',
+ 'Object',
+ 'PCH',
+ 'PDF',
+ 'PostScript',
+ 'Program',
+ 'RES',
+ 'RMIC',
+ 'SharedLibrary',
+ 'SharedObject',
+ 'StaticLibrary',
+ 'StaticObject',
+ 'Tar',
+ 'TypeLibrary',
+ 'Zip',
+ 'Package',
+]
+
+for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
+ exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name))
+del name
+
+# There are a handful of variables that used to live in the
+# Script/SConscript.py module that some SConscript files out there were
+# accessing directly as SCons.Script.SConscript.*. The problem is that
+# "SConscript" in this namespace is no longer a module, it's a global
+# function call--or more precisely, an object that implements a global
+# function call through the default Environment. Nevertheless, we can
+# maintain backwards compatibility for SConscripts that were reaching in
+# this way by hanging some attributes off the "SConscript" object here.
+SConscript = _SConscript.DefaultEnvironmentCall('SConscript')
+
+# Make SConscript look enough like the module it used to be so
+# that pychecker doesn't barf.
+SConscript.__name__ = 'SConscript'
+
+SConscript.Arguments = ARGUMENTS
+SConscript.ArgList = ARGLIST
+SConscript.BuildTargets = BUILD_TARGETS
+SConscript.CommandLineTargets = COMMAND_LINE_TARGETS
+SConscript.DefaultTargets = DEFAULT_TARGETS
+
+# The global Command() function must be handled differently than the
+# global functions for other construction environment methods because
+# we want people to be able to use Actions that must expand $TARGET
+# and $SOURCE later, when (and if) the Action is invoked to build
+# the target(s). We do this with the subst=1 argument, which creates
+# a DefaultEnvironmentCall instance that wraps up a normal default
+# construction environment that performs variable substitution, not a
+# proxy that doesn't.
+#
+# There's a flaw here, though, because any other $-variables on a command
+# line will *also* be expanded, each to a null string, but that should
+# only be a problem in the unusual case where someone was passing a '$'
+# on a command line and *expected* the $ to get through to the shell
+# because they were calling Command() and not env.Command()... This is
+# unlikely enough that we're going to leave this as is and cross that
+# bridge if someone actually comes to it.
+Command = _SConscript.DefaultEnvironmentCall('Command', subst=1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Sig.py b/src/engine/SCons/Sig.py
new file mode 100644
index 0000000..f42b473
--- /dev/null
+++ b/src/engine/SCons/Sig.py
@@ -0,0 +1,63 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Sig.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Place-holder for the old SCons.Sig module hierarchy
+
+This is no longer used, but code out there (such as the NSIS module on
+the SCons wiki) may try to import SCons.Sig. If so, we generate a warning
+that points them to the line that caused the import, and don't die.
+
+If someone actually tried to use the sub-modules or functions within
+the package (for example, SCons.Sig.MD5.signature()), then they'll still
+get an AttributeError, but at least they'll know where to start looking.
+"""
+
+import SCons.Util
+import SCons.Warnings
+
+msg = 'The SCons.Sig module no longer exists.\n' \
+ ' Remove the following "import SCons.Sig" line to eliminate this warning:'
+
+SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, msg)
+
+default_calc = None
+default_module = None
+
+class MD5Null(SCons.Util.Null):
+ def __repr__(self):
+ return "MD5Null()"
+
+class TimeStampNull(SCons.Util.Null):
+ def __repr__(self):
+ return "TimeStampNull()"
+
+MD5 = MD5Null()
+TimeStamp = TimeStampNull()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py
new file mode 100644
index 0000000..fe644a5
--- /dev/null
+++ b/src/engine/SCons/Subst.py
@@ -0,0 +1,911 @@
+"""SCons.Subst
+
+SCons string substitution.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Subst.py 4577 2009/12/27 19:44:43 scons"
+
+import re
+import string
+import types
+import UserList
+import UserString
+
+import SCons.Errors
+
+from SCons.Util import is_String, is_Sequence
+
+# Indexed by the SUBST_* constants below.
+_strconv = [SCons.Util.to_String_for_subst,
+ SCons.Util.to_String_for_subst,
+ SCons.Util.to_String_for_signature]
+
+
+
+AllowableExceptions = (IndexError, NameError)
+
+def SetAllowableExceptions(*excepts):
+ global AllowableExceptions
+ AllowableExceptions = filter(None, excepts)
+
+def raise_exception(exception, target, s):
+ name = exception.__class__.__name__
+ msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s)
+ if target:
+ raise SCons.Errors.BuildError, (target[0], msg)
+ else:
+ raise SCons.Errors.UserError, msg
+
+
+
+class Literal:
+ """A wrapper for a string. If you use this object wrapped
+ around a string, then it will be interpreted as literal.
+ When passed to the command interpreter, all special
+ characters will be escaped."""
+ def __init__(self, lstr):
+ self.lstr = lstr
+
+ def __str__(self):
+ return self.lstr
+
+ def escape(self, escape_func):
+ return escape_func(self.lstr)
+
+ def for_signature(self):
+ return self.lstr
+
+ def is_literal(self):
+ return 1
+
+class SpecialAttrWrapper:
+ """This is a wrapper for what we call a 'Node special attribute.'
+ This is any of the attributes of a Node that we can reference from
+ Environment variable substitution, such as $TARGET.abspath or
+ $SOURCES[1].filebase. We implement the same methods as Literal
+ so we can handle special characters, plus a for_signature method,
+ such that we can return some canonical string during signature
+ calculation to avoid unnecessary rebuilds."""
+
+ def __init__(self, lstr, for_signature=None):
+ """The for_signature parameter, if supplied, will be the
+ canonical string we return from for_signature(). Else
+ we will simply return lstr."""
+ self.lstr = lstr
+ if for_signature:
+ self.forsig = for_signature
+ else:
+ self.forsig = lstr
+
+ def __str__(self):
+ return self.lstr
+
+ def escape(self, escape_func):
+ return escape_func(self.lstr)
+
+ def for_signature(self):
+ return self.forsig
+
+ def is_literal(self):
+ return 1
+
+def quote_spaces(arg):
+ """Generic function for putting double quotes around any string that
+ has white space in it."""
+ if ' ' in arg or '\t' in arg:
+ return '"%s"' % arg
+ else:
+ return str(arg)
+
+class CmdStringHolder(UserString.UserString):
+ """This is a special class used to hold strings generated by
+ scons_subst() and scons_subst_list(). It defines a special method
+ escape(). When passed a function with an escape algorithm for a
+ particular platform, it will return the contained string with the
+ proper escape sequences inserted.
+ """
+ def __init__(self, cmd, literal=None):
+ UserString.UserString.__init__(self, cmd)
+ self.literal = literal
+
+ def is_literal(self):
+ return self.literal
+
+ def escape(self, escape_func, quote_func=quote_spaces):
+ """Escape the string with the supplied function. The
+ function is expected to take an arbitrary string, then
+ return it with all special characters escaped and ready
+ for passing to the command interpreter.
+
+ After calling this function, the next call to str() will
+ return the escaped string.
+ """
+
+ if self.is_literal():
+ return escape_func(self.data)
+ elif ' ' in self.data or '\t' in self.data:
+ return quote_func(self.data)
+ else:
+ return self.data
+
+def escape_list(list, escape_func):
+ """Escape a list of arguments by running the specified escape_func
+ on every object in the list that has an escape() method."""
+ def escape(obj, escape_func=escape_func):
+ try:
+ e = obj.escape
+ except AttributeError:
+ return obj
+ else:
+ return e(escape_func)
+ return map(escape, list)
+
+class NLWrapper:
+ """A wrapper class that delays turning a list of sources or targets
+ into a NodeList until it's needed. The specified function supplied
+ when the object is initialized is responsible for turning raw nodes
+ into proxies that implement the special attributes like .abspath,
+ .source, etc. This way, we avoid creating those proxies just
+ "in case" someone is going to use $TARGET or the like, and only
+ go through the trouble if we really have to.
+
+ In practice, this might be a wash performance-wise, but it's a little
+ cleaner conceptually...
+ """
+
+ def __init__(self, list, func):
+ self.list = list
+ self.func = func
+ def _return_nodelist(self):
+ return self.nodelist
+ def _gen_nodelist(self):
+ list = self.list
+ if list is None:
+ list = []
+ elif not is_Sequence(list):
+ list = [list]
+ # The map(self.func) call is what actually turns
+ # a list into appropriate proxies.
+ self.nodelist = SCons.Util.NodeList(map(self.func, list))
+ self._create_nodelist = self._return_nodelist
+ return self.nodelist
+ _create_nodelist = _gen_nodelist
+
+
+class Targets_or_Sources(UserList.UserList):
+ """A class that implements $TARGETS or $SOURCES expansions by in turn
+ wrapping a NLWrapper. This class handles the different methods used
+ to access the list, calling the NLWrapper to create proxies on demand.
+
+ Note that we subclass UserList.UserList purely so that the
+ is_Sequence() function will identify an object of this class as
+ a list during variable expansion. We're not really using any
+ UserList.UserList methods in practice.
+ """
+ def __init__(self, nl):
+ self.nl = nl
+ def __getattr__(self, attr):
+ nl = self.nl._create_nodelist()
+ return getattr(nl, attr)
+ def __getitem__(self, i):
+ nl = self.nl._create_nodelist()
+ return nl[i]
+ def __getslice__(self, i, j):
+ nl = self.nl._create_nodelist()
+ i = max(i, 0); j = max(j, 0)
+ return nl[i:j]
+ def __str__(self):
+ nl = self.nl._create_nodelist()
+ return str(nl)
+ def __repr__(self):
+ nl = self.nl._create_nodelist()
+ return repr(nl)
+
+class Target_or_Source:
+ """A class that implements $TARGET or $SOURCE expansions by in turn
+ wrapping a NLWrapper. This class handles the different methods used
+ to access an individual proxy Node, calling the NLWrapper to create
+ a proxy on demand.
+ """
+ def __init__(self, nl):
+ self.nl = nl
+ def __getattr__(self, attr):
+ nl = self.nl._create_nodelist()
+ try:
+ nl0 = nl[0]
+ except IndexError:
+ # If there is nothing in the list, then we have no attributes to
+ # pass through, so raise AttributeError for everything.
+ raise AttributeError, "NodeList has no attribute: %s" % attr
+ return getattr(nl0, attr)
+ def __str__(self):
+ nl = self.nl._create_nodelist()
+ if nl:
+ return str(nl[0])
+ return ''
+ def __repr__(self):
+ nl = self.nl._create_nodelist()
+ if nl:
+ return repr(nl[0])
+ return ''
+
+class NullNodeList(SCons.Util.NullSeq):
+ def __call__(self, *args, **kwargs): return ''
+ def __str__(self): return ''
+ # TODO(1.5): unneeded after new-style classes introduce iterators
+ def __getitem__(self, i):
+ raise IndexError
+
+NullNodesList = NullNodeList()
+
+def subst_dict(target, source):
+ """Create a dictionary for substitution of special
+ construction variables.
+
+ This translates the following special arguments:
+
+ target - the target (object or array of objects),
+ used to generate the TARGET and TARGETS
+ construction variables
+
+ source - the source (object or array of objects),
+ used to generate the SOURCES and SOURCE
+ construction variables
+ """
+ dict = {}
+
+ if target:
+ def get_tgt_subst_proxy(thing):
+ try:
+ subst_proxy = thing.get_subst_proxy()
+ except AttributeError:
+ subst_proxy = thing # probably a string, just return it
+ return subst_proxy
+ tnl = NLWrapper(target, get_tgt_subst_proxy)
+ dict['TARGETS'] = Targets_or_Sources(tnl)
+ dict['TARGET'] = Target_or_Source(tnl)
+
+ # This is a total cheat, but hopefully this dictionary goes
+ # away soon anyway. We just let these expand to $TARGETS
+ # because that's "good enough" for the use of ToolSurrogates
+ # (see test/ToolSurrogate.py) to generate documentation.
+ dict['CHANGED_TARGETS'] = '$TARGETS'
+ dict['UNCHANGED_TARGETS'] = '$TARGETS'
+ else:
+ dict['TARGETS'] = NullNodesList
+ dict['TARGET'] = NullNodesList
+
+ if source:
+ def get_src_subst_proxy(node):
+ try:
+ rfile = node.rfile
+ except AttributeError:
+ pass
+ else:
+ node = rfile()
+ try:
+ return node.get_subst_proxy()
+ except AttributeError:
+ return node # probably a String, just return it
+ snl = NLWrapper(source, get_src_subst_proxy)
+ dict['SOURCES'] = Targets_or_Sources(snl)
+ dict['SOURCE'] = Target_or_Source(snl)
+
+ # This is a total cheat, but hopefully this dictionary goes
+ # away soon anyway. We just let these expand to $TARGETS
+ # because that's "good enough" for the use of ToolSurrogates
+ # (see test/ToolSurrogate.py) to generate documentation.
+ dict['CHANGED_SOURCES'] = '$SOURCES'
+ dict['UNCHANGED_SOURCES'] = '$SOURCES'
+ else:
+ dict['SOURCES'] = NullNodesList
+ dict['SOURCE'] = NullNodesList
+
+ return dict
+
+# Constants for the "mode" parameter to scons_subst_list() and
+# scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD
+# gives a command line suitable for passing to a shell. SUBST_SIG
+# gives a command line appropriate for calculating the signature
+# of a command line...if this changes, we should rebuild.
+SUBST_CMD = 0
+SUBST_RAW = 1
+SUBST_SIG = 2
+
+_rm = re.compile(r'\$[()]')
+_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)')
+
+# Indexed by the SUBST_* constants above.
+_regex_remove = [ _rm, None, _remove ]
+
+def _rm_list(list):
+ #return [ l for l in list if not l in ('$(', '$)') ]
+ return filter(lambda l: not l in ('$(', '$)'), list)
+
+def _remove_list(list):
+ result = []
+ do_append = result.append
+ for l in list:
+ if l == '$(':
+ do_append = lambda x: None
+ elif l == '$)':
+ do_append = result.append
+ else:
+ do_append(l)
+ return result
+
+# Indexed by the SUBST_* constants above.
+_list_remove = [ _rm_list, None, _remove_list ]
+
+# Regular expressions for splitting strings and handling substitutions,
+# for use by the scons_subst() and scons_subst_list() functions:
+#
+# The first expression compiled matches all of the $-introduced tokens
+# that we need to process in some way, and is used for substitutions.
+# The expressions it matches are:
+#
+# "$$"
+# "$("
+# "$)"
+# "$variable" [must begin with alphabetic or underscore]
+# "${any stuff}"
+#
+# The second expression compiled is used for splitting strings into tokens
+# to be processed, and it matches all of the tokens listed above, plus
+# the following that affect how arguments do or don't get joined together:
+#
+# " " [white space]
+# "non-white-space" [without any dollar signs]
+# "$" [single dollar sign]
+#
+_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}'
+_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str)
+_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str)
+
+# This regular expression is used to replace strings of multiple white
+# space characters in the string result from the scons_subst() function.
+_space_sep = re.compile(r'[\t ]+(?![^{]*})')
+
+def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
+ """Expand a string or list containing construction variable
+ substitutions.
+
+ This is the work-horse function for substitutions in file names
+ and the like. The companion scons_subst_list() function (below)
+ handles separating command lines into lists of arguments, so see
+ that function if that's what you're looking for.
+ """
+ if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
+ return strSubst
+
+ class StringSubber:
+ """A class to construct the results of a scons_subst() call.
+
+ This binds a specific construction environment, mode, target and
+ source with two methods (substitute() and expand()) that handle
+ the expansion.
+ """
+ def __init__(self, env, mode, conv, gvars):
+ self.env = env
+ self.mode = mode
+ self.conv = conv
+ self.gvars = gvars
+
+ def expand(self, s, lvars):
+ """Expand a single "token" as necessary, returning an
+ appropriate string containing the expansion.
+
+ This handles expanding different types of things (strings,
+ lists, callables) appropriately. It calls the wrapper
+ substitute() method to re-expand things as necessary, so that
+ the results of expansions of side-by-side strings still get
+ re-evaluated separately, not smushed together.
+ """
+ if is_String(s):
+ try:
+ s0, s1 = s[:2]
+ except (IndexError, ValueError):
+ return s
+ if s0 != '$':
+ return s
+ if s1 == '$':
+ return '$'
+ elif s1 in '()':
+ return s
+ else:
+ key = s[1:]
+ if key[0] == '{' or string.find(key, '.') >= 0:
+ if key[0] == '{':
+ key = key[1:-1]
+ try:
+ s = eval(key, self.gvars, lvars)
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ if e.__class__ in AllowableExceptions:
+ return ''
+ raise_exception(e, lvars['TARGETS'], s)
+ else:
+ if lvars.has_key(key):
+ s = lvars[key]
+ elif self.gvars.has_key(key):
+ s = self.gvars[key]
+ elif not NameError in AllowableExceptions:
+ raise_exception(NameError(key), lvars['TARGETS'], s)
+ else:
+ return ''
+
+ # Before re-expanding the result, handle
+ # recursive expansion by copying the local
+ # variable dictionary and overwriting a null
+ # string for the value of the variable name
+ # we just expanded.
+ #
+ # This could potentially be optimized by only
+ # copying lvars when s contains more expansions,
+ # but lvars is usually supposed to be pretty
+ # small, and deeply nested variable expansions
+ # are probably more the exception than the norm,
+ # so it should be tolerable for now.
+ lv = lvars.copy()
+ var = string.split(key, '.')[0]
+ lv[var] = ''
+ return self.substitute(s, lv)
+ elif is_Sequence(s):
+ def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars):
+ return conv(substitute(l, lvars))
+ return map(func, s)
+ elif callable(s):
+ try:
+ s = s(target=lvars['TARGETS'],
+ source=lvars['SOURCES'],
+ env=self.env,
+ for_signature=(self.mode != SUBST_CMD))
+ except TypeError:
+ # This probably indicates that it's a callable
+ # object that doesn't match our calling arguments
+ # (like an Action).
+ if self.mode == SUBST_RAW:
+ return s
+ s = self.conv(s)
+ return self.substitute(s, lvars)
+ elif s is None:
+ return ''
+ else:
+ return s
+
+ def substitute(self, args, lvars):
+ """Substitute expansions in an argument or list of arguments.
+
+ This serves as a wrapper for splitting up a string into
+ separate tokens.
+ """
+ if is_String(args) and not isinstance(args, CmdStringHolder):
+ args = str(args) # In case it's a UserString.
+ try:
+ def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars):
+ return conv(expand(match.group(1), lvars))
+ result = _dollar_exps.sub(sub_match, args)
+ except TypeError:
+ # If the internal conversion routine doesn't return
+ # strings (it could be overridden to return Nodes, for
+ # example), then the 1.5.2 re module will throw this
+ # exception. Back off to a slower, general-purpose
+ # algorithm that works for all data types.
+ args = _separate_args.findall(args)
+ result = []
+ for a in args:
+ result.append(self.conv(self.expand(a, lvars)))
+ if len(result) == 1:
+ result = result[0]
+ else:
+ result = string.join(map(str, result), '')
+ return result
+ else:
+ return self.expand(args, lvars)
+
+ if conv is None:
+ conv = _strconv[mode]
+
+ # Doing this every time is a bit of a waste, since the Executor
+ # has typically already populated the OverrideEnvironment with
+ # $TARGET/$SOURCE variables. We're keeping this (for now), though,
+ # because it supports existing behavior that allows us to call
+ # an Action directly with an arbitrary target+source pair, which
+ # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
+ # If we dropped that behavior (or found another way to cover it),
+ # we could get rid of this call completely and just rely on the
+ # Executor setting the variables.
+ if not lvars.has_key('TARGET'):
+ d = subst_dict(target, source)
+ if d:
+ lvars = lvars.copy()
+ lvars.update(d)
+
+ # We're (most likely) going to eval() things. If Python doesn't
+ # find a __builtins__ value in the global dictionary used for eval(),
+ # it copies the current global values for you. Avoid this by
+ # setting it explicitly and then deleting, so we don't pollute the
+ # construction environment Dictionary(ies) that are typically used
+ # for expansion.
+ gvars['__builtins__'] = __builtins__
+
+ ss = StringSubber(env, mode, conv, gvars)
+ result = ss.substitute(strSubst, lvars)
+
+ try:
+ del gvars['__builtins__']
+ except KeyError:
+ pass
+
+ if is_String(result):
+ # Remove $(-$) pairs and any stuff in between,
+ # if that's appropriate.
+ remove = _regex_remove[mode]
+ if remove:
+ result = remove.sub('', result)
+ if mode != SUBST_RAW:
+ # Compress strings of white space characters into
+ # a single space.
+ result = string.strip(_space_sep.sub(' ', result))
+ elif is_Sequence(result):
+ remove = _list_remove[mode]
+ if remove:
+ result = remove(result)
+
+ return result
+
+#Subst_List_Strings = {}
+
+def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
+ """Substitute construction variables in a string (or list or other
+ object) and separate the arguments into a command list.
+
+ The companion scons_subst() function (above) handles basic
+ substitutions within strings, so see that function instead
+ if that's what you're looking for.
+ """
+# try:
+# Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1
+# except KeyError:
+# Subst_List_Strings[strSubst] = 1
+# import SCons.Debug
+# SCons.Debug.caller_trace(1)
+ class ListSubber(UserList.UserList):
+ """A class to construct the results of a scons_subst_list() call.
+
+ Like StringSubber, this class binds a specific construction
+ environment, mode, target and source with two methods
+ (substitute() and expand()) that handle the expansion.
+
+ In addition, however, this class is used to track the state of
+ the result(s) we're gathering so we can do the appropriate thing
+ whenever we have to append another word to the result--start a new
+ line, start a new word, append to the current word, etc. We do
+ this by setting the "append" attribute to the right method so
+ that our wrapper methods only need ever call ListSubber.append(),
+ and the rest of the object takes care of doing the right thing
+ internally.
+ """
+ def __init__(self, env, mode, conv, gvars):
+ UserList.UserList.__init__(self, [])
+ self.env = env
+ self.mode = mode
+ self.conv = conv
+ self.gvars = gvars
+
+ if self.mode == SUBST_RAW:
+ self.add_strip = lambda x, s=self: s.append(x)
+ else:
+ self.add_strip = lambda x, s=self: None
+ self.in_strip = None
+ self.next_line()
+
+ def expand(self, s, lvars, within_list):
+ """Expand a single "token" as necessary, appending the
+ expansion to the current result.
+
+ This handles expanding different types of things (strings,
+ lists, callables) appropriately. It calls the wrapper
+ substitute() method to re-expand things as necessary, so that
+ the results of expansions of side-by-side strings still get
+ re-evaluated separately, not smushed together.
+ """
+
+ if is_String(s):
+ try:
+ s0, s1 = s[:2]
+ except (IndexError, ValueError):
+ self.append(s)
+ return
+ if s0 != '$':
+ self.append(s)
+ return
+ if s1 == '$':
+ self.append('$')
+ elif s1 == '(':
+ self.open_strip('$(')
+ elif s1 == ')':
+ self.close_strip('$)')
+ else:
+ key = s[1:]
+ if key[0] == '{' or string.find(key, '.') >= 0:
+ if key[0] == '{':
+ key = key[1:-1]
+ try:
+ s = eval(key, self.gvars, lvars)
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ if e.__class__ in AllowableExceptions:
+ return
+ raise_exception(e, lvars['TARGETS'], s)
+ else:
+ if lvars.has_key(key):
+ s = lvars[key]
+ elif self.gvars.has_key(key):
+ s = self.gvars[key]
+ elif not NameError in AllowableExceptions:
+ raise_exception(NameError(), lvars['TARGETS'], s)
+ else:
+ return
+
+ # Before re-expanding the result, handle
+ # recursive expansion by copying the local
+ # variable dictionary and overwriting a null
+ # string for the value of the variable name
+ # we just expanded.
+ lv = lvars.copy()
+ var = string.split(key, '.')[0]
+ lv[var] = ''
+ self.substitute(s, lv, 0)
+ self.this_word()
+ elif is_Sequence(s):
+ for a in s:
+ self.substitute(a, lvars, 1)
+ self.next_word()
+ elif callable(s):
+ try:
+ s = s(target=lvars['TARGETS'],
+ source=lvars['SOURCES'],
+ env=self.env,
+ for_signature=(self.mode != SUBST_CMD))
+ except TypeError:
+ # This probably indicates that it's a callable
+ # object that doesn't match our calling arguments
+ # (like an Action).
+ if self.mode == SUBST_RAW:
+ self.append(s)
+ return
+ s = self.conv(s)
+ self.substitute(s, lvars, within_list)
+ elif s is None:
+ self.this_word()
+ else:
+ self.append(s)
+
+ def substitute(self, args, lvars, within_list):
+ """Substitute expansions in an argument or list of arguments.
+
+ This serves as a wrapper for splitting up a string into
+ separate tokens.
+ """
+
+ if is_String(args) and not isinstance(args, CmdStringHolder):
+ args = str(args) # In case it's a UserString.
+ args = _separate_args.findall(args)
+ for a in args:
+ if a[0] in ' \t\n\r\f\v':
+ if '\n' in a:
+ self.next_line()
+ elif within_list:
+ self.append(a)
+ else:
+ self.next_word()
+ else:
+ self.expand(a, lvars, within_list)
+ else:
+ self.expand(args, lvars, within_list)
+
+ def next_line(self):
+ """Arrange for the next word to start a new line. This
+ is like starting a new word, except that we have to append
+ another line to the result."""
+ UserList.UserList.append(self, [])
+ self.next_word()
+
+ def this_word(self):
+ """Arrange for the next word to append to the end of the
+ current last word in the result."""
+ self.append = self.add_to_current_word
+
+ def next_word(self):
+ """Arrange for the next word to start a new word."""
+ self.append = self.add_new_word
+
+ def add_to_current_word(self, x):
+ """Append the string x to the end of the current last word
+ in the result. If that is not possible, then just add
+ it as a new word. Make sure the entire concatenated string
+ inherits the object attributes of x (in particular, the
+ escape function) by wrapping it as CmdStringHolder."""
+
+ if not self.in_strip or self.mode != SUBST_SIG:
+ try:
+ current_word = self[-1][-1]
+ except IndexError:
+ self.add_new_word(x)
+ else:
+ # All right, this is a hack and it should probably
+ # be refactored out of existence in the future.
+ # The issue is that we want to smoosh words together
+ # and make one file name that gets escaped if
+ # we're expanding something like foo$EXTENSION,
+ # but we don't want to smoosh them together if
+ # it's something like >$TARGET, because then we'll
+ # treat the '>' like it's part of the file name.
+ # So for now, just hard-code looking for the special
+ # command-line redirection characters...
+ try:
+ last_char = str(current_word)[-1]
+ except IndexError:
+ last_char = '\0'
+ if last_char in '<>|':
+ self.add_new_word(x)
+ else:
+ y = current_word + x
+
+ # We used to treat a word appended to a literal
+ # as a literal itself, but this caused problems
+ # with interpreting quotes around space-separated
+ # targets on command lines. Removing this makes
+ # none of the "substantive" end-to-end tests fail,
+ # so we'll take this out but leave it commented
+ # for now in case there's a problem not covered
+ # by the test cases and we need to resurrect this.
+ #literal1 = self.literal(self[-1][-1])
+ #literal2 = self.literal(x)
+ y = self.conv(y)
+ if is_String(y):
+ #y = CmdStringHolder(y, literal1 or literal2)
+ y = CmdStringHolder(y, None)
+ self[-1][-1] = y
+
+ def add_new_word(self, x):
+ if not self.in_strip or self.mode != SUBST_SIG:
+ literal = self.literal(x)
+ x = self.conv(x)
+ if is_String(x):
+ x = CmdStringHolder(x, literal)
+ self[-1].append(x)
+ self.append = self.add_to_current_word
+
+ def literal(self, x):
+ try:
+ l = x.is_literal
+ except AttributeError:
+ return None
+ else:
+ return l()
+
+ def open_strip(self, x):
+ """Handle the "open strip" $( token."""
+ self.add_strip(x)
+ self.in_strip = 1
+
+ def close_strip(self, x):
+ """Handle the "close strip" $) token."""
+ self.add_strip(x)
+ self.in_strip = None
+
+ if conv is None:
+ conv = _strconv[mode]
+
+ # Doing this every time is a bit of a waste, since the Executor
+ # has typically already populated the OverrideEnvironment with
+ # $TARGET/$SOURCE variables. We're keeping this (for now), though,
+ # because it supports existing behavior that allows us to call
+ # an Action directly with an arbitrary target+source pair, which
+ # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
+ # If we dropped that behavior (or found another way to cover it),
+ # we could get rid of this call completely and just rely on the
+ # Executor setting the variables.
+ if not lvars.has_key('TARGET'):
+ d = subst_dict(target, source)
+ if d:
+ lvars = lvars.copy()
+ lvars.update(d)
+
+ # We're (most likely) going to eval() things. If Python doesn't
+ # find a __builtins__ value in the global dictionary used for eval(),
+ # it copies the current global values for you. Avoid this by
+ # setting it explicitly and then deleting, so we don't pollute the
+ # construction environment Dictionary(ies) that are typically used
+ # for expansion.
+ gvars['__builtins__'] = __builtins__
+
+ ls = ListSubber(env, mode, conv, gvars)
+ ls.substitute(strSubst, lvars, 0)
+
+ try:
+ del gvars['__builtins__']
+ except KeyError:
+ pass
+
+ return ls.data
+
+def scons_subst_once(strSubst, env, key):
+ """Perform single (non-recursive) substitution of a single
+ construction variable keyword.
+
+ This is used when setting a variable when copying or overriding values
+ in an Environment. We want to capture (expand) the old value before
+ we override it, so people can do things like:
+
+ env2 = env.Clone(CCFLAGS = '$CCFLAGS -g')
+
+ We do this with some straightforward, brute-force code here...
+ """
+ if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
+ return strSubst
+
+ matchlist = ['$' + key, '${' + key + '}']
+ val = env.get(key, '')
+ def sub_match(match, val=val, matchlist=matchlist):
+ a = match.group(1)
+ if a in matchlist:
+ a = val
+ if is_Sequence(a):
+ return string.join(map(str, a))
+ else:
+ return str(a)
+
+ if is_Sequence(strSubst):
+ result = []
+ for arg in strSubst:
+ if is_String(arg):
+ if arg in matchlist:
+ arg = val
+ if is_Sequence(arg):
+ result.extend(arg)
+ else:
+ result.append(arg)
+ else:
+ result.append(_dollar_exps.sub(sub_match, arg))
+ else:
+ result.append(arg)
+ return result
+ elif is_String(strSubst):
+ return _dollar_exps.sub(sub_match, strSubst)
+ else:
+ return strSubst
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py
new file mode 100644
index 0000000..5e83eaf
--- /dev/null
+++ b/src/engine/SCons/SubstTests.py
@@ -0,0 +1,1242 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/SubstTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+import StringIO
+import sys
+import types
+import unittest
+
+from UserDict import UserDict
+
+import SCons.Errors
+
+from SCons.Subst import *
+
+class DummyNode:
+ """Simple node work-alike."""
+ def __init__(self, name):
+ self.name = os.path.normpath(name)
+ def __str__(self):
+ return self.name
+ def is_literal(self):
+ return 1
+ def rfile(self):
+ return self
+ def get_subst_proxy(self):
+ return self
+
+class DummyEnv:
+ def __init__(self, dict={}):
+ self.dict = dict
+
+ def Dictionary(self, key = None):
+ if not key:
+ return self.dict
+ return self.dict[key]
+
+ def __getitem__(self, key):
+ return self.dict[key]
+
+ def get(self, key, default):
+ return self.dict.get(key, default)
+
+ def sig_dict(self):
+ dict = self.dict.copy()
+ dict["TARGETS"] = 'tsig'
+ dict["SOURCES"] = 'ssig'
+ return dict
+
+def cs(target=None, source=None, env=None, for_signature=None):
+ return 'cs'
+
+def cl(target=None, source=None, env=None, for_signature=None):
+ return ['cl']
+
+def CmdGen1(target, source, env, for_signature):
+ # Nifty trick...since Environment references are interpolated,
+ # instantiate an instance of a callable class with this one,
+ # which will then get evaluated.
+ assert str(target) == 't', target
+ assert str(source) == 's', source
+ return "${CMDGEN2('foo', %d)}" % for_signature
+
+class CmdGen2:
+ def __init__(self, mystr, forsig):
+ self.mystr = mystr
+ self.expect_for_signature = forsig
+
+ def __call__(self, target, source, env, for_signature):
+ assert str(target) == 't', target
+ assert str(source) == 's', source
+ assert for_signature == self.expect_for_signature, for_signature
+ return [ self.mystr, env.Dictionary('BAR') ]
+
+if os.sep == '/':
+ def cvt(str):
+ return str
+else:
+ def cvt(str):
+ return string.replace(str, '/', os.sep)
+
+class SubstTestCase(unittest.TestCase):
+ class MyNode(DummyNode):
+ """Simple node work-alike with some extra stuff for testing."""
+ def __init__(self, name):
+ DummyNode.__init__(self, name)
+ class Attribute:
+ pass
+ self.attribute = Attribute()
+ self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
+ self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
+ def get_stuff(self, extra):
+ return self.name + extra
+ foo = 1
+
+ class TestLiteral:
+ def __init__(self, literal):
+ self.literal = literal
+ def __str__(self):
+ return self.literal
+ def is_literal(self):
+ return 1
+
+ class TestCallable:
+ def __init__(self, value):
+ self.value = value
+ def __call__(self):
+ pass
+ def __str__(self):
+ return self.value
+
+ def function_foo(arg):
+ pass
+
+ target = [ MyNode("./foo/bar.exe"),
+ MyNode("/bar/baz with spaces.obj"),
+ MyNode("../foo/baz.obj") ]
+ source = [ MyNode("./foo/blah with spaces.cpp"),
+ MyNode("/bar/ack.cpp"),
+ MyNode("../foo/ack.c") ]
+
+ callable_object_1 = TestCallable('callable-1')
+ callable_object_2 = TestCallable('callable-2')
+
+ def _defines(defs):
+ l = []
+ for d in defs:
+ if SCons.Util.is_List(d) or type(d) is types.TupleType:
+ l.append(str(d[0]) + '=' + str(d[1]))
+ else:
+ l.append(str(d))
+ return l
+
+ loc = {
+ 'xxx' : None,
+ 'NEWLINE' : 'before\nafter',
+
+ 'null' : '',
+ 'zero' : 0,
+ 'one' : 1,
+ 'BAZ' : 'baz',
+ 'ONE' : '$TWO',
+ 'TWO' : '$THREE',
+ 'THREE' : 'four',
+
+ 'AAA' : 'a',
+ 'BBB' : 'b',
+ 'CCC' : 'c',
+
+ 'DO' : DummyNode('do something'),
+ 'FOO' : DummyNode('foo.in'),
+ 'BAR' : DummyNode('bar with spaces.out'),
+ 'CRAZY' : DummyNode('crazy\nfile.in'),
+
+ # $XXX$HHH should expand to GGGIII, not BADNEWS.
+ 'XXX' : '$FFF',
+ 'FFF' : 'GGG',
+ 'HHH' : 'III',
+ 'FFFIII' : 'BADNEWS',
+
+ 'LITERAL' : TestLiteral("$XXX"),
+
+ # Test that we can expand to and return a function.
+ #'FUNCTION' : function_foo,
+
+ 'CMDGEN1' : CmdGen1,
+ 'CMDGEN2' : CmdGen2,
+
+ 'LITERALS' : [ Literal('foo\nwith\nnewlines'),
+ Literal('bar\nwith\nnewlines') ],
+
+ 'NOTHING' : "",
+ 'NONE' : None,
+
+ # Test various combinations of strings, lists and functions.
+ 'N' : None,
+ 'X' : 'x',
+ 'Y' : '$X',
+ 'R' : '$R',
+ 'S' : 'x y',
+ 'LS' : ['x y'],
+ 'L' : ['x', 'y'],
+ 'TS' : ('x y'),
+ 'T' : ('x', 'y'),
+ 'CS' : cs,
+ 'CL' : cl,
+ 'US' : UserString.UserString('us'),
+
+ # Test function calls within ${}.
+ 'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
+ 'FUNC1' : lambda x: x,
+ 'FUNC2' : lambda target, source, env, for_signature: ['x$CCC'],
+
+ # Various tests refactored from ActionTests.py.
+ 'LIST' : [["This", "is", "$(", "$a", "$)", "test"]],
+
+ # Test recursion.
+ 'RECURSE' : 'foo $RECURSE bar',
+ 'RRR' : 'foo $SSS bar',
+ 'SSS' : '$RRR',
+
+ # Test callables that don't match the calling arguments.
+ 'CALLABLE1' : callable_object_1,
+ 'CALLABLE2' : callable_object_2,
+
+ '_defines' : _defines,
+ 'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ],
+ }
+
+ def basic_comparisons(self, function, convert):
+ env = DummyEnv(self.loc)
+ cases = self.basic_cases[:]
+ kwargs = {'target' : self.target, 'source' : self.source,
+ 'gvars' : env.Dictionary()}
+
+ failed = 0
+ while cases:
+ input, expect = cases[:2]
+ expect = convert(expect)
+ try:
+ result = apply(function, (input, env), kwargs)
+ except Exception, e:
+ fmt = " input %s generated %s (%s)"
+ print fmt % (repr(input), e.__class__.__name__, repr(e))
+ failed = failed + 1
+ else:
+ if result != expect:
+ if failed == 0: print
+ print " input %s => %s did not match %s" % (repr(input), repr(result), repr(expect))
+ failed = failed + 1
+ del cases[:2]
+ fmt = "%d %s() cases failed"
+ assert failed == 0, fmt % (failed, function.__name__)
+
+class scons_subst_TestCase(SubstTestCase):
+
+ # Basic tests of substitution functionality.
+ basic_cases = [
+ # Basics: strings without expansions are left alone, and
+ # the simplest possible expansion to a null-string value.
+ "test", "test",
+ "$null", "",
+
+ # Test expansion of integer values.
+ "test $zero", "test 0",
+ "test $one", "test 1",
+
+ # Test multiple re-expansion of values.
+ "test $ONE", "test four",
+
+ # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions.
+ "test $TARGETS $SOURCES",
+ "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp /bar/ack.cpp ../foo/ack.c",
+
+ "test ${TARGETS[:]} ${SOURCES[0]}",
+ "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp",
+
+ "test ${TARGETS[1:]}v",
+ "test /bar/baz with spaces.obj ../foo/baz.objv",
+
+ "test $TARGET",
+ "test foo/bar.exe",
+
+ "test $TARGET$NO_SUCH_VAR[0]",
+ "test foo/bar.exe[0]",
+
+ "test $TARGETS.foo",
+ "test 1 1 1",
+
+ "test ${SOURCES[0:2].foo}",
+ "test 1 1",
+
+ "test $SOURCE.foo",
+ "test 1",
+
+ "test ${TARGET.get_stuff('blah')}",
+ "test foo/bar.exeblah",
+
+ "test ${SOURCES.get_stuff('blah')}",
+ "test foo/blah with spaces.cppblah /bar/ack.cppblah ../foo/ack.cblah",
+
+ "test ${SOURCES[0:2].get_stuff('blah')}",
+ "test foo/blah with spaces.cppblah /bar/ack.cppblah",
+
+ "test ${SOURCES[0:2].get_stuff('blah')}",
+ "test foo/blah with spaces.cppblah /bar/ack.cppblah",
+
+ "test ${SOURCES.attribute.attr1}",
+ "test attr$1-blah with spaces.cpp attr$1-ack.cpp attr$1-ack.c",
+
+ "test ${SOURCES.attribute.attr2}",
+ "test attr$2-blah with spaces.cpp attr$2-ack.cpp attr$2-ack.c",
+
+ # Test adjacent expansions.
+ "foo$BAZ",
+ "foobaz",
+
+ "foo${BAZ}",
+ "foobaz",
+
+ # Test that adjacent expansions don't get re-interpreted
+ # together. The correct disambiguated expansion should be:
+ # $XXX$HHH => ${FFF}III => GGGIII
+ # not:
+ # $XXX$HHH => ${FFFIII} => BADNEWS
+ "$XXX$HHH", "GGGIII",
+
+ # Test double-dollar-sign behavior.
+ "$$FFF$HHH", "$FFFIII",
+
+ # Test that a Literal will stop dollar-sign substitution.
+ "$XXX $LITERAL $FFF", "GGG $XXX GGG",
+
+ # Test that we don't blow up even if they subscript
+ # something in ways they "can't."
+ "${FFF[0]}", "G",
+ "${FFF[7]}", "",
+ "${NOTHING[1]}", "",
+
+ # Test various combinations of strings and lists.
+ #None, '',
+ '', '',
+ 'x', 'x',
+ 'x y', 'x y',
+ '$N', '',
+ '$X', 'x',
+ '$Y', 'x',
+ '$R', '',
+ '$S', 'x y',
+ '$LS', 'x y',
+ '$L', 'x y',
+ '$TS', 'x y',
+ '$T', 'x y',
+ '$S z', 'x y z',
+ '$LS z', 'x y z',
+ '$L z', 'x y z',
+ '$TS z', 'x y z',
+ '$T z', 'x y z',
+ #cs, 'cs',
+ #cl, 'cl',
+ '$CS', 'cs',
+ '$CL', 'cl',
+
+ # Various uses of UserString.
+ UserString.UserString('x'), 'x',
+ UserString.UserString('$X'), 'x',
+ UserString.UserString('$US'), 'us',
+ '$US', 'us',
+
+ # Test function calls within ${}.
+ '$FUNCCALL', 'a xc b',
+
+ # Bug reported by Christoph Wiedemann.
+ cvt('$xxx/bin'), '/bin',
+
+ # Tests callables that don't match our calling arguments.
+ '$CALLABLE1', 'callable-1',
+
+ # Test handling of quotes.
+ 'aaa "bbb ccc" ddd', 'aaa "bbb ccc" ddd',
+ ]
+
+ def test_scons_subst(self):
+ """Test scons_subst(): basic substitution"""
+ return self.basic_comparisons(scons_subst, cvt)
+
+ subst_cases = [
+ "test $xxx",
+ "test ",
+ "test",
+ "test",
+
+ "test $($xxx$)",
+ "test $($)",
+ "test",
+ "test",
+
+ "test $( $xxx $)",
+ "test $( $)",
+ "test",
+ "test",
+
+ "$AAA ${AAA}A $BBBB $BBB",
+ "a aA b",
+ "a aA b",
+ "a aA b",
+
+ "$RECURSE",
+ "foo bar",
+ "foo bar",
+ "foo bar",
+
+ "$RRR",
+ "foo bar",
+ "foo bar",
+ "foo bar",
+
+ # Verify what happens with no target or source nodes.
+ "$TARGET $SOURCES",
+ " ",
+ "",
+ "",
+
+ "$TARGETS $SOURCE",
+ " ",
+ "",
+ "",
+
+ # Various tests refactored from ActionTests.py.
+ "${LIST}",
+ "This is $( $) test",
+ "This is test",
+ "This is test",
+
+ ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
+ ["|", "$(", "a", "|", "b", "$)", "|", "c", "1"],
+ ["|", "a", "|", "b", "|", "c", "1"],
+ ["|", "|", "c", "1"],
+ ]
+
+ def test_subst_env(self):
+ """Test scons_subst(): expansion dictionary"""
+ # The expansion dictionary no longer comes from the construction
+ # environment automatically.
+ env = DummyEnv(self.loc)
+ s = scons_subst('$AAA', env)
+ assert s == '', s
+
+ def test_subst_SUBST_modes(self):
+ """Test scons_subst(): SUBST_* modes"""
+ env = DummyEnv(self.loc)
+ subst_cases = self.subst_cases[:]
+
+ gvars = env.Dictionary()
+
+ failed = 0
+ while subst_cases:
+ input, eraw, ecmd, esig = subst_cases[:4]
+ result = scons_subst(input, env, mode=SUBST_RAW, gvars=gvars)
+ if result != eraw:
+ if failed == 0: print
+ print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
+ failed = failed + 1
+ result = scons_subst(input, env, mode=SUBST_CMD, gvars=gvars)
+ if result != ecmd:
+ if failed == 0: print
+ print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
+ failed = failed + 1
+ result = scons_subst(input, env, mode=SUBST_SIG, gvars=gvars)
+ if result != esig:
+ if failed == 0: print
+ print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
+ failed = failed + 1
+ del subst_cases[:4]
+ assert failed == 0, "%d subst() mode cases failed" % failed
+
+ def test_subst_target_source(self):
+ """Test scons_subst(): target= and source= arguments"""
+ env = DummyEnv(self.loc)
+ t1 = self.MyNode('t1')
+ t2 = self.MyNode('t2')
+ s1 = self.MyNode('s1')
+ s2 = self.MyNode('s2')
+ result = scons_subst("$TARGET $SOURCES", env,
+ target=[t1, t2],
+ source=[s1, s2])
+ assert result == "t1 s1 s2", result
+ result = scons_subst("$TARGET $SOURCES", env,
+ target=[t1, t2],
+ source=[s1, s2],
+ gvars={})
+ assert result == "t1 s1 s2", result
+
+ result = scons_subst("$TARGET $SOURCES", env, target=[], source=[])
+ assert result == " ", result
+ result = scons_subst("$TARGETS $SOURCE", env, target=[], source=[])
+ assert result == " ", result
+
+ def test_subst_callable_expansion(self):
+ """Test scons_subst(): expanding a callable"""
+ env = DummyEnv(self.loc)
+ gvars = env.Dictionary()
+ newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS", env,
+ target=self.MyNode('t'), source=self.MyNode('s'),
+ gvars=gvars)
+ assert newcom == "test foo bar with spaces.out s t", newcom
+
+ def test_subst_attribute_errors(self):
+ """Test scons_subst(): handling attribute errors"""
+ env = DummyEnv(self.loc)
+ try:
+ class Foo:
+ pass
+ scons_subst('${foo.bar}', env, gvars={'foo':Foo()})
+ except SCons.Errors.UserError, e:
+ expect = [
+ "AttributeError `bar' trying to evaluate `${foo.bar}'",
+ "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
+ "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
+ ]
+ assert str(e) in expect, e
+ else:
+ raise AssertionError, "did not catch expected UserError"
+
+ def test_subst_syntax_errors(self):
+ """Test scons_subst(): handling syntax errors"""
+ env = DummyEnv(self.loc)
+ try:
+ scons_subst('$foo.bar.3.0', env)
+ except SCons.Errors.UserError, e:
+ expect = [
+ # Python 1.5
+ "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
+ # Python 2.2, 2.3, 2.4
+ "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
+ # Python 2.5
+ "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
+ ]
+ assert str(e) in expect, e
+ else:
+ raise AssertionError, "did not catch expected UserError"
+
+ def test_subst_type_errors(self):
+ """Test scons_subst(): handling type errors"""
+ env = DummyEnv(self.loc)
+ try:
+ scons_subst("${NONE[2]}", env, gvars={'NONE':None})
+ except SCons.Errors.UserError, e:
+ expect = [
+ # Python 1.5, 2.2, 2.3, 2.4
+ "TypeError `unsubscriptable object' trying to evaluate `${NONE[2]}'",
+ # Python 2.5 and later
+ "TypeError `'NoneType' object is unsubscriptable' trying to evaluate `${NONE[2]}'",
+ ]
+ assert str(e) in expect, e
+ else:
+ raise AssertionError, "did not catch expected UserError"
+
+ try:
+ def func(a, b, c):
+ pass
+ scons_subst("${func(1)}", env, gvars={'func':func})
+ except SCons.Errors.UserError, e:
+ expect = [
+ # Python 1.5
+ "TypeError `not enough arguments; expected 3, got 1' trying to evaluate `${func(1)}'",
+ # Python 2.2, 2.3, 2.4, 2.5
+ "TypeError `func() takes exactly 3 arguments (1 given)' trying to evaluate `${func(1)}'"
+ ]
+ assert str(e) in expect, repr(str(e))
+ else:
+ raise AssertionError, "did not catch expected UserError"
+
+ def test_subst_raw_function(self):
+ """Test scons_subst(): fetch function with SUBST_RAW plus conv"""
+ # Test that the combination of SUBST_RAW plus a pass-through
+ # conversion routine allows us to fetch a function through the
+ # dictionary. CommandAction uses this to allow delayed evaluation
+ # of $SPAWN variables.
+ env = DummyEnv(self.loc)
+ gvars = env.Dictionary()
+ x = lambda x: x
+ r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, conv=x, gvars=gvars)
+ assert r is self.callable_object_1, repr(r)
+ r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == 'callable-1', repr(r)
+
+ # Test how we handle overriding the internal conversion routines.
+ def s(obj):
+ return obj
+
+ n1 = self.MyNode('n1')
+ env = DummyEnv({'NODE' : n1})
+ gvars = env.Dictionary()
+ node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
+ assert node is n1, node
+ node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
+ assert node is n1, node
+ node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
+ assert node is n1, node
+
+ #def test_subst_function_return(self):
+ # """Test scons_subst(): returning a function"""
+ # env = DummyEnv({'FUNCTION' : foo})
+ # gvars = env.Dictionary()
+ # func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars)
+ # assert func is function_foo, func
+ # func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars)
+ # assert func is function_foo, func
+ # func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars)
+ # assert func is function_foo, func
+
+ def test_subst_overriding_gvars(self):
+ """Test scons_subst(): supplying an overriding gvars dictionary"""
+ env = DummyEnv({'XXX' : 'xxx'})
+ result = scons_subst('$XXX', env, gvars=env.Dictionary())
+ assert result == 'xxx', result
+ result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'})
+ assert result == 'yyy', result
+
+class CLVar_TestCase(unittest.TestCase):
+ def test_CLVar(self):
+ """Test scons_subst() and scons_subst_list() with CLVar objects"""
+
+ loc = {}
+ loc['FOO'] = 'foo'
+ loc['BAR'] = SCons.Util.CLVar('bar')
+ loc['CALL'] = lambda target, source, env, for_signature: 'call'
+ env = DummyEnv(loc)
+
+ cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
+
+ newcmd = scons_subst(cmd, env, gvars=env.Dictionary())
+ assert newcmd == ['test', 'foo', 'bar', 'call', 'test'], newcmd
+
+ cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary())
+ assert len(cmd_list) == 1, cmd_list
+ assert cmd_list[0][0] == "test", cmd_list[0][0]
+ assert cmd_list[0][1] == "foo", cmd_list[0][1]
+ assert cmd_list[0][2] == "bar", cmd_list[0][2]
+ assert cmd_list[0][3] == "call", cmd_list[0][3]
+ assert cmd_list[0][4] == "test", cmd_list[0][4]
+
+class scons_subst_list_TestCase(SubstTestCase):
+
+ basic_cases = [
+ "$TARGETS",
+ [
+ ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
+ ],
+
+ "$SOURCES $NEWLINE $TARGETS",
+ [
+ ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
+ ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
+ ],
+
+ "$SOURCES$NEWLINE",
+ [
+ ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
+ ["after"],
+ ],
+
+ "foo$FFF",
+ [
+ ["fooGGG"],
+ ],
+
+ "foo${FFF}",
+ [
+ ["fooGGG"],
+ ],
+
+ "test ${SOURCES.attribute.attr1}",
+ [
+ ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"],
+ ],
+
+ "test ${SOURCES.attribute.attr2}",
+ [
+ ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"],
+ ],
+
+ "$DO --in=$FOO --out=$BAR",
+ [
+ ["do something", "--in=foo.in", "--out=bar with spaces.out"],
+ ],
+
+ # This test is now fixed, and works like it should.
+ "$DO --in=$CRAZY --out=$BAR",
+ [
+ ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
+ ],
+
+ # Try passing a list to scons_subst_list().
+ [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
+ [
+ ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
+ ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
+ ],
+
+ # Test against a former bug in scons_subst_list().
+ "$XXX$HHH",
+ [
+ ["GGGIII"],
+ ],
+
+ # Test double-dollar-sign behavior.
+ "$$FFF$HHH",
+ [
+ ["$FFFIII"],
+ ],
+
+ # Test various combinations of strings, lists and functions.
+ None, [[]],
+ [None], [[]],
+ '', [[]],
+ [''], [[]],
+ 'x', [['x']],
+ ['x'], [['x']],
+ 'x y', [['x', 'y']],
+ ['x y'], [['x y']],
+ ['x', 'y'], [['x', 'y']],
+ '$N', [[]],
+ ['$N'], [[]],
+ '$X', [['x']],
+ ['$X'], [['x']],
+ '$Y', [['x']],
+ ['$Y'], [['x']],
+ #'$R', [[]],
+ #['$R'], [[]],
+ '$S', [['x', 'y']],
+ '$S z', [['x', 'y', 'z']],
+ ['$S'], [['x', 'y']],
+ ['$S z'], [['x', 'y z']], # XXX - IS THIS BEST?
+ ['$S', 'z'], [['x', 'y', 'z']],
+ '$LS', [['x y']],
+ '$LS z', [['x y', 'z']],
+ ['$LS'], [['x y']],
+ ['$LS z'], [['x y z']],
+ ['$LS', 'z'], [['x y', 'z']],
+ '$L', [['x', 'y']],
+ '$L z', [['x', 'y', 'z']],
+ ['$L'], [['x', 'y']],
+ ['$L z'], [['x', 'y z']], # XXX - IS THIS BEST?
+ ['$L', 'z'], [['x', 'y', 'z']],
+ cs, [['cs']],
+ [cs], [['cs']],
+ cl, [['cl']],
+ [cl], [['cl']],
+ '$CS', [['cs']],
+ ['$CS'], [['cs']],
+ '$CL', [['cl']],
+ ['$CL'], [['cl']],
+
+ # Various uses of UserString.
+ UserString.UserString('x'), [['x']],
+ [UserString.UserString('x')], [['x']],
+ UserString.UserString('$X'), [['x']],
+ [UserString.UserString('$X')], [['x']],
+ UserString.UserString('$US'), [['us']],
+ [UserString.UserString('$US')], [['us']],
+ '$US', [['us']],
+ ['$US'], [['us']],
+
+ # Test function calls within ${}.
+ '$FUNCCALL', [['a', 'xc', 'b']],
+
+ # Test handling of newlines in white space.
+ 'foo\nbar', [['foo'], ['bar']],
+ 'foo\n\nbar', [['foo'], ['bar']],
+ 'foo \n \n bar', [['foo'], ['bar']],
+ 'foo \nmiddle\n bar', [['foo'], ['middle'], ['bar']],
+
+ # Bug reported by Christoph Wiedemann.
+ cvt('$xxx/bin'), [['/bin']],
+
+ # Test variables smooshed together with different prefixes.
+ 'foo$AAA', [['fooa']],
+ '<$AAA', [['<', 'a']],
+ '>$AAA', [['>', 'a']],
+ '|$AAA', [['|', 'a']],
+
+ # Test callables that don't match our calling arguments.
+ '$CALLABLE2', [['callable-2']],
+
+ # Test handling of quotes.
+ # XXX Find a way to handle this in the future.
+ #'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']],
+
+ '${_defines(DEFS)}', [['Q1="q1"', 'Q2="a"']],
+ ]
+
+ def test_scons_subst_list(self):
+ """Test scons_subst_list(): basic substitution"""
+ def convert_lists(expect):
+ return map(lambda l: map(cvt, l), expect)
+ return self.basic_comparisons(scons_subst_list, convert_lists)
+
+ subst_list_cases = [
+ "test $xxx",
+ [["test"]],
+ [["test"]],
+ [["test"]],
+
+ "test $($xxx$)",
+ [["test", "$($)"]],
+ [["test"]],
+ [["test"]],
+
+ "test $( $xxx $)",
+ [["test", "$(", "$)"]],
+ [["test"]],
+ [["test"]],
+
+ "$AAA ${AAA}A $BBBB $BBB",
+ [["a", "aA", "b"]],
+ [["a", "aA", "b"]],
+ [["a", "aA", "b"]],
+
+ "$RECURSE",
+ [["foo", "bar"]],
+ [["foo", "bar"]],
+ [["foo", "bar"]],
+
+ "$RRR",
+ [["foo", "bar"]],
+ [["foo", "bar"]],
+ [["foo", "bar"]],
+
+ # Verify what happens with no target or source nodes.
+ "$TARGET $SOURCES",
+ [[]],
+ [[]],
+ [[]],
+
+ "$TARGETS $SOURCE",
+ [[]],
+ [[]],
+ [[]],
+
+ # Various test refactored from ActionTests.py
+ "${LIST}",
+ [['This', 'is', '$(', '$)', 'test']],
+ [['This', 'is', 'test']],
+ [['This', 'is', 'test']],
+
+ ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
+ [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
+ [["|", "a", "|", "b", "|", "c", "1"]],
+ [["|", "|", "c", "1"]],
+ ]
+
+ def test_subst_env(self):
+ """Test scons_subst_list(): expansion dictionary"""
+ # The expansion dictionary no longer comes from the construction
+ # environment automatically.
+ env = DummyEnv()
+ s = scons_subst_list('$AAA', env)
+ assert s == [[]], s
+
+ def test_subst_target_source(self):
+ """Test scons_subst_list(): target= and source= arguments"""
+ env = DummyEnv(self.loc)
+ gvars = env.Dictionary()
+ t1 = self.MyNode('t1')
+ t2 = self.MyNode('t2')
+ s1 = self.MyNode('s1')
+ s2 = self.MyNode('s2')
+ result = scons_subst_list("$TARGET $SOURCES", env,
+ target=[t1, t2],
+ source=[s1, s2],
+ gvars=gvars)
+ assert result == [['t1', 's1', 's2']], result
+ result = scons_subst_list("$TARGET $SOURCES", env,
+ target=[t1, t2],
+ source=[s1, s2],
+ gvars={})
+ assert result == [['t1', 's1', 's2']], result
+
+ # Test interpolating a callable.
+ _t = DummyNode('t')
+ _s = DummyNode('s')
+ cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
+ env, target=_t, source=_s,
+ gvars=gvars)
+ assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
+
+ def test_subst_escape(self):
+ """Test scons_subst_list(): escape functionality"""
+ env = DummyEnv(self.loc)
+ gvars = env.Dictionary()
+ def escape_func(foo):
+ return '**' + foo + '**'
+ cmd_list = scons_subst_list("abc $LITERALS xyz", env, gvars=gvars)
+ assert cmd_list == [['abc',
+ 'foo\nwith\nnewlines',
+ 'bar\nwith\nnewlines',
+ 'xyz']], cmd_list
+ c = cmd_list[0][0].escape(escape_func)
+ assert c == 'abc', c
+ c = cmd_list[0][1].escape(escape_func)
+ assert c == '**foo\nwith\nnewlines**', c
+ c = cmd_list[0][2].escape(escape_func)
+ assert c == '**bar\nwith\nnewlines**', c
+ c = cmd_list[0][3].escape(escape_func)
+ assert c == 'xyz', c
+
+ # We used to treat literals smooshed together like the whole
+ # thing was literal and escape it as a unit. The commented-out
+ # asserts below are in case we ever have to find a way to
+ # resurrect that functionality in some way.
+ cmd_list = scons_subst_list("abc${LITERALS}xyz", env, gvars=gvars)
+ c = cmd_list[0][0].escape(escape_func)
+ #assert c == '**abcfoo\nwith\nnewlines**', c
+ assert c == 'abcfoo\nwith\nnewlines', c
+ c = cmd_list[0][1].escape(escape_func)
+ #assert c == '**bar\nwith\nnewlinesxyz**', c
+ assert c == 'bar\nwith\nnewlinesxyz', c
+
+ _t = DummyNode('t')
+
+ cmd_list = scons_subst_list('echo "target: $TARGET"', env,
+ target=_t, gvars=gvars)
+ c = cmd_list[0][0].escape(escape_func)
+ assert c == 'echo', c
+ c = cmd_list[0][1].escape(escape_func)
+ assert c == '"target:', c
+ c = cmd_list[0][2].escape(escape_func)
+ assert c == 't"', c
+
+ def test_subst_SUBST_modes(self):
+ """Test scons_subst_list(): SUBST_* modes"""
+ env = DummyEnv(self.loc)
+ subst_list_cases = self.subst_list_cases[:]
+ gvars = env.Dictionary()
+
+ r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == [[]], r
+
+ failed = 0
+ while subst_list_cases:
+ input, eraw, ecmd, esig = subst_list_cases[:4]
+ result = scons_subst_list(input, env, mode=SUBST_RAW, gvars=gvars)
+ if result != eraw:
+ if failed == 0: print
+ print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
+ failed = failed + 1
+ result = scons_subst_list(input, env, mode=SUBST_CMD, gvars=gvars)
+ if result != ecmd:
+ if failed == 0: print
+ print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
+ failed = failed + 1
+ result = scons_subst_list(input, env, mode=SUBST_SIG, gvars=gvars)
+ if result != esig:
+ if failed == 0: print
+ print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
+ failed = failed + 1
+ del subst_list_cases[:4]
+ assert failed == 0, "%d subst() mode cases failed" % failed
+
+ def test_subst_attribute_errors(self):
+ """Test scons_subst_list(): handling attribute errors"""
+ env = DummyEnv()
+ try:
+ class Foo:
+ pass
+ scons_subst_list('${foo.bar}', env, gvars={'foo':Foo()})
+ except SCons.Errors.UserError, e:
+ expect = [
+ "AttributeError `bar' trying to evaluate `${foo.bar}'",
+ "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
+ "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
+ ]
+ assert str(e) in expect, e
+ else:
+ raise AssertionError, "did not catch expected UserError"
+
+ def test_subst_syntax_errors(self):
+ """Test scons_subst_list(): handling syntax errors"""
+ env = DummyEnv()
+ try:
+ scons_subst_list('$foo.bar.3.0', env)
+ except SCons.Errors.UserError, e:
+ expect = [
+ "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
+ "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
+ "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
+ ]
+ assert str(e) in expect, e
+ else:
+ raise AssertionError, "did not catch expected SyntaxError"
+
+ def test_subst_raw_function(self):
+ """Test scons_subst_list(): fetch function with SUBST_RAW plus conv"""
+ # Test that the combination of SUBST_RAW plus a pass-through
+ # conversion routine allows us to fetch a function through the
+ # dictionary.
+ env = DummyEnv(self.loc)
+ gvars = env.Dictionary()
+ x = lambda x: x
+ r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, conv=x, gvars=gvars)
+ assert r == [[self.callable_object_2]], repr(r)
+ r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == [['callable-2']], repr(r)
+
+ def test_subst_list_overriding_gvars(self):
+ """Test scons_subst_list(): overriding conv()"""
+ env = DummyEnv()
+ def s(obj):
+ return obj
+
+ n1 = self.MyNode('n1')
+ env = DummyEnv({'NODE' : n1})
+ gvars=env.Dictionary()
+ node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
+ assert node == [[n1]], node
+ node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
+ assert node == [[n1]], node
+ node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
+ assert node == [[n1]], node
+
+ def test_subst_list_overriding_gvars(self):
+ """Test scons_subst_list(): supplying an overriding gvars dictionary"""
+ env = DummyEnv({'XXX' : 'xxx'})
+ result = scons_subst_list('$XXX', env, gvars=env.Dictionary())
+ assert result == [['xxx']], result
+ result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
+ assert result == [['yyy']], result
+
+class scons_subst_once_TestCase(unittest.TestCase):
+
+ loc = {
+ 'CCFLAGS' : '-DFOO',
+ 'ONE' : 1,
+ 'RECURSE' : 'r $RECURSE r',
+ 'LIST' : ['a', 'b', 'c'],
+ }
+
+ basic_cases = [
+ '$CCFLAGS -DBAR',
+ 'OTHER_KEY',
+ '$CCFLAGS -DBAR',
+
+ '$CCFLAGS -DBAR',
+ 'CCFLAGS',
+ '-DFOO -DBAR',
+
+ 'x $ONE y',
+ 'ONE',
+ 'x 1 y',
+
+ 'x $RECURSE y',
+ 'RECURSE',
+ 'x r $RECURSE r y',
+
+ '$LIST',
+ 'LIST',
+ 'a b c',
+
+ ['$LIST'],
+ 'LIST',
+ ['a', 'b', 'c'],
+
+ ['x', '$LIST', 'y'],
+ 'LIST',
+ ['x', 'a', 'b', 'c', 'y'],
+
+ ['x', 'x $LIST y', 'y'],
+ 'LIST',
+ ['x', 'x a b c y', 'y'],
+
+ ['x', 'x $CCFLAGS y', 'y'],
+ 'LIST',
+ ['x', 'x $CCFLAGS y', 'y'],
+
+ ['x', 'x $RECURSE y', 'y'],
+ 'LIST',
+ ['x', 'x $RECURSE y', 'y'],
+ ]
+
+ def test_subst_once(self):
+ """Test the scons_subst_once() function"""
+ env = DummyEnv(self.loc)
+ cases = self.basic_cases[:]
+
+ failed = 0
+ while cases:
+ input, key, expect = cases[:3]
+ result = scons_subst_once(input, env, key)
+ if result != expect:
+ if failed == 0: print
+ print " input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
+ failed = failed + 1
+ del cases[:3]
+ assert failed == 0, "%d subst() cases failed" % failed
+
+class quote_spaces_TestCase(unittest.TestCase):
+ def test_quote_spaces(self):
+ """Test the quote_spaces() method..."""
+ q = quote_spaces('x')
+ assert q == 'x', q
+
+ q = quote_spaces('x x')
+ assert q == '"x x"', q
+
+ q = quote_spaces('x\tx')
+ assert q == '"x\tx"', q
+
+ class Node:
+ def __init__(self, name, children=[]):
+ self.children = children
+ self.name = name
+ def __str__(self):
+ return self.name
+ def exists(self):
+ return 1
+ def rexists(self):
+ return 1
+ def has_builder(self):
+ return 1
+ def has_explicit_builder(self):
+ return 1
+ def side_effect(self):
+ return 1
+ def precious(self):
+ return 1
+ def always_build(self):
+ return 1
+ def current(self):
+ return 1
+
+class LiteralTestCase(unittest.TestCase):
+ def test_Literal(self):
+ """Test the Literal() function."""
+ input_list = [ '$FOO', Literal('$BAR') ]
+ gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
+
+ def escape_func(cmd):
+ return '**' + cmd + '**'
+
+ cmd_list = scons_subst_list(input_list, None, gvars=gvars)
+ cmd_list = escape_list(cmd_list[0], escape_func)
+ assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
+
+class SpecialAttrWrapperTestCase(unittest.TestCase):
+ def test_SpecialAttrWrapper(self):
+ """Test the SpecialAttrWrapper() function."""
+ input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
+ gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
+
+ def escape_func(cmd):
+ return '**' + cmd + '**'
+
+ cmd_list = scons_subst_list(input_list, None, gvars=gvars)
+ cmd_list = escape_list(cmd_list[0], escape_func)
+ assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
+
+ cmd_list = scons_subst_list(input_list, None, mode=SUBST_SIG, gvars=gvars)
+ cmd_list = escape_list(cmd_list[0], escape_func)
+ assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
+
+class subst_dict_TestCase(unittest.TestCase):
+ def test_subst_dict(self):
+ """Test substituting dictionary values in an Action
+ """
+ t = DummyNode('t')
+ s = DummyNode('s')
+ d = subst_dict(target=t, source=s)
+ assert str(d['TARGETS'][0]) == 't', d['TARGETS']
+ assert str(d['TARGET']) == 't', d['TARGET']
+ assert str(d['SOURCES'][0]) == 's', d['SOURCES']
+ assert str(d['SOURCE']) == 's', d['SOURCE']
+
+ t1 = DummyNode('t1')
+ t2 = DummyNode('t2')
+ s1 = DummyNode('s1')
+ s2 = DummyNode('s2')
+ d = subst_dict(target=[t1, t2], source=[s1, s2])
+ TARGETS = map(lambda x: str(x), d['TARGETS'])
+ TARGETS.sort()
+ assert TARGETS == ['t1', 't2'], d['TARGETS']
+ assert str(d['TARGET']) == 't1', d['TARGET']
+ SOURCES = map(lambda x: str(x), d['SOURCES'])
+ SOURCES.sort()
+ assert SOURCES == ['s1', 's2'], d['SOURCES']
+ assert str(d['SOURCE']) == 's1', d['SOURCE']
+
+ class V:
+ # Fake Value node with no rfile() method.
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return 'v-'+self.name
+ def get_subst_proxy(self):
+ return self
+
+ class N(V):
+ def rfile(self):
+ return self.__class__('rstr-' + self.name)
+
+ t3 = N('t3')
+ t4 = DummyNode('t4')
+ t5 = V('t5')
+ s3 = DummyNode('s3')
+ s4 = N('s4')
+ s5 = V('s5')
+ d = subst_dict(target=[t3, t4, t5], source=[s3, s4, s5])
+ TARGETS = map(lambda x: str(x), d['TARGETS'])
+ TARGETS.sort()
+ assert TARGETS == ['t4', 'v-t3', 'v-t5'], TARGETS
+ SOURCES = map(lambda x: str(x), d['SOURCES'])
+ SOURCES.sort()
+ assert SOURCES == ['s3', 'v-rstr-s4', 'v-s5'], SOURCES
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ CLVar_TestCase,
+ LiteralTestCase,
+ SpecialAttrWrapperTestCase,
+ quote_spaces_TestCase,
+ scons_subst_TestCase,
+ scons_subst_list_TestCase,
+ scons_subst_once_TestCase,
+ subst_dict_TestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
new file mode 100644
index 0000000..1da11bd
--- /dev/null
+++ b/src/engine/SCons/Taskmaster.py
@@ -0,0 +1,1030 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = """
+Generic Taskmaster module for the SCons build engine.
+
+This module contains the primary interface(s) between a wrapping user
+interface and the SCons build engine. There are two key classes here:
+
+ Taskmaster
+ This is the main engine for walking the dependency graph and
+ calling things to decide what does or doesn't need to be built.
+
+ Task
+ This is the base class for allowing a wrapping interface to
+ decide what does or doesn't actually need to be done. The
+ intention is for a wrapping interface to subclass this as
+ appropriate for different types of behavior it may need.
+
+ The canonical example is the SCons native Python interface,
+ which has Task subclasses that handle its specific behavior,
+ like printing "`foo' is up to date" when a top-level target
+ doesn't need to be built, and handling the -c option by removing
+ targets as its "build" action. There is also a separate subclass
+ for suppressing this output when the -q option is used.
+
+ The Taskmaster instantiates a Task object for each (set of)
+ target(s) that it decides need to be evaluated and/or built.
+"""
+
+__revision__ = "src/engine/SCons/Taskmaster.py 4577 2009/12/27 19:44:43 scons"
+
+from itertools import chain
+import operator
+import string
+import sys
+import traceback
+
+import SCons.Errors
+import SCons.Node
+import SCons.Warnings
+
+StateString = SCons.Node.StateString
+NODE_NO_STATE = SCons.Node.no_state
+NODE_PENDING = SCons.Node.pending
+NODE_EXECUTING = SCons.Node.executing
+NODE_UP_TO_DATE = SCons.Node.up_to_date
+NODE_EXECUTED = SCons.Node.executed
+NODE_FAILED = SCons.Node.failed
+
+
+# A subsystem for recording stats about how different Nodes are handled by
+# the main Taskmaster loop. There's no external control here (no need for
+# a --debug= option); enable it by changing the value of CollectStats.
+
+CollectStats = None
+
+class Stats:
+ """
+ A simple class for holding statistics about the disposition of a
+ Node by the Taskmaster. If we're collecting statistics, each Node
+ processed by the Taskmaster gets one of these attached, in which case
+ the Taskmaster records its decision each time it processes the Node.
+ (Ideally, that's just once per Node.)
+ """
+ def __init__(self):
+ """
+ Instantiates a Taskmaster.Stats object, initializing all
+ appropriate counters to zero.
+ """
+ self.considered = 0
+ self.already_handled = 0
+ self.problem = 0
+ self.child_failed = 0
+ self.not_built = 0
+ self.side_effects = 0
+ self.build = 0
+
+StatsNodes = []
+
+fmt = "%(considered)3d "\
+ "%(already_handled)3d " \
+ "%(problem)3d " \
+ "%(child_failed)3d " \
+ "%(not_built)3d " \
+ "%(side_effects)3d " \
+ "%(build)3d "
+
+def dump_stats():
+ StatsNodes.sort(lambda a, b: cmp(str(a), str(b)))
+ for n in StatsNodes:
+ print (fmt % n.stats.__dict__) + str(n)
+
+
+
+class Task:
+ """
+ Default SCons build engine task.
+
+ This controls the interaction of the actual building of node
+ and the rest of the engine.
+
+ This is expected to handle all of the normally-customizable
+ aspects of controlling a build, so any given application
+ *should* be able to do what it wants by sub-classing this
+ class and overriding methods as appropriate. If an application
+ needs to customze something by sub-classing Taskmaster (or
+ some other build engine class), we should first try to migrate
+ that functionality into this class.
+
+ Note that it's generally a good idea for sub-classes to call
+ these methods explicitly to update state, etc., rather than
+ roll their own interaction with Taskmaster from scratch.
+ """
+ def __init__(self, tm, targets, top, node):
+ self.tm = tm
+ self.targets = targets
+ self.top = top
+ self.node = node
+ self.exc_clear()
+
+ def trace_message(self, method, node, description='node'):
+ fmt = '%-20s %s %s\n'
+ return fmt % (method + ':', description, self.tm.trace_node(node))
+
+ def display(self, message):
+ """
+ Hook to allow the calling interface to display a message.
+
+ This hook gets called as part of preparing a task for execution
+ (that is, a Node to be built). As part of figuring out what Node
+ should be built next, the actually target list may be altered,
+ along with a message describing the alteration. The calling
+ interface can subclass Task and provide a concrete implementation
+ of this method to see those messages.
+ """
+ pass
+
+ def prepare(self):
+ """
+ Called just before the task is executed.
+
+ This is mainly intended to give the target Nodes a chance to
+ unlink underlying files and make all necessary directories before
+ the Action is actually called to build the targets.
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.prepare()', self.node))
+
+ # Now that it's the appropriate time, give the TaskMaster a
+ # chance to raise any exceptions it encountered while preparing
+ # this task.
+ self.exception_raise()
+
+ if self.tm.message:
+ self.display(self.tm.message)
+ self.tm.message = None
+
+ # Let the targets take care of any necessary preparations.
+ # This includes verifying that all of the necessary sources
+ # and dependencies exist, removing the target file(s), etc.
+ #
+ # As of April 2008, the get_executor().prepare() method makes
+ # sure that all of the aggregate sources necessary to build this
+ # Task's target(s) exist in one up-front check. The individual
+ # target t.prepare() methods check that each target's explicit
+ # or implicit dependencies exists, and also initialize the
+ # .sconsign info.
+ executor = self.targets[0].get_executor()
+ executor.prepare()
+ for t in executor.get_action_targets():
+ t.prepare()
+ for s in t.side_effects:
+ s.prepare()
+
+ def get_target(self):
+ """Fetch the target being built or updated by this task.
+ """
+ return self.node
+
+ def needs_execute(self):
+ # TODO(deprecate): "return True" is the old default behavior;
+ # change it to NotImplementedError (after running through the
+ # Deprecation Cycle) so the desired behavior is explicitly
+ # determined by which concrete subclass is used.
+ #raise NotImplementedError
+ msg = ('Direct use of the Taskmaster.Task class will be deprecated\n'
+ + '\tin a future release.')
+ SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg)
+ return True
+
+ def execute(self):
+ """
+ Called to execute the task.
+
+ This method is called from multiple threads in a parallel build,
+ so only do thread safe stuff here. Do thread unsafe stuff in
+ prepare(), executed() or failed().
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.execute()', self.node))
+
+ try:
+ everything_was_cached = 1
+ for t in self.targets:
+ if t.retrieve_from_cache():
+ # Call the .built() method without calling the
+ # .push_to_cache() method, since we just got the
+ # target from the cache and don't need to push
+ # it back there.
+ t.set_state(NODE_EXECUTED)
+ t.built()
+ else:
+ everything_was_cached = 0
+ break
+ if not everything_was_cached:
+ self.targets[0].build()
+ except SystemExit:
+ exc_value = sys.exc_info()[1]
+ raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code)
+ except SCons.Errors.UserError:
+ raise
+ except SCons.Errors.BuildError:
+ raise
+ except Exception, e:
+ buildError = SCons.Errors.convert_to_BuildError(e)
+ buildError.node = self.targets[0]
+ buildError.exc_info = sys.exc_info()
+ raise buildError
+
+ def executed_without_callbacks(self):
+ """
+ Called when the task has been successfully executed
+ and the Taskmaster instance doesn't want to call
+ the Node's callback methods.
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.executed_without_callbacks()',
+ self.node))
+
+ for t in self.targets:
+ if t.get_state() == NODE_EXECUTING:
+ for side_effect in t.side_effects:
+ side_effect.set_state(NODE_NO_STATE)
+ t.set_state(NODE_EXECUTED)
+
+ def executed_with_callbacks(self):
+ """
+ Called when the task has been successfully executed and
+ the Taskmaster instance wants to call the Node's callback
+ methods.
+
+ This may have been a do-nothing operation (to preserve build
+ order), so we must check the node's state before deciding whether
+ it was "built", in which case we call the appropriate Node method.
+ In any event, we always call "visited()", which will handle any
+ post-visit actions that must take place regardless of whether
+ or not the target was an actual built target or a source Node.
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.executed_with_callbacks()',
+ self.node))
+
+ for t in self.targets:
+ if t.get_state() == NODE_EXECUTING:
+ for side_effect in t.side_effects:
+ side_effect.set_state(NODE_NO_STATE)
+ t.set_state(NODE_EXECUTED)
+ t.push_to_cache()
+ t.built()
+ t.visited()
+
+ executed = executed_with_callbacks
+
+ def failed(self):
+ """
+ Default action when a task fails: stop the build.
+
+ Note: Although this function is normally invoked on nodes in
+ the executing state, it might also be invoked on up-to-date
+ nodes when using Configure().
+ """
+ self.fail_stop()
+
+ def fail_stop(self):
+ """
+ Explicit stop-the-build failure.
+
+ This sets failure status on the target nodes and all of
+ their dependent parent nodes.
+
+ Note: Although this function is normally invoked on nodes in
+ the executing state, it might also be invoked on up-to-date
+ nodes when using Configure().
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.failed_stop()', self.node))
+
+ # Invoke will_not_build() to clean-up the pending children
+ # list.
+ self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED))
+
+ # Tell the taskmaster to not start any new tasks
+ self.tm.stop()
+
+ # We're stopping because of a build failure, but give the
+ # calling Task class a chance to postprocess() the top-level
+ # target under which the build failure occurred.
+ self.targets = [self.tm.current_top]
+ self.top = 1
+
+ def fail_continue(self):
+ """
+ Explicit continue-the-build failure.
+
+ This sets failure status on the target nodes and all of
+ their dependent parent nodes.
+
+ Note: Although this function is normally invoked on nodes in
+ the executing state, it might also be invoked on up-to-date
+ nodes when using Configure().
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.failed_continue()', self.node))
+
+ self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED))
+
+ def make_ready_all(self):
+ """
+ Marks all targets in a task ready for execution.
+
+ This is used when the interface needs every target Node to be
+ visited--the canonical example being the "scons -c" option.
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.make_ready_all()', self.node))
+
+ self.out_of_date = self.targets[:]
+ for t in self.targets:
+ t.disambiguate().set_state(NODE_EXECUTING)
+ for s in t.side_effects:
+ s.set_state(NODE_EXECUTING)
+
+ def make_ready_current(self):
+ """
+ Marks all targets in a task ready for execution if any target
+ is not current.
+
+ This is the default behavior for building only what's necessary.
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.make_ready_current()',
+ self.node))
+
+ self.out_of_date = []
+ needs_executing = False
+ for t in self.targets:
+ try:
+ t.disambiguate().make_ready()
+ is_up_to_date = not t.has_builder() or \
+ (not t.always_build and t.is_up_to_date())
+ except EnvironmentError, e:
+ raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename)
+
+ if not is_up_to_date:
+ self.out_of_date.append(t)
+ needs_executing = True
+
+ if needs_executing:
+ for t in self.targets:
+ t.set_state(NODE_EXECUTING)
+ for s in t.side_effects:
+ s.set_state(NODE_EXECUTING)
+ else:
+ for t in self.targets:
+ # We must invoke visited() to ensure that the node
+ # information has been computed before allowing the
+ # parent nodes to execute. (That could occur in a
+ # parallel build...)
+ t.visited()
+ t.set_state(NODE_UP_TO_DATE)
+
+ make_ready = make_ready_current
+
+ def postprocess(self):
+ """
+ Post-processes a task after it's been executed.
+
+ This examines all the targets just built (or not, we don't care
+ if the build was successful, or even if there was no build
+ because everything was up-to-date) to see if they have any
+ waiting parent Nodes, or Nodes waiting on a common side effect,
+ that can be put back on the candidates list.
+ """
+ T = self.tm.trace
+ if T: T.write(self.trace_message('Task.postprocess()', self.node))
+
+ # We may have built multiple targets, some of which may have
+ # common parents waiting for this build. Count up how many
+ # targets each parent was waiting for so we can subtract the
+ # values later, and so we *don't* put waiting side-effect Nodes
+ # back on the candidates list if the Node is also a waiting
+ # parent.
+
+ targets = set(self.targets)
+
+ pending_children = self.tm.pending_children
+ parents = {}
+ for t in targets:
+ # A node can only be in the pending_children set if it has
+ # some waiting_parents.
+ if t.waiting_parents:
+ if T: T.write(self.trace_message('Task.postprocess()',
+ t,
+ 'removing'))
+ pending_children.discard(t)
+ for p in t.waiting_parents:
+ parents[p] = parents.get(p, 0) + 1
+
+ for t in targets:
+ for s in t.side_effects:
+ if s.get_state() == NODE_EXECUTING:
+ s.set_state(NODE_NO_STATE)
+ for p in s.waiting_parents:
+ parents[p] = parents.get(p, 0) + 1
+ for p in s.waiting_s_e:
+ if p.ref_count == 0:
+ self.tm.candidates.append(p)
+
+ for p, subtract in parents.items():
+ p.ref_count = p.ref_count - subtract
+ if T: T.write(self.trace_message('Task.postprocess()',
+ p,
+ 'adjusted parent ref count'))
+ if p.ref_count == 0:
+ self.tm.candidates.append(p)
+
+ for t in targets:
+ t.postprocess()
+
+ # Exception handling subsystem.
+ #
+ # Exceptions that occur while walking the DAG or examining Nodes
+ # must be raised, but must be raised at an appropriate time and in
+ # a controlled manner so we can, if necessary, recover gracefully,
+ # possibly write out signature information for Nodes we've updated,
+ # etc. This is done by having the Taskmaster tell us about the
+ # exception, and letting
+
+ def exc_info(self):
+ """
+ Returns info about a recorded exception.
+ """
+ return self.exception
+
+ def exc_clear(self):
+ """
+ Clears any recorded exception.
+
+ This also changes the "exception_raise" attribute to point
+ to the appropriate do-nothing method.
+ """
+ self.exception = (None, None, None)
+ self.exception_raise = self._no_exception_to_raise
+
+ def exception_set(self, exception=None):
+ """
+ Records an exception to be raised at the appropriate time.
+
+ This also changes the "exception_raise" attribute to point
+ to the method that will, in fact
+ """
+ if not exception:
+ exception = sys.exc_info()
+ self.exception = exception
+ self.exception_raise = self._exception_raise
+
+ def _no_exception_to_raise(self):
+ pass
+
+ def _exception_raise(self):
+ """
+ Raises a pending exception that was recorded while getting a
+ Task ready for execution.
+ """
+ exc = self.exc_info()[:]
+ try:
+ exc_type, exc_value, exc_traceback = exc
+ except ValueError:
+ exc_type, exc_value = exc
+ exc_traceback = None
+ raise exc_type, exc_value, exc_traceback
+
+class AlwaysTask(Task):
+ def needs_execute(self):
+ """
+ Always returns True (indicating this Task should always
+ be executed).
+
+ Subclasses that need this behavior (as opposed to the default
+ of only executing Nodes that are out of date w.r.t. their
+ dependencies) can use this as follows:
+
+ class MyTaskSubclass(SCons.Taskmaster.Task):
+ needs_execute = SCons.Taskmaster.Task.execute_always
+ """
+ return True
+
+class OutOfDateTask(Task):
+ def needs_execute(self):
+ """
+ Returns True (indicating this Task should be executed) if this
+ Task's target state indicates it needs executing, which has
+ already been determined by an earlier up-to-date check.
+ """
+ return self.targets[0].get_state() == SCons.Node.executing
+
+
+def find_cycle(stack, visited):
+ if stack[-1] in visited:
+ return None
+ visited.add(stack[-1])
+ for n in stack[-1].waiting_parents:
+ stack.append(n)
+ if stack[0] == stack[-1]:
+ return stack
+ if find_cycle(stack, visited):
+ return stack
+ stack.pop()
+ return None
+
+
+class Taskmaster:
+ """
+ The Taskmaster for walking the dependency DAG.
+ """
+
+ def __init__(self, targets=[], tasker=None, order=None, trace=None):
+ self.original_top = targets
+ self.top_targets_left = targets[:]
+ self.top_targets_left.reverse()
+ self.candidates = []
+ if tasker is None:
+ tasker = OutOfDateTask
+ self.tasker = tasker
+ if not order:
+ order = lambda l: l
+ self.order = order
+ self.message = None
+ self.trace = trace
+ self.next_candidate = self.find_next_candidate
+ self.pending_children = set()
+
+ def find_next_candidate(self):
+ """
+ Returns the next candidate Node for (potential) evaluation.
+
+ The candidate list (really a stack) initially consists of all of
+ the top-level (command line) targets provided when the Taskmaster
+ was initialized. While we walk the DAG, visiting Nodes, all the
+ children that haven't finished processing get pushed on to the
+ candidate list. Each child can then be popped and examined in
+ turn for whether *their* children are all up-to-date, in which
+ case a Task will be created for their actual evaluation and
+ potential building.
+
+ Here is where we also allow candidate Nodes to alter the list of
+ Nodes that should be examined. This is used, for example, when
+ invoking SCons in a source directory. A source directory Node can
+ return its corresponding build directory Node, essentially saying,
+ "Hey, you really need to build this thing over here instead."
+ """
+ try:
+ return self.candidates.pop()
+ except IndexError:
+ pass
+ try:
+ node = self.top_targets_left.pop()
+ except IndexError:
+ return None
+ self.current_top = node
+ alt, message = node.alter_targets()
+ if alt:
+ self.message = message
+ self.candidates.append(node)
+ self.candidates.extend(self.order(alt))
+ node = self.candidates.pop()
+ return node
+
+ def no_next_candidate(self):
+ """
+ Stops Taskmaster processing by not returning a next candidate.
+
+ Note that we have to clean-up the Taskmaster candidate list
+ because the cycle detection depends on the fact all nodes have
+ been processed somehow.
+ """
+ while self.candidates:
+ candidates = self.candidates
+ self.candidates = []
+ self.will_not_build(candidates)
+ return None
+
+ def _validate_pending_children(self):
+ """
+ Validate the content of the pending_children set. Assert if an
+ internal error is found.
+
+ This function is used strictly for debugging the taskmaster by
+ checking that no invariants are violated. It is not used in
+ normal operation.
+
+ The pending_children set is used to detect cycles in the
+ dependency graph. We call a "pending child" a child that is
+ found in the "pending" state when checking the dependencies of
+ its parent node.
+
+ A pending child can occur when the Taskmaster completes a loop
+ through a cycle. For example, lets imagine a graph made of
+ three node (A, B and C) making a cycle. The evaluation starts
+ at node A. The taskmaster first consider whether node A's
+ child B is up-to-date. Then, recursively, node B needs to
+ check whether node C is up-to-date. This leaves us with a
+ dependency graph looking like:
+
+ Next candidate \
+ \
+ Node A (Pending) --> Node B(Pending) --> Node C (NoState)
+ ^ |
+ | |
+ +-------------------------------------+
+
+ Now, when the Taskmaster examines the Node C's child Node A,
+ it finds that Node A is in the "pending" state. Therefore,
+ Node A is a pending child of node C.
+
+ Pending children indicate that the Taskmaster has potentially
+ loop back through a cycle. We say potentially because it could
+ also occur when a DAG is evaluated in parallel. For example,
+ consider the following graph:
+
+
+ Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ...
+ | ^
+ | |
+ +----------> Node D (NoState) --------+
+ /
+ Next candidate /
+
+ The Taskmaster first evaluates the nodes A, B, and C and
+ starts building some children of node C. Assuming, that the
+ maximum parallel level has not been reached, the Taskmaster
+ will examine Node D. It will find that Node C is a pending
+ child of Node D.
+
+ In summary, evaluating a graph with a cycle will always
+ involve a pending child at one point. A pending child might
+ indicate either a cycle or a diamond-shaped DAG. Only a
+ fraction of the nodes ends-up being a "pending child" of
+ another node. This keeps the pending_children set small in
+ practice.
+
+ We can differentiate between the two cases if we wait until
+ the end of the build. At this point, all the pending children
+ nodes due to a diamond-shaped DAG will have been properly
+ built (or will have failed to build). But, the pending
+ children involved in a cycle will still be in the pending
+ state.
+
+ The taskmaster removes nodes from the pending_children set as
+ soon as a pending_children node moves out of the pending
+ state. This also helps to keep the pending_children set small.
+ """
+
+ for n in self.pending_children:
+ assert n.state in (NODE_PENDING, NODE_EXECUTING), \
+ (str(n), StateString[n.state])
+ assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents))
+ for p in n.waiting_parents:
+ assert p.ref_count > 0, (str(n), str(p), p.ref_count)
+
+
+ def trace_message(self, message):
+ return 'Taskmaster: %s\n' % message
+
+ def trace_node(self, node):
+ return '<%-10s %-3s %s>' % (StateString[node.get_state()],
+ node.ref_count,
+ repr(str(node)))
+
+ def _find_next_ready_node(self):
+ """
+ Finds the next node that is ready to be built.
+
+ This is *the* main guts of the DAG walk. We loop through the
+ list of candidates, looking for something that has no un-built
+ children (i.e., that is a leaf Node or has dependencies that are
+ all leaf Nodes or up-to-date). Candidate Nodes are re-scanned
+ (both the target Node itself and its sources, which are always
+ scanned in the context of a given target) to discover implicit
+ dependencies. A Node that must wait for some children to be
+ built will be put back on the candidates list after the children
+ have finished building. A Node that has been put back on the
+ candidates list in this way may have itself (or its sources)
+ re-scanned, in order to handle generated header files (e.g.) and
+ the implicit dependencies therein.
+
+ Note that this method does not do any signature calculation or
+ up-to-date check itself. All of that is handled by the Task
+ class. This is purely concerned with the dependency graph walk.
+ """
+
+ self.ready_exc = None
+
+ T = self.trace
+ if T: T.write('\n' + self.trace_message('Looking for a node to evaluate'))
+
+ while 1:
+ node = self.next_candidate()
+ if node is None:
+ if T: T.write(self.trace_message('No candidate anymore.') + '\n')
+ return None
+
+ node = node.disambiguate()
+ state = node.get_state()
+
+ # For debugging only:
+ #
+ # try:
+ # self._validate_pending_children()
+ # except:
+ # self.ready_exc = sys.exc_info()
+ # return node
+
+ if CollectStats:
+ if not hasattr(node, 'stats'):
+ node.stats = Stats()
+ StatsNodes.append(node)
+ S = node.stats
+ S.considered = S.considered + 1
+ else:
+ S = None
+
+ if T: T.write(self.trace_message(' Considering node %s and its children:' % self.trace_node(node)))
+
+ if state == NODE_NO_STATE:
+ # Mark this node as being on the execution stack:
+ node.set_state(NODE_PENDING)
+ elif state > NODE_PENDING:
+ # Skip this node if it has already been evaluated:
+ if S: S.already_handled = S.already_handled + 1
+ if T: T.write(self.trace_message(' already handled (executed)'))
+ continue
+
+ executor = node.get_executor()
+
+ try:
+ children = executor.get_all_children()
+ except SystemExit:
+ exc_value = sys.exc_info()[1]
+ e = SCons.Errors.ExplicitExit(node, exc_value.code)
+ self.ready_exc = (SCons.Errors.ExplicitExit, e)
+ if T: T.write(self.trace_message(' SystemExit'))
+ return node
+ except Exception, e:
+ # We had a problem just trying to figure out the
+ # children (like a child couldn't be linked in to a
+ # VariantDir, or a Scanner threw something). Arrange to
+ # raise the exception when the Task is "executed."
+ self.ready_exc = sys.exc_info()
+ if S: S.problem = S.problem + 1
+ if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e))
+ return node
+
+ children_not_visited = []
+ children_pending = set()
+ children_not_ready = []
+ children_failed = False
+
+ for child in chain(executor.get_all_prerequisites(), children):
+ childstate = child.get_state()
+
+ if T: T.write(self.trace_message(' ' + self.trace_node(child)))
+
+ if childstate == NODE_NO_STATE:
+ children_not_visited.append(child)
+ elif childstate == NODE_PENDING:
+ children_pending.add(child)
+ elif childstate == NODE_FAILED:
+ children_failed = True
+
+ if childstate <= NODE_EXECUTING:
+ children_not_ready.append(child)
+
+
+ # These nodes have not even been visited yet. Add
+ # them to the list so that on some next pass we can
+ # take a stab at evaluating them (or their children).
+ children_not_visited.reverse()
+ self.candidates.extend(self.order(children_not_visited))
+ #if T and children_not_visited:
+ # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited)))
+ # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates)))
+
+ # Skip this node if any of its children have failed.
+ #
+ # This catches the case where we're descending a top-level
+ # target and one of our children failed while trying to be
+ # built by a *previous* descent of an earlier top-level
+ # target.
+ #
+ # It can also occur if a node is reused in multiple
+ # targets. One first descends though the one of the
+ # target, the next time occurs through the other target.
+ #
+ # Note that we can only have failed_children if the
+ # --keep-going flag was used, because without it the build
+ # will stop before diving in the other branch.
+ #
+ # Note that even if one of the children fails, we still
+ # added the other children to the list of candidate nodes
+ # to keep on building (--keep-going).
+ if children_failed:
+ for n in executor.get_action_targets():
+ n.set_state(NODE_FAILED)
+
+ if S: S.child_failed = S.child_failed + 1
+ if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node)))
+ continue
+
+ if children_not_ready:
+ for child in children_not_ready:
+ # We're waiting on one or more derived targets
+ # that have not yet finished building.
+ if S: S.not_built = S.not_built + 1
+
+ # Add this node to the waiting parents lists of
+ # anything we're waiting on, with a reference
+ # count so we can be put back on the list for
+ # re-evaluation when they've all finished.
+ node.ref_count = node.ref_count + child.add_to_waiting_parents(node)
+ if T: T.write(self.trace_message(' adjusted ref count: %s, child %s' %
+ (self.trace_node(node), repr(str(child)))))
+
+ if T:
+ for pc in children_pending:
+ T.write(self.trace_message(' adding %s to the pending children set\n' %
+ self.trace_node(pc)))
+ self.pending_children = self.pending_children | children_pending
+
+ continue
+
+ # Skip this node if it has side-effects that are
+ # currently being built:
+ wait_side_effects = False
+ for se in executor.get_action_side_effects():
+ if se.get_state() == NODE_EXECUTING:
+ se.add_to_waiting_s_e(node)
+ wait_side_effects = True
+
+ if wait_side_effects:
+ if S: S.side_effects = S.side_effects + 1
+ continue
+
+ # The default when we've gotten through all of the checks above:
+ # this node is ready to be built.
+ if S: S.build = S.build + 1
+ if T: T.write(self.trace_message('Evaluating %s\n' %
+ self.trace_node(node)))
+
+ # For debugging only:
+ #
+ # try:
+ # self._validate_pending_children()
+ # except:
+ # self.ready_exc = sys.exc_info()
+ # return node
+
+ return node
+
+ return None
+
+ def next_task(self):
+ """
+ Returns the next task to be executed.
+
+ This simply asks for the next Node to be evaluated, and then wraps
+ it in the specific Task subclass with which we were initialized.
+ """
+ node = self._find_next_ready_node()
+
+ if node is None:
+ return None
+
+ tlist = node.get_executor().get_all_targets()
+
+ task = self.tasker(self, tlist, node in self.original_top, node)
+ try:
+ task.make_ready()
+ except:
+ # We had a problem just trying to get this task ready (like
+ # a child couldn't be linked in to a VariantDir when deciding
+ # whether this node is current). Arrange to raise the
+ # exception when the Task is "executed."
+ self.ready_exc = sys.exc_info()
+
+ if self.ready_exc:
+ task.exception_set(self.ready_exc)
+
+ self.ready_exc = None
+
+ return task
+
+ def will_not_build(self, nodes, node_func=lambda n: None):
+ """
+ Perform clean-up about nodes that will never be built. Invokes
+ a user defined function on all of these nodes (including all
+ of their parents).
+ """
+
+ T = self.trace
+
+ pending_children = self.pending_children
+
+ to_visit = set(nodes)
+ pending_children = pending_children - to_visit
+
+ if T:
+ for n in nodes:
+ T.write(self.trace_message(' removing node %s from the pending children set\n' %
+ self.trace_node(n)))
+ try:
+ while 1:
+ try:
+ node = to_visit.pop()
+ except AttributeError:
+ # Python 1.5.2
+ if len(to_visit):
+ node = to_visit[0]
+ to_visit.remove(node)
+ else:
+ break
+
+ node_func(node)
+
+ # Prune recursion by flushing the waiting children
+ # list immediately.
+ parents = node.waiting_parents
+ node.waiting_parents = set()
+
+ to_visit = to_visit | parents
+ pending_children = pending_children - parents
+
+ for p in parents:
+ p.ref_count = p.ref_count - 1
+ if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' %
+ self.trace_node(p)))
+ except KeyError:
+ # The container to_visit has been emptied.
+ pass
+
+ # We have the stick back the pending_children list into the
+ # task master because the python 1.5.2 compatibility does not
+ # allow us to use in-place updates
+ self.pending_children = pending_children
+
+ def stop(self):
+ """
+ Stops the current build completely.
+ """
+ self.next_candidate = self.no_next_candidate
+
+ def cleanup(self):
+ """
+ Check for dependency cycles.
+ """
+ if not self.pending_children:
+ return
+
+ # TODO(1.5)
+ #nclist = [ (n, find_cycle([n], set())) for n in self.pending_children ]
+ nclist = map(lambda n: (n, find_cycle([n], set())), self.pending_children)
+
+ # TODO(1.5)
+ #genuine_cycles = [
+ # node for node, cycle in nclist
+ # if cycle or node.get_state() != NODE_EXECUTED
+ #]
+ genuine_cycles = filter(lambda t: t[1] or t[0].get_state() != NODE_EXECUTED, nclist)
+ if not genuine_cycles:
+ # All of the "cycles" found were single nodes in EXECUTED state,
+ # which is to say, they really weren't cycles. Just return.
+ return
+
+ desc = 'Found dependency cycle(s):\n'
+ for node, cycle in nclist:
+ if cycle:
+ desc = desc + " " + string.join(map(str, cycle), " -> ") + "\n"
+ else:
+ desc = desc + \
+ " Internal Error: no cycle found for node %s (%s) in state %s\n" % \
+ (node, repr(node), StateString[node.get_state()])
+
+ raise SCons.Errors.UserError, desc
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
new file mode 100644
index 0000000..cef256c
--- /dev/null
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -0,0 +1,1138 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/TaskmasterTests.py 4577 2009/12/27 19:44:43 scons"
+
+import copy
+import sys
+import unittest
+
+import SCons.Taskmaster
+import SCons.Errors
+
+
+built_text = None
+cache_text = []
+visited_nodes = []
+executed = None
+scan_called = 0
+
+class Node:
+ def __init__(self, name, kids = [], scans = []):
+ self.name = name
+ self.kids = kids
+ self.scans = scans
+ self.cached = 0
+ self.scanned = 0
+ self.scanner = None
+ self.targets = [self]
+ self.prerequisites = []
+ class Builder:
+ def targets(self, node):
+ return node.targets
+ self.builder = Builder()
+ self.bsig = None
+ self.csig = None
+ self.state = SCons.Node.no_state
+ self.prepared = None
+ self.ref_count = 0
+ self.waiting_parents = set()
+ self.waiting_s_e = set()
+ self.side_effect = 0
+ self.side_effects = []
+ self.alttargets = []
+ self.postprocessed = None
+ self._bsig_val = None
+ self._current_val = 0
+ self.always_build = None
+
+ def disambiguate(self):
+ return self
+
+ def push_to_cache(self):
+ pass
+
+ def retrieve_from_cache(self):
+ global cache_text
+ if self.cached:
+ cache_text.append(self.name + " retrieved")
+ return self.cached
+
+ def make_ready(self):
+ pass
+
+ def prepare(self):
+ self.prepared = 1
+
+ def build(self):
+ global built_text
+ built_text = self.name + " built"
+
+ def built(self):
+ global built_text
+ if not self.cached:
+ built_text = built_text + " really"
+
+ def has_builder(self):
+ return not self.builder is None
+
+ def is_derived(self):
+ return self.has_builder or self.side_effect
+
+ def alter_targets(self):
+ return self.alttargets, None
+
+ def visited(self):
+ global visited_nodes
+ visited_nodes.append(self.name)
+
+ def children(self):
+ if not self.scanned:
+ self.scan()
+ self.scanned = 1
+ return self.kids
+
+ def scan(self):
+ global scan_called
+ scan_called = scan_called + 1
+ self.kids = self.kids + self.scans
+ self.scans = []
+
+ def scanner_key(self):
+ return self.name
+
+ def add_to_waiting_parents(self, node):
+ wp = self.waiting_parents
+ if node in wp:
+ return 0
+ wp.add(node)
+ return 1
+
+ def get_state(self):
+ return self.state
+
+ def set_state(self, state):
+ self.state = state
+
+ def set_bsig(self, bsig):
+ self.bsig = bsig
+
+ def set_csig(self, csig):
+ self.csig = csig
+
+ def store_csig(self):
+ pass
+
+ def store_bsig(self):
+ pass
+
+ def is_pseudo_derived(self):
+ pass
+
+ def is_up_to_date(self):
+ return self._current_val
+
+ def depends_on(self, nodes):
+ for node in nodes:
+ if node in self.kids:
+ return 1
+ return 0
+
+ def __str__(self):
+ return self.name
+
+ def postprocess(self):
+ self.postprocessed = 1
+ self.waiting_parents = set()
+
+ def get_executor(self):
+ if not hasattr(self, 'executor'):
+ class Executor:
+ def prepare(self):
+ pass
+ def get_action_targets(self):
+ return self.targets
+ def get_all_targets(self):
+ return self.targets
+ def get_all_children(self):
+ result = []
+ for node in self.targets:
+ result.extend(node.children())
+ return result
+ def get_all_prerequisites(self):
+ return []
+ def get_action_side_effects(self):
+ return []
+ self.executor = Executor()
+ self.executor.targets = self.targets
+ return self.executor
+
+class OtherError(Exception):
+ pass
+
+class MyException(Exception):
+ pass
+
+
+class TaskmasterTestCase(unittest.TestCase):
+
+ def test_next_task(self):
+ """Test fetching the next task
+ """
+ global built_text
+
+ n1 = Node("n1")
+ tm = SCons.Taskmaster.Taskmaster([n1, n1])
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ t = tm.next_task()
+ assert t is None
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1, n2])
+
+ tm = SCons.Taskmaster.Taskmaster([n3])
+
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n1 built", built_text
+ t.executed()
+ t.postprocess()
+
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n2 built", built_text
+ t.executed()
+ t.postprocess()
+
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n3 built", built_text
+ t.executed()
+ t.postprocess()
+
+ assert tm.next_task() is None
+
+ built_text = "up to date: "
+ top_node = n3
+
+ class MyTask(SCons.Taskmaster.Task):
+ def execute(self):
+ global built_text
+ if self.targets[0].get_state() == SCons.Node.up_to_date:
+ if self.top:
+ built_text = self.targets[0].name + " up-to-date top"
+ else:
+ built_text = self.targets[0].name + " up-to-date"
+ else:
+ self.targets[0].build()
+
+ n1.set_state(SCons.Node.no_state)
+ n1._current_val = 1
+ n2.set_state(SCons.Node.no_state)
+ n2._current_val = 1
+ n3.set_state(SCons.Node.no_state)
+ n3._current_val = 1
+ tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask)
+
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n1 up-to-date", built_text
+ t.executed()
+ t.postprocess()
+
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n2 up-to-date", built_text
+ t.executed()
+ t.postprocess()
+
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n3 up-to-date top", built_text
+ t.executed()
+ t.postprocess()
+
+ assert tm.next_task() is None
+
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1, n2])
+ n4 = Node("n4")
+ n5 = Node("n5", [n3, n4])
+ tm = SCons.Taskmaster.Taskmaster([n5])
+
+ t1 = tm.next_task()
+ assert t1.get_target() == n1
+
+ t2 = tm.next_task()
+ assert t2.get_target() == n2
+
+ t4 = tm.next_task()
+ assert t4.get_target() == n4
+ t4.executed()
+ t4.postprocess()
+
+ t1.executed()
+ t1.postprocess()
+ t2.executed()
+ t2.postprocess()
+ t3 = tm.next_task()
+ assert t3.get_target() == n3
+
+ t3.executed()
+ t3.postprocess()
+ t5 = tm.next_task()
+ assert t5.get_target() == n5, t5.get_target()
+ t5.executed()
+ t5.postprocess()
+
+ assert tm.next_task() is None
+
+
+ n4 = Node("n4")
+ n4.set_state(SCons.Node.executed)
+ tm = SCons.Taskmaster.Taskmaster([n4])
+ assert tm.next_task() is None
+
+ n1 = Node("n1")
+ n2 = Node("n2", [n1])
+ tm = SCons.Taskmaster.Taskmaster([n2,n2])
+ t = tm.next_task()
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert tm.next_task() is None
+
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1], [n2])
+ tm = SCons.Taskmaster.Taskmaster([n3])
+ t = tm.next_task()
+ target = t.get_target()
+ assert target == n1, target
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ target = t.get_target()
+ assert target == n2, target
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ target = t.get_target()
+ assert target == n3, target
+ t.executed()
+ t.postprocess()
+ assert tm.next_task() is None
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1, n2])
+ n4 = Node("n4", [n3])
+ n5 = Node("n5", [n3])
+ global scan_called
+ scan_called = 0
+ tm = SCons.Taskmaster.Taskmaster([n4])
+ t = tm.next_task()
+ assert t.get_target() == n1
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n2
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n3
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n4
+ t.executed()
+ t.postprocess()
+ assert tm.next_task() is None
+ assert scan_called == 4, scan_called
+
+ tm = SCons.Taskmaster.Taskmaster([n5])
+ t = tm.next_task()
+ assert t.get_target() == n5, t.get_target()
+ t.executed()
+ assert tm.next_task() is None
+ assert scan_called == 5, scan_called
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3")
+ n4 = Node("n4", [n1,n2,n3])
+ n5 = Node("n5", [n4])
+ n3.side_effect = 1
+ n1.side_effects = n2.side_effects = n3.side_effects = [n4]
+ tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5])
+ t = tm.next_task()
+ assert t.get_target() == n1
+ assert n4.state == SCons.Node.executing, n4.state
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n2
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n3
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n4
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n5
+ assert not tm.next_task()
+ t.executed()
+ t.postprocess()
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3")
+ n4 = Node("n4", [n1,n2,n3])
+ def reverse(dependencies):
+ dependencies.reverse()
+ return dependencies
+ tm = SCons.Taskmaster.Taskmaster([n4], order=reverse)
+ t = tm.next_task()
+ assert t.get_target() == n3, t.get_target()
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n2, t.get_target()
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n1, t.get_target()
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n4, t.get_target()
+ t.executed()
+ t.postprocess()
+
+ n5 = Node("n5")
+ n6 = Node("n6")
+ n7 = Node("n7")
+ n6.alttargets = [n7]
+
+ tm = SCons.Taskmaster.Taskmaster([n5])
+ t = tm.next_task()
+ assert t.get_target() == n5
+ t.executed()
+ t.postprocess()
+
+ tm = SCons.Taskmaster.Taskmaster([n6])
+ t = tm.next_task()
+ assert t.get_target() == n7
+ t.executed()
+ t.postprocess()
+ t = tm.next_task()
+ assert t.get_target() == n6
+ t.executed()
+ t.postprocess()
+
+ n1 = Node("n1")
+ n2 = Node("n2", [n1])
+ n1.set_state(SCons.Node.failed)
+ tm = SCons.Taskmaster.Taskmaster([n2])
+ assert tm.next_task() is None
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n1.targets = [n1, n2]
+ n1._current_val = 1
+ tm = SCons.Taskmaster.Taskmaster([n1])
+ t = tm.next_task()
+ t.executed()
+ t.postprocess()
+
+ s = n1.get_state()
+ assert s == SCons.Node.executed, s
+ s = n2.get_state()
+ assert s == SCons.Node.executed, s
+
+
+ def test_make_ready_out_of_date(self):
+ """Test the Task.make_ready() method's list of out-of-date Nodes
+ """
+ ood = []
+ def TaskGen(tm, targets, top, node, ood=ood):
+ class MyTask(SCons.Taskmaster.Task):
+ def make_ready(self):
+ SCons.Taskmaster.Task.make_ready(self)
+ self.ood.extend(self.out_of_date)
+ t = MyTask(tm, targets, top, node)
+ t.ood = ood
+ return t
+
+ n1 = Node("n1")
+ c2 = Node("c2")
+ c2._current_val = 1
+ n3 = Node("n3")
+ c4 = Node("c4")
+ c4._current_val = 1
+ a5 = Node("a5")
+ a5._current_val = 1
+ a5.always_build = 1
+ tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5],
+ tasker = TaskGen)
+
+ del ood[:]
+ t = tm.next_task()
+ assert ood == [n1], ood
+
+ del ood[:]
+ t = tm.next_task()
+ assert ood == [], ood
+
+ del ood[:]
+ t = tm.next_task()
+ assert ood == [n3], ood
+
+ del ood[:]
+ t = tm.next_task()
+ assert ood == [], ood
+
+ del ood[:]
+ t = tm.next_task()
+ assert ood == [a5], ood
+
+ def test_make_ready_exception(self):
+ """Test handling exceptions from Task.make_ready()
+ """
+ class MyTask(SCons.Taskmaster.Task):
+ def make_ready(self):
+ raise MyException, "from make_ready()"
+
+ n1 = Node("n1")
+ tm = SCons.Taskmaster.Taskmaster(targets = [n1], tasker = MyTask)
+ t = tm.next_task()
+ exc_type, exc_value, exc_tb = t.exception
+ assert exc_type == MyException, repr(exc_type)
+ assert str(exc_value) == "from make_ready()", exc_value
+
+
+ def test_make_ready_all(self):
+ """Test the make_ready_all() method"""
+ class MyTask(SCons.Taskmaster.Task):
+ make_ready = SCons.Taskmaster.Task.make_ready_all
+
+ n1 = Node("n1")
+ c2 = Node("c2")
+ c2._current_val = 1
+ n3 = Node("n3")
+ c4 = Node("c4")
+ c4._current_val = 1
+
+ tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4])
+
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is n1, target
+ assert target.state == SCons.Node.executing, target.state
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is c2, target
+ assert target.state == SCons.Node.up_to_date, target.state
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is n3, target
+ assert target.state == SCons.Node.executing, target.state
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is c4, target
+ assert target.state == SCons.Node.up_to_date, target.state
+ t = tm.next_task()
+ assert t is None
+
+ n1 = Node("n1")
+ c2 = Node("c2")
+ n3 = Node("n3")
+ c4 = Node("c4")
+
+ tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4],
+ tasker = MyTask)
+
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is n1, target
+ assert target.state == SCons.Node.executing, target.state
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is c2, target
+ assert target.state == SCons.Node.executing, target.state
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is n3, target
+ assert target.state == SCons.Node.executing, target.state
+ t = tm.next_task()
+ target = t.get_target()
+ assert target is c4, target
+ assert target.state == SCons.Node.executing, target.state
+ t = tm.next_task()
+ assert t is None
+
+
+ def test_children_errors(self):
+ """Test errors when fetching the children of a node.
+ """
+ class StopNode(Node):
+ def children(self):
+ raise SCons.Errors.StopError, "stop!"
+ class ExitNode(Node):
+ def children(self):
+ sys.exit(77)
+
+ n1 = StopNode("n1")
+ tm = SCons.Taskmaster.Taskmaster([n1])
+ t = tm.next_task()
+ exc_type, exc_value, exc_tb = t.exception
+ assert exc_type == SCons.Errors.StopError, repr(exc_type)
+ assert str(exc_value) == "stop!", exc_value
+
+ n2 = ExitNode("n2")
+ tm = SCons.Taskmaster.Taskmaster([n2])
+ t = tm.next_task()
+ exc_type, exc_value = t.exception
+ assert exc_type == SCons.Errors.ExplicitExit, repr(exc_type)
+ assert exc_value.node == n2, exc_value.node
+ assert exc_value.status == 77, exc_value.status
+
+ def test_cycle_detection(self):
+ """Test detecting dependency cycles
+ """
+ n1 = Node("n1")
+ n2 = Node("n2", [n1])
+ n3 = Node("n3", [n2])
+ n1.kids = [n3]
+
+ tm = SCons.Taskmaster.Taskmaster([n3])
+ try:
+ t = tm.next_task()
+ except SCons.Errors.UserError, e:
+ assert str(e) == "Dependency cycle: n3 -> n1 -> n2 -> n3", str(e)
+ else:
+ assert 'Did not catch expected UserError'
+
+ def test_next_top_level_candidate(self):
+ """Test the next_top_level_candidate() method
+ """
+ n1 = Node("n1")
+ n2 = Node("n2", [n1])
+ n3 = Node("n3", [n2])
+
+ tm = SCons.Taskmaster.Taskmaster([n3])
+ t = tm.next_task()
+ assert t.targets == [n1], t.targets
+ t.fail_stop()
+ assert t.targets == [n3], map(str, t.targets)
+ assert t.top == 1, t.top
+
+ def test_stop(self):
+ """Test the stop() method
+
+ Both default and overridden in a subclass.
+ """
+ global built_text
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1, n2])
+
+ tm = SCons.Taskmaster.Taskmaster([n3])
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ assert built_text == "n1 built", built_text
+ t.executed()
+ t.postprocess()
+ assert built_text == "n1 built really", built_text
+
+ tm.stop()
+ assert tm.next_task() is None
+
+ class MyTM(SCons.Taskmaster.Taskmaster):
+ def stop(self):
+ global built_text
+ built_text = "MyTM.stop()"
+ SCons.Taskmaster.Taskmaster.stop(self)
+
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1, n2])
+
+ built_text = None
+ tm = MyTM([n3])
+ tm.next_task().execute()
+ assert built_text == "n1 built"
+
+ tm.stop()
+ assert built_text == "MyTM.stop()"
+ assert tm.next_task() is None
+
+ def test_executed(self):
+ """Test when a task has been executed
+ """
+ global built_text
+ global visited_nodes
+
+ n1 = Node("n1")
+ tm = SCons.Taskmaster.Taskmaster([n1])
+ t = tm.next_task()
+ built_text = "xxx"
+ visited_nodes = []
+ n1.set_state(SCons.Node.executing)
+
+ t.executed()
+
+ s = n1.get_state()
+ assert s == SCons.Node.executed, s
+ assert built_text == "xxx really", built_text
+ assert visited_nodes == ['n1'], visited_nodes
+
+ n2 = Node("n2")
+ tm = SCons.Taskmaster.Taskmaster([n2])
+ t = tm.next_task()
+ built_text = "should_not_change"
+ visited_nodes = []
+ n2.set_state(None)
+
+ t.executed()
+
+ s = n2.get_state()
+ assert s is None, s
+ assert built_text == "should_not_change", built_text
+ assert visited_nodes == ['n2'], visited_nodes
+
+ n3 = Node("n3")
+ n4 = Node("n4")
+ n3.targets = [n3, n4]
+ tm = SCons.Taskmaster.Taskmaster([n3])
+ t = tm.next_task()
+ visited_nodes = []
+ n3.set_state(SCons.Node.up_to_date)
+ n4.set_state(SCons.Node.executing)
+
+ t.executed()
+
+ s = n3.get_state()
+ assert s == SCons.Node.up_to_date, s
+ s = n4.get_state()
+ assert s == SCons.Node.executed, s
+ assert visited_nodes == ['n3', 'n4'], visited_nodes
+
+ def test_prepare(self):
+ """Test preparation of multiple Nodes for a task
+ """
+ n1 = Node("n1")
+ n2 = Node("n2")
+ tm = SCons.Taskmaster.Taskmaster([n1, n2])
+ t = tm.next_task()
+ # This next line is moderately bogus. We're just reaching
+ # in and setting the targets for this task to an array. The
+ # "right" way to do this would be to have the next_task() call
+ # set it up by having something that approximates a real Builder
+ # return this list--but that's more work than is probably
+ # warranted right now.
+ n1.get_executor().targets = [n1, n2]
+ t.prepare()
+ assert n1.prepared
+ assert n2.prepared
+
+ n3 = Node("n3")
+ n4 = Node("n4")
+ tm = SCons.Taskmaster.Taskmaster([n3, n4])
+ t = tm.next_task()
+ # More bogus reaching in and setting the targets.
+ n3.set_state(SCons.Node.up_to_date)
+ n3.get_executor().targets = [n3, n4]
+ t.prepare()
+ assert n3.prepared
+ assert n4.prepared
+
+ # If the Node has had an exception recorded while it was getting
+ # prepared, then prepare() should raise that exception.
+ class MyException(Exception):
+ pass
+
+ built_text = None
+ n5 = Node("n5")
+ tm = SCons.Taskmaster.Taskmaster([n5])
+ t = tm.next_task()
+ t.exception_set((MyException, "exception value"))
+ exc_caught = None
+ try:
+ t.prepare()
+ except MyException, e:
+ exc_caught = 1
+ except:
+ pass
+ assert exc_caught, "did not catch expected MyException"
+ assert str(e) == "exception value", e
+ assert built_text is None, built_text
+
+ # Regression test, make sure we prepare not only
+ # all targets, but their side effects as well.
+ n6 = Node("n6")
+ n7 = Node("n7")
+ n8 = Node("n8")
+ n9 = Node("n9")
+ n10 = Node("n10")
+
+ n6.side_effects = [ n8 ]
+ n7.side_effects = [ n9, n10 ]
+
+ tm = SCons.Taskmaster.Taskmaster([n6, n7])
+ t = tm.next_task()
+ # More bogus reaching in and setting the targets.
+ n6.get_executor().targets = [n6, n7]
+ t.prepare()
+ assert n6.prepared
+ assert n7.prepared
+ assert n8.prepared
+ assert n9.prepared
+ assert n10.prepared
+
+ # Make sure we call an Executor's prepare() method.
+ class ExceptionExecutor:
+ def prepare(self):
+ raise Exception, "Executor.prepare() exception"
+ def get_all_targets(self):
+ return self.nodes
+ def get_all_children(self):
+ result = []
+ for node in self.nodes:
+ result.extend(node.children())
+ return result
+ def get_all_prerequisites(self):
+ return []
+ def get_action_side_effects(self):
+ return []
+
+ n11 = Node("n11")
+ n11.executor = ExceptionExecutor()
+ n11.executor.nodes = [n11]
+ tm = SCons.Taskmaster.Taskmaster([n11])
+ t = tm.next_task()
+ try:
+ t.prepare()
+ except Exception, e:
+ assert str(e) == "Executor.prepare() exception", e
+ else:
+ raise AssertionError, "did not catch expected exception"
+
+ def test_execute(self):
+ """Test executing a task
+ """
+ global built_text
+ global cache_text
+
+ n1 = Node("n1")
+ tm = SCons.Taskmaster.Taskmaster([n1])
+ t = tm.next_task()
+ t.execute()
+ assert built_text == "n1 built", built_text
+
+ def raise_UserError():
+ raise SCons.Errors.UserError
+ n2 = Node("n2")
+ n2.build = raise_UserError
+ tm = SCons.Taskmaster.Taskmaster([n2])
+ t = tm.next_task()
+ try:
+ t.execute()
+ except SCons.Errors.UserError:
+ pass
+ else:
+ raise TestFailed, "did not catch expected UserError"
+
+ def raise_BuildError():
+ raise SCons.Errors.BuildError
+ n3 = Node("n3")
+ n3.build = raise_BuildError
+ tm = SCons.Taskmaster.Taskmaster([n3])
+ t = tm.next_task()
+ try:
+ t.execute()
+ except SCons.Errors.BuildError:
+ pass
+ else:
+ raise TestFailed, "did not catch expected BuildError"
+
+ # On a generic (non-BuildError) exception from a Builder,
+ # the target should throw a BuildError exception with the
+ # args set to the exception value, instance, and traceback.
+ def raise_OtherError():
+ raise OtherError
+ n4 = Node("n4")
+ n4.build = raise_OtherError
+ tm = SCons.Taskmaster.Taskmaster([n4])
+ t = tm.next_task()
+ try:
+ t.execute()
+ except SCons.Errors.BuildError, e:
+ assert e.node == n4, e.node
+ assert e.errstr == "OtherError : ", e.errstr
+ assert len(e.exc_info) == 3, e.exc_info
+ exc_traceback = sys.exc_info()[2]
+ assert type(e.exc_info[2]) == type(exc_traceback), e.exc_info[2]
+ else:
+ raise TestFailed, "did not catch expected BuildError"
+
+ built_text = None
+ cache_text = []
+ n5 = Node("n5")
+ n6 = Node("n6")
+ n6.cached = 1
+ tm = SCons.Taskmaster.Taskmaster([n5])
+ t = tm.next_task()
+ # This next line is moderately bogus. We're just reaching
+ # in and setting the targets for this task to an array. The
+ # "right" way to do this would be to have the next_task() call
+ # set it up by having something that approximates a real Builder
+ # return this list--but that's more work than is probably
+ # warranted right now.
+ t.targets = [n5, n6]
+ t.execute()
+ assert built_text == "n5 built", built_text
+ assert cache_text == [], cache_text
+
+ built_text = None
+ cache_text = []
+ n7 = Node("n7")
+ n8 = Node("n8")
+ n7.cached = 1
+ n8.cached = 1
+ tm = SCons.Taskmaster.Taskmaster([n7])
+ t = tm.next_task()
+ # This next line is moderately bogus. We're just reaching
+ # in and setting the targets for this task to an array. The
+ # "right" way to do this would be to have the next_task() call
+ # set it up by having something that approximates a real Builder
+ # return this list--but that's more work than is probably
+ # warranted right now.
+ t.targets = [n7, n8]
+ t.execute()
+ assert built_text is None, built_text
+ assert cache_text == ["n7 retrieved", "n8 retrieved"], cache_text
+
+ def test_exception(self):
+ """Test generic Taskmaster exception handling
+
+ """
+ n1 = Node("n1")
+ tm = SCons.Taskmaster.Taskmaster([n1])
+ t = tm.next_task()
+
+ t.exception_set((1, 2))
+ exc_type, exc_value = t.exception
+ assert exc_type == 1, exc_type
+ assert exc_value == 2, exc_value
+
+ t.exception_set(3)
+ assert t.exception == 3
+
+ try: 1/0
+ except: pass
+ t.exception_set(None)
+ exc_type, exc_value, exc_tb = t.exception
+ assert exc_type is ZeroDivisionError, exc_type
+ exception_values = [
+ "integer division or modulo",
+ "integer division or modulo by zero",
+ ]
+ assert str(exc_value) in exception_values, exc_value
+
+ class Exception1(Exception):
+ pass
+
+ t.exception_set((Exception1, None))
+ try:
+ t.exception_raise()
+ except:
+ exc_type, exc_value = sys.exc_info()[:2]
+ assert exc_type == Exception1, exc_type
+ assert str(exc_value) == '', exc_value
+ else:
+ assert 0, "did not catch expected exception"
+
+ class Exception2(Exception):
+ pass
+
+ t.exception_set((Exception2, "xyzzy"))
+ try:
+ t.exception_raise()
+ except:
+ exc_type, exc_value = sys.exc_info()[:2]
+ assert exc_type == Exception2, exc_type
+ assert str(exc_value) == "xyzzy", exc_value
+ else:
+ assert 0, "did not catch expected exception"
+
+ class Exception3(Exception):
+ pass
+
+ try:
+ 1/0
+ except:
+ tb = sys.exc_info()[2]
+ t.exception_set((Exception3, "arg", tb))
+ try:
+ t.exception_raise()
+ except:
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ assert exc_type == Exception3, exc_type
+ assert str(exc_value) == "arg", exc_value
+ import traceback
+ x = traceback.extract_tb(tb)[-1]
+ y = traceback.extract_tb(exc_tb)[-1]
+ assert x == y, "x = %s, y = %s" % (x, y)
+ else:
+ assert 0, "did not catch expected exception"
+
+ def test_postprocess(self):
+ """Test postprocessing targets to give them a chance to clean up
+ """
+ n1 = Node("n1")
+ tm = SCons.Taskmaster.Taskmaster([n1])
+
+ t = tm.next_task()
+ assert not n1.postprocessed
+ t.postprocess()
+ assert n1.postprocessed
+
+ n2 = Node("n2")
+ n3 = Node("n3")
+ tm = SCons.Taskmaster.Taskmaster([n2, n3])
+
+ assert not n2.postprocessed
+ assert not n3.postprocessed
+ t = tm.next_task()
+ t.postprocess()
+ assert n2.postprocessed
+ assert not n3.postprocessed
+ t = tm.next_task()
+ t.postprocess()
+ assert n2.postprocessed
+ assert n3.postprocessed
+
+ def test_trace(self):
+ """Test Taskmaster tracing
+ """
+ import StringIO
+
+ trace = StringIO.StringIO()
+ n1 = Node("n1")
+ n2 = Node("n2")
+ n3 = Node("n3", [n1, n2])
+ tm = SCons.Taskmaster.Taskmaster([n1, n1, n3], trace=trace)
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ t.postprocess()
+ n1.set_state(SCons.Node.executed)
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ t.postprocess()
+ n2.set_state(SCons.Node.executed)
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+ t.postprocess()
+ t = tm.next_task()
+ assert t is None
+
+ value = trace.getvalue()
+ expect = """\
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'n1'> and its children:
+Taskmaster: Evaluating <pending 0 'n1'>
+
+Task.make_ready_current(): node <pending 0 'n1'>
+Task.prepare(): node <executing 0 'n1'>
+Task.execute(): node <executing 0 'n1'>
+Task.postprocess(): node <executing 0 'n1'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <executed 0 'n1'> and its children:
+Taskmaster: already handled (executed)
+Taskmaster: Considering node <no_state 0 'n3'> and its children:
+Taskmaster: <executed 0 'n1'>
+Taskmaster: <no_state 0 'n2'>
+Taskmaster: adjusted ref count: <pending 1 'n3'>, child 'n2'
+Taskmaster: Considering node <no_state 0 'n2'> and its children:
+Taskmaster: Evaluating <pending 0 'n2'>
+
+Task.make_ready_current(): node <pending 0 'n2'>
+Task.prepare(): node <executing 0 'n2'>
+Task.execute(): node <executing 0 'n2'>
+Task.postprocess(): node <executing 0 'n2'>
+Task.postprocess(): removing <executing 0 'n2'>
+Task.postprocess(): adjusted parent ref count <pending 0 'n3'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <pending 0 'n3'> and its children:
+Taskmaster: <executed 0 'n1'>
+Taskmaster: <executed 0 'n2'>
+Taskmaster: Evaluating <pending 0 'n3'>
+
+Task.make_ready_current(): node <pending 0 'n3'>
+Task.prepare(): node <executing 0 'n3'>
+Task.execute(): node <executing 0 'n3'>
+Task.postprocess(): node <executing 0 'n3'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
+
+"""
+ assert value == expect, value
+
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(TaskmasterTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/386asm.py b/src/engine/SCons/Tool/386asm.py
new file mode 100644
index 0000000..7b026e4
--- /dev/null
+++ b/src/engine/SCons/Tool/386asm.py
@@ -0,0 +1,61 @@
+"""SCons.Tool.386asm
+
+Tool specification for the 386ASM assembler for the Phar Lap ETS embedded
+operating system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/386asm.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.PharLapCommon import addPharLapPaths
+import SCons.Util
+
+as_module = __import__('as', globals(), locals(), [])
+
+def generate(env):
+ """Add Builders and construction variables for ar to an Environment."""
+ as_module.generate(env)
+
+ env['AS'] = '386asm'
+ env['ASFLAGS'] = SCons.Util.CLVar('')
+ env['ASPPFLAGS'] = '$ASFLAGS'
+ env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET'
+ env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET'
+
+ addPharLapPaths(env)
+
+def exists(env):
+ return env.Detect('386asm')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/386asm.xml b/src/engine/SCons/Tool/386asm.xml
new file mode 100644
index 0000000..ed48820
--- /dev/null
+++ b/src/engine/SCons/Tool/386asm.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="386asm">
+<summary>
+Sets construction variables for the 386ASM assembler
+for the Phar Lap ETS embedded operating system.
+</summary>
+<sets>
+AS
+ASFLAGS
+ASPPFLAGS
+ASCOM
+ASPPCOM
+</sets>
+<uses>
+CC
+CPPFLAGS
+_CPPDEFFLAGS
+_CPPINCFLAGS
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/BitKeeper.py b/src/engine/SCons/Tool/BitKeeper.py
new file mode 100644
index 0000000..6830ca9
--- /dev/null
+++ b/src/engine/SCons/Tool/BitKeeper.py
@@ -0,0 +1,65 @@
+"""SCons.Tool.BitKeeper.py
+
+Tool-specific initialization for the BitKeeper source code control
+system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/BitKeeper.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+
+def generate(env):
+ """Add a Builder factory function and construction variables for
+ BitKeeper to an Environment."""
+
+ def BitKeeperFactory(env=env):
+ """ """
+ act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR")
+ return SCons.Builder.Builder(action = act, env = env)
+
+ #setattr(env, 'BitKeeper', BitKeeperFactory)
+ env.BitKeeper = BitKeeperFactory
+
+ env['BITKEEPER'] = 'bk'
+ env['BITKEEPERGET'] = '$BITKEEPER get'
+ env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('')
+ env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET'
+
+def exists(env):
+ return env.Detect('bk')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/BitKeeper.xml b/src/engine/SCons/Tool/BitKeeper.xml
new file mode 100644
index 0000000..4eb7a32
--- /dev/null
+++ b/src/engine/SCons/Tool/BitKeeper.xml
@@ -0,0 +1,58 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="BitKeeper">
+<summary>
+Sets construction variables for the BitKeeper
+source code control system.
+</summary>
+<sets>
+BITKEEPER
+BITKEEPERGET
+BITKEEPERGETFLAGS
+BITKEEPERCOM
+</sets>
+<uses>
+BITKEEPERCOMSTR
+</uses>
+</tool>
+
+<cvar name="BITKEEPER">
+<summary>
+The BitKeeper executable.
+</summary>
+</cvar>
+
+<cvar name="BITKEEPERCOM">
+<summary>
+The command line for
+fetching source files using BitKeeper.
+</summary>
+</cvar>
+
+<cvar name="BITKEEPERCOMSTR">
+<summary>
+The string displayed when fetching
+a source file using BitKeeper.
+If this is not set, then &cv-link-BITKEEPERCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="BITKEEPERGET">
+<summary>
+The command (&cv-link-BITKEEPER;) and subcommand
+for fetching source files using BitKeeper.
+</summary>
+</cvar>
+
+<cvar name="BITKEEPERGETFLAGS">
+<summary>
+Options that are passed to the BitKeeper
+<command>get</command>
+subcommand.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/CVS.py b/src/engine/SCons/Tool/CVS.py
new file mode 100644
index 0000000..f2334c8
--- /dev/null
+++ b/src/engine/SCons/Tool/CVS.py
@@ -0,0 +1,73 @@
+"""SCons.Tool.CVS.py
+
+Tool-specific initialization for CVS.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/CVS.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+
+def generate(env):
+ """Add a Builder factory function and construction variables for
+ CVS to an Environment."""
+
+ def CVSFactory(repos, module='', env=env):
+ """ """
+ # fail if repos is not an absolute path name?
+ if module != '':
+ # Don't use os.path.join() because the name we fetch might
+ # be across a network and must use POSIX slashes as separators.
+ module = module + '/'
+ env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}'
+ act = SCons.Action.Action('$CVSCOM', '$CVSCOMSTR')
+ return SCons.Builder.Builder(action = act,
+ env = env,
+ CVSREPOSITORY = repos,
+ CVSMODULE = module)
+
+ #setattr(env, 'CVS', CVSFactory)
+ env.CVS = CVSFactory
+
+ env['CVS'] = 'cvs'
+ env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY')
+ env['CVSCOFLAGS'] = SCons.Util.CLVar('')
+ env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}'
+
+def exists(env):
+ return env.Detect('cvs')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/CVS.xml b/src/engine/SCons/Tool/CVS.xml
new file mode 100644
index 0000000..fdb5360
--- /dev/null
+++ b/src/engine/SCons/Tool/CVS.xml
@@ -0,0 +1,66 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="CVS">
+<summary>
+Sets construction variables for the CVS source code
+management system.
+</summary>
+<sets>
+CVS
+CVSCOM
+CVSFLAGS
+CVSCOFLAGS
+</sets>
+<uses>
+CVSCOMSTR
+</uses>
+</tool>
+
+<cvar name="CVS">
+<summary>
+The CVS executable.
+</summary>
+</cvar>
+
+<cvar name="CVSCOFLAGS">
+<summary>
+Options that are passed to the CVS checkout subcommand.
+</summary>
+</cvar>
+
+<cvar name="CVSCOM">
+<summary>
+The command line used to
+fetch source files from a CVS repository.
+</summary>
+</cvar>
+
+<cvar name="CVSCOMSTR">
+<summary>
+The string displayed when fetching
+a source file from a CVS repository.
+If this is not set, then &cv-link-CVSCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="CVSFLAGS">
+<summary>
+General options that are passed to CVS.
+By default, this is set to
+<literal>-d $CVSREPOSITORY</literal>
+to specify from where the files must be fetched.
+</summary>
+</cvar>
+
+<cvar name="CVSREPOSITORY">
+<summary>
+The path to the CVS repository.
+This is referenced in the default
+&cv-link-CVSFLAGS; value.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/FortranCommon.py b/src/engine/SCons/Tool/FortranCommon.py
new file mode 100644
index 0000000..784b3fa
--- /dev/null
+++ b/src/engine/SCons/Tool/FortranCommon.py
@@ -0,0 +1,247 @@
+"""SCons.Tool.FortranCommon
+
+Stuff for processing Fortran, common to all fortran dialects.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/FortranCommon.py 4577 2009/12/27 19:44:43 scons"
+
+import re
+import string
+import os.path
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Scanner.Fortran
+import SCons.Tool
+import SCons.Util
+
+def isfortran(env, source):
+ """Return 1 if any of code in source has fortran files in it, 0
+ otherwise."""
+ try:
+ fsuffixes = env['FORTRANSUFFIXES']
+ except KeyError:
+ # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look
+ # for fortran sources.
+ return 0
+
+ if not source:
+ # Source might be None for unusual cases like SConf.
+ return 0
+ for s in source:
+ if s.sources:
+ ext = os.path.splitext(str(s.sources[0]))[1]
+ if ext in fsuffixes:
+ return 1
+ return 0
+
+def _fortranEmitter(target, source, env):
+ node = source[0].rfile()
+ if not node.exists() and not node.is_derived():
+ print "Could not locate " + str(node.name)
+ return ([], [])
+ mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)"""
+ cre = re.compile(mod_regex,re.M)
+ # Retrieve all USE'd module names
+ modules = cre.findall(node.get_text_contents())
+ # Remove unique items from the list
+ modules = SCons.Util.unique(modules)
+ # Convert module name to a .mod filename
+ suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source)
+ moddir = env.subst('$FORTRANMODDIR', target=target, source=source)
+ modules = map(lambda x, s=suffix: string.lower(x) + s, modules)
+ for m in modules:
+ target.append(env.fs.File(m, moddir))
+ return (target, source)
+
+def FortranEmitter(target, source, env):
+ target, source = _fortranEmitter(target, source, env)
+ return SCons.Defaults.StaticObjectEmitter(target, source, env)
+
+def ShFortranEmitter(target, source, env):
+ target, source = _fortranEmitter(target, source, env)
+ return SCons.Defaults.SharedObjectEmitter(target, source, env)
+
+def ComputeFortranSuffixes(suffixes, ppsuffixes):
+ """suffixes are fortran source files, and ppsuffixes the ones to be
+ pre-processed. Both should be sequences, not strings."""
+ assert len(suffixes) > 0
+ s = suffixes[0]
+ sup = string.upper(s)
+ upper_suffixes = map(string.upper, suffixes)
+ if SCons.Util.case_sensitive_suffixes(s, sup):
+ ppsuffixes.extend(upper_suffixes)
+ else:
+ suffixes.extend(upper_suffixes)
+
+def CreateDialectActions(dialect):
+ """Create dialect specific actions."""
+ CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect)
+ CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect)
+ ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect)
+ ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect)
+
+ return CompAction, CompPPAction, ShCompAction, ShCompPPAction
+
+def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0):
+ """Add dialect specific construction variables."""
+ ComputeFortranSuffixes(suffixes, ppsuffixes)
+
+ fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect)
+
+ for suffix in suffixes + ppsuffixes:
+ SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan)
+
+ env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes)
+
+ compaction, compppaction, shcompaction, shcompppaction = \
+ CreateDialectActions(dialect)
+
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in suffixes:
+ static_obj.add_action(suffix, compaction)
+ shared_obj.add_action(suffix, shcompaction)
+ static_obj.add_emitter(suffix, FortranEmitter)
+ shared_obj.add_emitter(suffix, ShFortranEmitter)
+
+ for suffix in ppsuffixes:
+ static_obj.add_action(suffix, compppaction)
+ shared_obj.add_action(suffix, shcompppaction)
+ static_obj.add_emitter(suffix, FortranEmitter)
+ shared_obj.add_emitter(suffix, ShFortranEmitter)
+
+ if not env.has_key('%sFLAGS' % dialect):
+ env['%sFLAGS' % dialect] = SCons.Util.CLVar('')
+
+ if not env.has_key('SH%sFLAGS' % dialect):
+ env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
+
+ # If a tool does not define fortran prefix/suffix for include path, use C ones
+ if not env.has_key('INC%sPREFIX' % dialect):
+ env['INC%sPREFIX' % dialect] = '$INCPREFIX'
+
+ if not env.has_key('INC%sSUFFIX' % dialect):
+ env['INC%sSUFFIX' % dialect] = '$INCSUFFIX'
+
+ env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect)
+
+ if support_module == 1:
+ env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
+ env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
+ env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
+ env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
+ else:
+ env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
+ env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
+ env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
+ env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
+
+def add_fortran_to_env(env):
+ """Add Builders and construction variables for Fortran to an Environment."""
+ try:
+ FortranSuffixes = env['FORTRANFILESUFFIXES']
+ except KeyError:
+ FortranSuffixes = ['.f', '.for', '.ftn']
+
+ #print "Adding %s to fortran suffixes" % FortranSuffixes
+ try:
+ FortranPPSuffixes = env['FORTRANPPFILESUFFIXES']
+ except KeyError:
+ FortranPPSuffixes = ['.fpp', '.FPP']
+
+ DialectAddToEnv(env, "FORTRAN", FortranSuffixes,
+ FortranPPSuffixes, support_module = 1)
+
+ env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX
+ env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX
+
+ env['FORTRANMODDIR'] = '' # where the compiler should place .mod files
+ env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
+ env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
+ env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+
+def add_f77_to_env(env):
+ """Add Builders and construction variables for f77 to an Environment."""
+ try:
+ F77Suffixes = env['F77FILESUFFIXES']
+ except KeyError:
+ F77Suffixes = ['.f77']
+
+ #print "Adding %s to f77 suffixes" % F77Suffixes
+ try:
+ F77PPSuffixes = env['F77PPFILESUFFIXES']
+ except KeyError:
+ F77PPSuffixes = []
+
+ DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes)
+
+def add_f90_to_env(env):
+ """Add Builders and construction variables for f90 to an Environment."""
+ try:
+ F90Suffixes = env['F90FILESUFFIXES']
+ except KeyError:
+ F90Suffixes = ['.f90']
+
+ #print "Adding %s to f90 suffixes" % F90Suffixes
+ try:
+ F90PPSuffixes = env['F90PPFILESUFFIXES']
+ except KeyError:
+ F90PPSuffixes = []
+
+ DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes,
+ support_module = 1)
+
+def add_f95_to_env(env):
+ """Add Builders and construction variables for f95 to an Environment."""
+ try:
+ F95Suffixes = env['F95FILESUFFIXES']
+ except KeyError:
+ F95Suffixes = ['.f95']
+
+ #print "Adding %s to f95 suffixes" % F95Suffixes
+ try:
+ F95PPSuffixes = env['F95PPFILESUFFIXES']
+ except KeyError:
+ F95PPSuffixes = []
+
+ DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes,
+ support_module = 1)
+
+def add_all_to_env(env):
+ """Add builders and construction variables for all supported fortran
+ dialects."""
+ add_fortran_to_env(env)
+ add_f77_to_env(env)
+ add_f90_to_env(env)
+ add_f95_to_env(env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py
new file mode 100644
index 0000000..44a6091
--- /dev/null
+++ b/src/engine/SCons/Tool/JavaCommon.py
@@ -0,0 +1,324 @@
+"""SCons.Tool.JavaCommon
+
+Stuff for processing Java.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/JavaCommon.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import re
+import string
+
+java_parsing = 1
+
+default_java_version = '1.4'
+
+if java_parsing:
+ # Parse Java files for class names.
+ #
+ # This is a really cool parser from Charles Crain
+ # that finds appropriate class names in Java source.
+
+ # A regular expression that will find, in a java file:
+ # newlines;
+ # double-backslashes;
+ # a single-line comment "//";
+ # single or double quotes preceeded by a backslash;
+ # single quotes, double quotes, open or close braces, semi-colons,
+ # periods, open or close parentheses;
+ # floating-point numbers;
+ # any alphanumeric token (keyword, class name, specifier);
+ # any alphanumeric token surrounded by angle brackets (generics);
+ # the multi-line comment begin and end tokens /* and */;
+ # array declarations "[]".
+ _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' +
+ r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' +
+ r'/\*|\*/|\[\])')
+
+ class OuterState:
+ """The initial state for parsing a Java file for classes,
+ interfaces, and anonymous inner classes."""
+ def __init__(self, version=default_java_version):
+
+ if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6',
+ '5', '6'):
+ msg = "Java version %s not supported" % version
+ raise NotImplementedError, msg
+
+ self.version = version
+ self.listClasses = []
+ self.listOutputs = []
+ self.stackBrackets = []
+ self.brackets = 0
+ self.nextAnon = 1
+ self.localClasses = []
+ self.stackAnonClassBrackets = []
+ self.anonStacksStack = [[0]]
+ self.package = None
+
+ def trace(self):
+ pass
+
+ def __getClassState(self):
+ try:
+ return self.classState
+ except AttributeError:
+ ret = ClassState(self)
+ self.classState = ret
+ return ret
+
+ def __getPackageState(self):
+ try:
+ return self.packageState
+ except AttributeError:
+ ret = PackageState(self)
+ self.packageState = ret
+ return ret
+
+ def __getAnonClassState(self):
+ try:
+ return self.anonState
+ except AttributeError:
+ self.outer_state = self
+ ret = SkipState(1, AnonClassState(self))
+ self.anonState = ret
+ return ret
+
+ def __getSkipState(self):
+ try:
+ return self.skipState
+ except AttributeError:
+ ret = SkipState(1, self)
+ self.skipState = ret
+ return ret
+
+ def __getAnonStack(self):
+ return self.anonStacksStack[-1]
+
+ def openBracket(self):
+ self.brackets = self.brackets + 1
+
+ def closeBracket(self):
+ self.brackets = self.brackets - 1
+ if len(self.stackBrackets) and \
+ self.brackets == self.stackBrackets[-1]:
+ self.listOutputs.append(string.join(self.listClasses, '$'))
+ self.localClasses.pop()
+ self.listClasses.pop()
+ self.anonStacksStack.pop()
+ self.stackBrackets.pop()
+ if len(self.stackAnonClassBrackets) and \
+ self.brackets == self.stackAnonClassBrackets[-1]:
+ self.__getAnonStack().pop()
+ self.stackAnonClassBrackets.pop()
+
+ def parseToken(self, token):
+ if token[:2] == '//':
+ return IgnoreState('\n', self)
+ elif token == '/*':
+ return IgnoreState('*/', self)
+ elif token == '{':
+ self.openBracket()
+ elif token == '}':
+ self.closeBracket()
+ elif token in [ '"', "'" ]:
+ return IgnoreState(token, self)
+ elif token == "new":
+ # anonymous inner class
+ if len(self.listClasses) > 0:
+ return self.__getAnonClassState()
+ return self.__getSkipState() # Skip the class name
+ elif token in ['class', 'interface', 'enum']:
+ if len(self.listClasses) == 0:
+ self.nextAnon = 1
+ self.stackBrackets.append(self.brackets)
+ return self.__getClassState()
+ elif token == 'package':
+ return self.__getPackageState()
+ elif token == '.':
+ # Skip the attribute, it might be named "class", in which
+ # case we don't want to treat the following token as
+ # an inner class name...
+ return self.__getSkipState()
+ return self
+
+ def addAnonClass(self):
+ """Add an anonymous inner class"""
+ if self.version in ('1.1', '1.2', '1.3', '1.4'):
+ clazz = self.listClasses[0]
+ self.listOutputs.append('%s$%d' % (clazz, self.nextAnon))
+ elif self.version in ('1.5', '1.6', '5', '6'):
+ self.stackAnonClassBrackets.append(self.brackets)
+ className = []
+ className.extend(self.listClasses)
+ self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1
+ for anon in self.__getAnonStack():
+ className.append(str(anon))
+ self.listOutputs.append(string.join(className, '$'))
+
+ self.nextAnon = self.nextAnon + 1
+ self.__getAnonStack().append(0)
+
+ def setPackage(self, package):
+ self.package = package
+
+ class AnonClassState:
+ """A state that looks for anonymous inner classes."""
+ def __init__(self, old_state):
+ # outer_state is always an instance of OuterState
+ self.outer_state = old_state.outer_state
+ self.old_state = old_state
+ self.brace_level = 0
+ def parseToken(self, token):
+ # This is an anonymous class if and only if the next
+ # non-whitespace token is a bracket. Everything between
+ # braces should be parsed as normal java code.
+ if token[:2] == '//':
+ return IgnoreState('\n', self)
+ elif token == '/*':
+ return IgnoreState('*/', self)
+ elif token == '\n':
+ return self
+ elif token[0] == '<' and token[-1] == '>':
+ return self
+ elif token == '(':
+ self.brace_level = self.brace_level + 1
+ return self
+ if self.brace_level > 0:
+ if token == 'new':
+ # look further for anonymous inner class
+ return SkipState(1, AnonClassState(self))
+ elif token in [ '"', "'" ]:
+ return IgnoreState(token, self)
+ elif token == ')':
+ self.brace_level = self.brace_level - 1
+ return self
+ if token == '{':
+ self.outer_state.addAnonClass()
+ return self.old_state.parseToken(token)
+
+ class SkipState:
+ """A state that will skip a specified number of tokens before
+ reverting to the previous state."""
+ def __init__(self, tokens_to_skip, old_state):
+ self.tokens_to_skip = tokens_to_skip
+ self.old_state = old_state
+ def parseToken(self, token):
+ self.tokens_to_skip = self.tokens_to_skip - 1
+ if self.tokens_to_skip < 1:
+ return self.old_state
+ return self
+
+ class ClassState:
+ """A state we go into when we hit a class or interface keyword."""
+ def __init__(self, outer_state):
+ # outer_state is always an instance of OuterState
+ self.outer_state = outer_state
+ def parseToken(self, token):
+ # the next non-whitespace token should be the name of the class
+ if token == '\n':
+ return self
+ # If that's an inner class which is declared in a method, it
+ # requires an index prepended to the class-name, e.g.
+ # 'Foo$1Inner' (Tigris Issue 2087)
+ if self.outer_state.localClasses and \
+ self.outer_state.stackBrackets[-1] > \
+ self.outer_state.stackBrackets[-2]+1:
+ locals = self.outer_state.localClasses[-1]
+ try:
+ idx = locals[token]
+ locals[token] = locals[token]+1
+ except KeyError:
+ locals[token] = 1
+ token = str(locals[token]) + token
+ self.outer_state.localClasses.append({})
+ self.outer_state.listClasses.append(token)
+ self.outer_state.anonStacksStack.append([0])
+ return self.outer_state
+
+ class IgnoreState:
+ """A state that will ignore all tokens until it gets to a
+ specified token."""
+ def __init__(self, ignore_until, old_state):
+ self.ignore_until = ignore_until
+ self.old_state = old_state
+ def parseToken(self, token):
+ if self.ignore_until == token:
+ return self.old_state
+ return self
+
+ class PackageState:
+ """The state we enter when we encounter the package keyword.
+ We assume the next token will be the package name."""
+ def __init__(self, outer_state):
+ # outer_state is always an instance of OuterState
+ self.outer_state = outer_state
+ def parseToken(self, token):
+ self.outer_state.setPackage(token)
+ return self.outer_state
+
+ def parse_java_file(fn, version=default_java_version):
+ return parse_java(open(fn, 'r').read(), version)
+
+ def parse_java(contents, version=default_java_version, trace=None):
+ """Parse a .java file and return a double of package directory,
+ plus a list of .class files that compiling that .java file will
+ produce"""
+ package = None
+ initial = OuterState(version)
+ currstate = initial
+ for token in _reToken.findall(contents):
+ # The regex produces a bunch of groups, but only one will
+ # have anything in it.
+ currstate = currstate.parseToken(token)
+ if trace: trace(token, currstate)
+ if initial.package:
+ package = string.replace(initial.package, '.', os.sep)
+ return (package, initial.listOutputs)
+
+else:
+ # Don't actually parse Java files for class names.
+ #
+ # We might make this a configurable option in the future if
+ # Java-file parsing takes too long (although it shouldn't relative
+ # to how long the Java compiler itself seems to take...).
+
+ def parse_java_file(fn):
+ """ "Parse" a .java file.
+
+ This actually just splits the file name, so the assumption here
+ is that the file name matches the public class name, and that
+ the path to the file is the same as the package name.
+ """
+ return os.path.split(file)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py
new file mode 100644
index 0000000..da44639
--- /dev/null
+++ b/src/engine/SCons/Tool/JavaCommonTests.py
@@ -0,0 +1,580 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/JavaCommonTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import sys
+import unittest
+
+import SCons.Tool.JavaCommon
+
+
+# Adding trace=trace to any of the parse_jave() calls below will cause
+# the parser to spit out trace messages of the tokens it sees and the
+# attendant transitions.
+
+def trace(token, newstate):
+ from SCons.Debug import Trace
+ statename = newstate.__class__.__name__
+ Trace('token = %s, state = %s\n' % (repr(token), statename))
+
+class parse_javaTestCase(unittest.TestCase):
+
+ def test_bare_bones(self):
+ """Test a bare-bones class"""
+
+ input = """\
+package com.sub.bar;
+
+public class Foo
+{
+
+ public static void main(String[] args)
+ {
+
+ /* This tests a former bug where strings would eat later code. */
+ String hello1 = new String("Hello, world!");
+
+ }
+
+}
+"""
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir == os.path.join('com', 'sub', 'bar'), pkg_dir
+ assert classes == ['Foo'], classes
+
+
+
+ def test_dollar_sign(self):
+ """Test class names with $ in them"""
+
+ input = """\
+public class BadDep {
+ public void new$rand () {}
+}
+"""
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir is None, pkg_dir
+ assert classes == ['BadDep'], classes
+
+
+
+ def test_inner_classes(self):
+ """Test parsing various forms of inner classes"""
+
+ input = """\
+class Empty {
+}
+
+interface Listener {
+ public void execute();
+}
+
+public
+class
+Test implements Listener {
+ class Inner {
+ void go() {
+ use(new Listener() {
+ public void execute() {
+ System.out.println("In Inner");
+ }
+ });
+ }
+ String s1 = "class A";
+ String s2 = "new Listener() { }";
+ /* class B */
+ /* new Listener() { } */
+ }
+
+ class Inner2 {
+ Inner2() { Listener l = new Listener(); }
+ }
+
+ /* Make sure this class doesn't get interpreted as an inner class of the previous one, when "new" is used in the previous class. */
+ class Inner3 {
+
+ }
+
+ public static void main(String[] args) {
+ new Test().run();
+ }
+
+ void run() {
+ use(new Listener() {
+ public void execute() {
+ use(new Listener( ) {
+ public void execute() {
+ System.out.println("Inside execute()");
+ }
+ });
+ }
+ });
+
+ new Inner().go();
+ }
+
+ void use(Listener l) {
+ l.execute();
+ }
+}
+
+class Private {
+ void run() {
+ new Listener() {
+ public void execute() {
+ }
+ };
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
+ assert pkg_dir is None, pkg_dir
+ expect = [
+ 'Empty',
+ 'Listener',
+ 'Test$1',
+ 'Test$Inner',
+ 'Test$Inner2',
+ 'Test$Inner3',
+ 'Test$2',
+ 'Test$3',
+ 'Test',
+ 'Private$1',
+ 'Private',
+ ]
+ assert classes == expect, classes
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ assert pkg_dir is None, pkg_dir
+ expect = [
+ 'Empty',
+ 'Listener',
+ 'Test$Inner$1',
+ 'Test$Inner',
+ 'Test$Inner2',
+ 'Test$Inner3',
+ 'Test$1',
+ 'Test$1$1',
+ 'Test',
+ 'Private$1',
+ 'Private',
+ ]
+ assert classes == expect, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '5')
+ assert pkg_dir is None, pkg_dir
+ expect = [
+ 'Empty',
+ 'Listener',
+ 'Test$Inner$1',
+ 'Test$Inner',
+ 'Test$Inner2',
+ 'Test$Inner3',
+ 'Test$1',
+ 'Test$1$1',
+ 'Test',
+ 'Private$1',
+ 'Private',
+ ]
+ assert classes == expect, (expect, classes)
+
+
+
+ def test_comments(self):
+ """Test a class with comments"""
+
+ input = """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Example1 extends UnicastRemoteObject implements Hello {
+
+ public Example1() throws RemoteException {
+ super();
+ }
+
+ public String sayHello() {
+ return "Hello World!";
+ }
+
+ public static void main(String args[]) {
+ if (System.getSecurityManager() == null) {
+ System.setSecurityManager(new RMISecurityManager());
+ }
+ // a comment
+ try {
+ Example1 obj = new Example1();
+
+ Naming.rebind("//myhost/HelloServer", obj);
+
+ System.out.println("HelloServer bound in registry");
+ } catch (Exception e) {
+ System.out.println("Example1 err: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir == os.path.join('com', 'sub', 'foo'), pkg_dir
+ assert classes == ['Example1'], classes
+
+
+ def test_arrays(self):
+ """Test arrays of class instances"""
+
+ input = """\
+public class Test {
+ MyClass abc = new MyClass();
+ MyClass xyz = new MyClass();
+ MyClass _array[] = new MyClass[] {
+ abc,
+ xyz
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir is None, pkg_dir
+ assert classes == ['Test'], classes
+
+
+
+ def test_backslash(self):
+ """Test backslash handling"""
+
+ input = """\
+public class MyTabs
+{
+ private class MyInternal
+ {
+ }
+ private final static String PATH = "images\\\\";
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir is None, pkg_dir
+ assert classes == ['MyTabs$MyInternal', 'MyTabs'], classes
+
+
+ def test_enum(self):
+ """Test the Java 1.5 enum keyword"""
+
+ input = """\
+package p;
+public enum a {}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir == 'p', pkg_dir
+ assert classes == ['a'], classes
+
+
+ def test_anon_classes(self):
+ """Test anonymous classes"""
+
+ input = """\
+public abstract class TestClass
+{
+ public void completed()
+ {
+ new Thread()
+ {
+ }.start();
+
+ new Thread()
+ {
+ }.start();
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir is None, pkg_dir
+ assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes
+
+
+ def test_closing_bracket(self):
+ """Test finding a closing bracket instead of an anonymous class"""
+
+ input = """\
+class TestSCons {
+ public static void main(String[] args) {
+ Foo[] fooArray = new Foo[] { new Foo() };
+ }
+}
+
+class Foo { }
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir is None, pkg_dir
+ assert classes == ['TestSCons', 'Foo'], classes
+
+
+ def test_dot_class_attributes(self):
+ """Test handling ".class" attributes"""
+
+ input = """\
+public class Test extends Object
+{
+ static {
+ Class c = Object[].class;
+ Object[] s = new Object[] {};
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert classes == ['Test'], classes
+
+ input = """\
+public class A {
+ public class B {
+ public void F(Object[] o) {
+ F(new Object[] {Object[].class});
+ }
+ public void G(Object[] o) {
+ F(new Object[] {});
+ }
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
+ assert pkg_dir is None, pkg_dir
+ assert classes == ['A$B', 'A'], classes
+
+ def test_anonymous_classes_with_parentheses(self):
+ """Test finding anonymous classes marked by parentheses"""
+
+ input = """\
+import java.io.File;
+
+public class Foo {
+ public static void main(String[] args) {
+ File f = new File(
+ new File("a") {
+ public String toString() {
+ return "b";
+ }
+ } to String()
+ ) {
+ public String toString() {
+ return "c";
+ }
+ };
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
+ assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6')
+ assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes
+
+
+
+ def test_nested_anonymous_inner_classes(self):
+ """Test finding nested anonymous inner classes"""
+
+ input = """\
+// import java.util.*;
+
+public class NestedExample
+{
+ public NestedExample()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ };
+ while (true)
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ }
+ };
+ }
+
+
+ public static void main(String argv[])
+ {
+ NestedExample e = new NestedExample();
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
+ expect = [ 'NestedExample$1', 'NestedExample$2', 'NestedExample' ]
+ assert expect == classes, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
+ assert expect == classes, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6')
+ expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
+ assert expect == classes, (expect, classes)
+
+ def test_private_inner_class_instantiation(self):
+ """Test anonymous inner class generated by private instantiation"""
+
+ input = """\
+class test
+{
+ test()
+ {
+ super();
+ new inner();
+ }
+
+ static class inner
+ {
+ private inner() {}
+ }
+}
+"""
+
+ # This is what we *should* generate, apparently due to the
+ # private instantiation of the inner class, but don't today.
+ #expect = [ 'test$1', 'test$inner', 'test' ]
+
+ # What our parser currently generates, which doesn't match
+ # what the Java compiler actually generates.
+ expect = [ 'test$inner', 'test' ]
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
+ assert expect == classes, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ assert expect == classes, (expect, classes)
+
+ def test_floating_point_numbers(self):
+ """Test floating-point numbers in the input stream"""
+ input = """
+// Broken.java
+class Broken
+{
+ /**
+ * Detected.
+ */
+ Object anonymousInnerOK = new Runnable() { public void run () {} };
+
+ /**
+ * Detected.
+ */
+ class InnerOK { InnerOK () { } }
+
+ {
+ System.out.println("a number: " + 1000.0 + "");
+ }
+
+ /**
+ * Not detected.
+ */
+ Object anonymousInnerBAD = new Runnable() { public void run () {} };
+
+ /**
+ * Not detected.
+ */
+ class InnerBAD { InnerBAD () { } }
+}
+"""
+
+ expect = ['Broken$1', 'Broken$InnerOK', 'Broken$2', 'Broken$InnerBAD', 'Broken']
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
+ assert expect == classes, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ assert expect == classes, (expect, classes)
+
+
+ def test_genercis(self):
+ """Test that generics don't interfere with detecting anonymous classes"""
+
+ input = """\
+import java.util.Date;
+import java.util.Comparator;
+
+public class Foo
+{
+ public void foo()
+ {
+ Comparator<Date> comp = new Comparator<Date>()
+ {
+ static final long serialVersionUID = 1L;
+ public int compare(Date lhs, Date rhs)
+ {
+ return 0;
+ }
+ };
+ }
+}
+"""
+
+ expect = [ 'Foo$1', 'Foo' ]
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.6')
+ assert expect == classes, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6')
+ assert expect == classes, (expect, classes)
+
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ parse_javaTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/MSCommon/__init__.py b/src/engine/SCons/Tool/MSCommon/__init__.py
new file mode 100644
index 0000000..78a6030
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/__init__.py
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+Common functions for Microsoft Visual Studio and Visual C/C++.
+"""
+
+import copy
+import os
+import re
+import subprocess
+
+import SCons.Errors
+import SCons.Platform.win32
+import SCons.Util
+
+from SCons.Tool.MSCommon.sdk import mssdk_exists, \
+ mssdk_setup_env
+
+from SCons.Tool.MSCommon.vc import msvc_exists, \
+ msvc_setup_env, \
+ msvc_setup_env_once
+
+from SCons.Tool.MSCommon.vs import get_default_version, \
+ get_vs_by_version, \
+ merge_default_version, \
+ msvs_exists, \
+ query_versions
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/MSCommon/arch.py b/src/engine/SCons/Tool/MSCommon/arch.py
new file mode 100644
index 0000000..3cf2fa0
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/arch.py
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Module to define supported Windows chip architectures.
+"""
+
+import os
+
+class ArchDefinition:
+ """
+ A class for defining architecture-specific settings and logic.
+ """
+ def __init__(self, arch, synonyms=[]):
+ self.arch = arch
+ self.synonyms = synonyms
+
+SupportedArchitectureList = [
+ ArchitectureDefinition(
+ 'x86',
+ ['i386', 'i486', 'i586', 'i686'],
+ ),
+
+ ArchitectureDefinition(
+ 'x86_64',
+ ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'],
+ ),
+
+ ArchitectureDefinition(
+ 'ia64',
+ ['IA64'],
+ ),
+]
+
+SupportedArchitectureMap = {}
+for a in SupportedArchitectureList:
+ SupportedArchitectureMap[a.arch] = a
+ for s in a.synonyms:
+ SupportedArchitectureMap[s] = a
+
diff --git a/src/engine/SCons/Tool/MSCommon/common.py b/src/engine/SCons/Tool/MSCommon/common.py
new file mode 100644
index 0000000..8fc49cc
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/common.py
@@ -0,0 +1,195 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/common.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+Common helper functions for working with the Microsoft tool chain.
+"""
+
+import copy
+import os
+import subprocess
+import re
+
+import SCons.Util
+
+
+logfile = os.environ.get('SCONS_MSCOMMON_DEBUG')
+if logfile == '-':
+ def debug(x):
+ print x
+elif logfile:
+ try:
+ import logging
+ except ImportError:
+ debug = lambda x: open(logfile, 'a').write(x + '\n')
+ else:
+ logging.basicConfig(filename=logfile, level=logging.DEBUG)
+ debug = logging.debug
+else:
+ debug = lambda x: None
+
+
+_is_win64 = None
+
+def is_win64():
+ """Return true if running on windows 64 bits.
+
+ Works whether python itself runs in 64 bits or 32 bits."""
+ # Unfortunately, python does not provide a useful way to determine
+ # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether
+ # the Python itself is 32-bit or 64-bit affects what it returns,
+ # so nothing in sys.* or os.* help. So we go to the registry to
+ # look directly for a clue from Windows, caching the result to
+ # avoid repeated registry calls.
+ global _is_win64
+ if _is_win64 is None:
+ _is_win64 = has_reg(r"Software\Wow6432Node")
+ return _is_win64
+
+
+def read_reg(value):
+ return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0]
+
+def has_reg(value):
+ """Return True if the given key exists in HKEY_LOCAL_MACHINE, False
+ otherwise."""
+ try:
+ SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value)
+ ret = True
+ except WindowsError:
+ ret = False
+ return ret
+
+# Functions for fetching environment variable settings from batch files.
+
+def normalize_env(env, keys):
+ """Given a dictionary representing a shell environment, add the variables
+ from os.environ needed for the processing of .bat files; the keys are
+ controlled by the keys argument.
+
+ It also makes sure the environment values are correctly encoded.
+
+ Note: the environment is copied"""
+ normenv = {}
+ if env:
+ for k in env.keys():
+ normenv[k] = copy.deepcopy(env[k]).encode('mbcs')
+
+ for k in keys:
+ if os.environ.has_key(k):
+ normenv[k] = os.environ[k].encode('mbcs')
+
+ return normenv
+
+def get_output(vcbat, args = None, env = None):
+ """Parse the output of given bat file, with given args."""
+ if args:
+ debug("Calling '%s %s'" % (vcbat, args))
+ popen = subprocess.Popen('"%s" %s & set' % (vcbat, args),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
+ else:
+ debug("Calling '%s'" % vcbat)
+ popen = subprocess.Popen('"%s" & set' % vcbat,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
+
+ # Use the .stdout and .stderr attributes directly because the
+ # .communicate() method uses the threading module on Windows
+ # and won't work under Pythons not built with threading.
+ stdout = popen.stdout.read()
+ if popen.wait() != 0:
+ raise IOError(popen.stderr.read().decode("mbcs"))
+
+ output = stdout.decode("mbcs")
+ return output
+
+def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")):
+ # dkeep is a dict associating key: path_list, where key is one item from
+ # keep, and pat_list the associated list of paths
+
+ # TODO(1.5): replace with the following list comprehension:
+ #dkeep = dict([(i, []) for i in keep])
+ dkeep = dict(map(lambda i: (i, []), keep))
+
+ # rdk will keep the regex to match the .bat file output line starts
+ rdk = {}
+ for i in keep:
+ rdk[i] = re.compile('%s=(.*)' % i, re.I)
+
+ def add_env(rmatch, key, dkeep=dkeep):
+ plist = rmatch.group(1).split(os.pathsep)
+ for p in plist:
+ # Do not add empty paths (when a var ends with ;)
+ if p:
+ p = p.encode('mbcs')
+ # XXX: For some reason, VC98 .bat file adds "" around the PATH
+ # values, and it screws up the environment later, so we strip
+ # it.
+ p = p.strip('"')
+ dkeep[key].append(p)
+
+ for line in output.splitlines():
+ for k,v in rdk.items():
+ m = v.match(line)
+ if m:
+ add_env(m, k)
+
+ return dkeep
+
+# TODO(sgk): unused
+def output_to_dict(output):
+ """Given an output string, parse it to find env variables.
+
+ Return a dict where keys are variables names, and values their content"""
+ envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$')
+ parsedenv = {}
+ for line in output.splitlines():
+ m = envlinem.match(line)
+ if m:
+ parsedenv[m.group(1)] = m.group(2)
+ return parsedenv
+
+# TODO(sgk): unused
+def get_new(l1, l2):
+ """Given two list l1 and l2, return the items in l2 which are not in l1.
+ Order is maintained."""
+
+ # We don't try to be smart: lists are small, and this is not the bottleneck
+ # is any case
+ new = []
+ for i in l2:
+ if i not in l1:
+ new.append(i)
+
+ return new
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/MSCommon/netframework.py b/src/engine/SCons/Tool/MSCommon/netframework.py
new file mode 100644
index 0000000..3a38abd
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/netframework.py
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+"""
+
+import os
+import re
+import string
+
+from common import read_reg, debug
+
+# Original value recorded by dcournapeau
+_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot'
+# On SGK's system
+_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder'
+
+def find_framework_root():
+ # XXX: find it from environment (FrameworkDir)
+ try:
+ froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT)
+ debug("Found framework install root in registry: %s" % froot)
+ except WindowsError, e:
+ debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT)
+ return None
+
+ if not os.path.exists(froot):
+ debug("%s not found on fs" % froot)
+ return None
+
+ return froot
+
+def query_versions():
+ froot = find_framework_root()
+ if froot:
+ contents = os.listdir(froot)
+
+ l = re.compile('v[0-9]+.*')
+ versions = filter(lambda e, l=l: l.match(e), contents)
+
+ def versrt(a,b):
+ # since version numbers aren't really floats...
+ aa = a[1:]
+ bb = b[1:]
+ aal = string.split(aa, '.')
+ bbl = string.split(bb, '.')
+ # sequence comparison in python is lexicographical
+ # which is exactly what we want.
+ # Note we sort backwards so the highest version is first.
+ return cmp(bbl,aal)
+
+ versions.sort(versrt)
+ else:
+ versions = []
+
+ return versions
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/MSCommon/sdk.py b/src/engine/SCons/Tool/MSCommon/sdk.py
new file mode 100644
index 0000000..88af6aa
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/sdk.py
@@ -0,0 +1,321 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Module to detect the Platform/Windows SDK
+
+PSDK 2003 R1 is the earliest version detected.
+"""
+
+import os
+
+import SCons.Errors
+import SCons.Util
+
+import common
+
+debug = common.debug
+
+# SDK Checks. This is of course a mess as everything else on MS platforms. Here
+# is what we do to detect the SDK:
+#
+# For Windows SDK >= 6.0: just look into the registry entries:
+# HKLM\Software\Microsoft\Microsoft SDKs\Windows
+# All the keys in there are the available versions.
+#
+# For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not
+# seem to be any sane registry key, so the precise location is hardcoded.
+#
+# For versions below 2003R1, it seems the PSDK is included with Visual Studio?
+#
+# Also, per the following:
+# http://benjamin.smedbergs.us/blog/tag/atl/
+# VC++ Professional comes with the SDK, VC++ Express does not.
+
+# Location of the SDK (checked for 6.1 only)
+_CURINSTALLED_SDK_HKEY_ROOT = \
+ r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder"
+
+
+class SDKDefinition:
+ """
+ An abstract base class for trying to find installed SDK directories.
+ """
+ def __init__(self, version, **kw):
+ self.version = version
+ self.__dict__.update(kw)
+
+ def find_sdk_dir(self):
+ """Try to find the MS SDK from the registry.
+
+ Return None if failed or the directory does not exist.
+ """
+ if not SCons.Util.can_read_reg:
+ debug('find_sdk_dir(): can not read registry')
+ return None
+
+ hkey = self.HKEY_FMT % self.hkey_data
+
+ try:
+ sdk_dir = common.read_reg(hkey)
+ except WindowsError, e:
+ debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey))
+ return None
+
+ if not os.path.exists(sdk_dir):
+ debug('find_sdk_dir(): %s not on file system' % sdk_dir)
+ return None
+
+ ftc = os.path.join(sdk_dir, self.sanity_check_file)
+ if not os.path.exists(ftc):
+ debug("find_sdk_dir(): sanity check %s not found" % ftc)
+ return None
+
+ return sdk_dir
+
+ def get_sdk_dir(self):
+ """Return the MSSSDK given the version string."""
+ try:
+ return self._sdk_dir
+ except AttributeError:
+ sdk_dir = self.find_sdk_dir()
+ self._sdk_dir = sdk_dir
+ return sdk_dir
+
+class WindowsSDK(SDKDefinition):
+ """
+ A subclass for trying to find installed Windows SDK directories.
+ """
+ HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder'
+ def __init__(self, *args, **kw):
+ apply(SDKDefinition.__init__, (self,)+args, kw)
+ self.hkey_data = self.version
+
+class PlatformSDK(SDKDefinition):
+ """
+ A subclass for trying to find installed Platform SDK directories.
+ """
+ HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir'
+ def __init__(self, *args, **kw):
+ apply(SDKDefinition.__init__, (self,)+args, kw)
+ self.hkey_data = self.uuid
+
+# The list of support SDKs which we know how to detect.
+#
+# The first SDK found in the list is the one used by default if there
+# are multiple SDKs installed. Barring good reasons to the contrary,
+# this means we should list SDKs with from most recent to oldest.
+#
+# If you update this list, update the documentation in Tool/mssdk.xml.
+SupportedSDKList = [
+ WindowsSDK('6.1',
+ sanity_check_file=r'bin\SetEnv.Cmd',
+ include_subdir='include',
+ lib_subdir={
+ 'x86' : ['lib'],
+ 'x86_64' : [r'lib\x64'],
+ 'ia64' : [r'lib\ia64'],
+ },
+ ),
+
+ WindowsSDK('6.0A',
+ sanity_check_file=r'include\windows.h',
+ include_subdir='include',
+ lib_subdir={
+ 'x86' : ['lib'],
+ 'x86_64' : [r'lib\x64'],
+ 'ia64' : [r'lib\ia64'],
+ },
+ ),
+
+ WindowsSDK('6.0',
+ sanity_check_file=r'bin\gacutil.exe',
+ include_subdir='include',
+ lib_subdir='lib',
+ ),
+
+ PlatformSDK('2003R2',
+ sanity_check_file=r'SetEnv.Cmd',
+ uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1"
+ ),
+
+ PlatformSDK('2003R1',
+ sanity_check_file=r'SetEnv.Cmd',
+ uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3",
+ ),
+]
+
+SupportedSDKMap = {}
+for sdk in SupportedSDKList:
+ SupportedSDKMap[sdk.version] = sdk
+
+
+# Finding installed SDKs isn't cheap, because it goes not only to the
+# registry but also to the disk to sanity-check that there is, in fact,
+# an SDK installed there and that the registry entry isn't just stale.
+# Find this information once, when requested, and cache it.
+
+InstalledSDKList = None
+InstalledSDKMap = None
+
+def get_installed_sdks():
+ global InstalledSDKList
+ global InstalledSDKMap
+ if InstalledSDKList is None:
+ InstalledSDKList = []
+ InstalledSDKMap = {}
+ for sdk in SupportedSDKList:
+ debug('trying to find SDK %s' % sdk.version)
+ if sdk.get_sdk_dir():
+ debug('found SDK %s' % sdk.version)
+ InstalledSDKList.append(sdk)
+ InstalledSDKMap[sdk.version] = sdk
+ return InstalledSDKList
+
+
+# We may be asked to update multiple construction environments with
+# SDK information. When doing this, we check on-disk for whether
+# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk
+# is expensive, cache results by directory.
+
+SDKEnvironmentUpdates = {}
+
+def set_sdk_by_directory(env, sdk_dir):
+ global SDKEnvironmentUpdates
+ try:
+ env_tuple_list = SDKEnvironmentUpdates[sdk_dir]
+ except KeyError:
+ env_tuple_list = []
+ SDKEnvironmentUpdates[sdk_dir] = env_tuple_list
+
+ include_path = os.path.join(sdk_dir, 'include')
+ mfc_path = os.path.join(include_path, 'mfc')
+ atl_path = os.path.join(include_path, 'atl')
+
+ if os.path.exists(mfc_path):
+ env_tuple_list.append(('INCLUDE', mfc_path))
+ if os.path.exists(atl_path):
+ env_tuple_list.append(('INCLUDE', atl_path))
+ env_tuple_list.append(('INCLUDE', include_path))
+
+ env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib')))
+ env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib')))
+ env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin')))
+
+ for variable, directory in env_tuple_list:
+ env.PrependENVPath(variable, directory)
+
+
+# TODO(sgk): currently unused; remove?
+def get_cur_sdk_dir_from_reg():
+ """Try to find the platform sdk directory from the registry.
+
+ Return None if failed or the directory does not exist"""
+ if not SCons.Util.can_read_reg:
+ debug('SCons cannot read registry')
+ return None
+
+ try:
+ val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT)
+ debug("Found current sdk dir in registry: %s" % val)
+ except WindowsError, e:
+ debug("Did not find current sdk in registry")
+ return None
+
+ if not os.path.exists(val):
+ debug("Current sdk dir %s not on fs" % val)
+ return None
+
+ return val
+
+def get_sdk_by_version(mssdk):
+ if not SupportedSDKMap.has_key(mssdk):
+ msg = "SDK version %s is not supported" % repr(mssdk)
+ raise SCons.Errors.UserError, msg
+ get_installed_sdks()
+ return InstalledSDKMap.get(mssdk)
+
+def get_default_sdk():
+ """Set up the default Platform/Windows SDK."""
+ get_installed_sdks()
+ if not InstalledSDKList:
+ return None
+ return InstalledSDKList[0]
+
+def mssdk_setup_env(env):
+ debug('msvs_setup_env()')
+ if env.has_key('MSSDK_DIR'):
+ sdk_dir = env['MSSDK_DIR']
+ if sdk_dir is None:
+ return
+ sdk_dir = env.subst(sdk_dir)
+ elif env.has_key('MSSDK_VERSION'):
+ sdk_version = env['MSSDK_VERSION']
+ if sdk_version is None:
+ msg = "SDK version %s is not installed" % repr(mssdk)
+ raise SCons.Errors.UserError, msg
+ sdk_version = env.subst(sdk_version)
+ mssdk = get_sdk_by_version(sdk_version)
+ sdk_dir = mssdk.get_sdk_dir()
+ elif env.has_key('MSVS_VERSION'):
+ msvs_version = env['MSVS_VERSION']
+ debug('Getting MSVS_VERSION from env:%s'%msvs_version)
+ if msvs_version is None:
+ return
+ msvs_version = env.subst(msvs_version)
+ import vs
+ msvs = vs.get_vs_by_version(msvs_version)
+ debug('msvs is :%s'%msvs)
+ if not msvs:
+ return
+ sdk_version = msvs.sdk_version
+ if not sdk_version:
+ return
+ mssdk = get_sdk_by_version(sdk_version)
+ if not mssdk:
+ mssdk = get_default_sdk()
+ if not mssdk:
+ return
+ sdk_dir = mssdk.get_sdk_dir()
+ else:
+ mssdk = get_default_sdk()
+ if not mssdk:
+ return
+ sdk_dir = mssdk.get_sdk_dir()
+
+ set_sdk_by_directory(env, sdk_dir)
+
+ #print "No MSVS_VERSION: this is likely to be a bug"
+
+def mssdk_exists(version=None):
+ sdks = get_installed_sdks()
+ if version is None:
+ return len(sdks) > 0
+ return sdks.has_key(version)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py
new file mode 100644
index 0000000..7d34374
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/vc.py
@@ -0,0 +1,367 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+# TODO:
+# * supported arch for versions: for old versions of batch file without
+# argument, giving bogus argument cannot be detected, so we have to hardcode
+# this here
+# * print warning when msvc version specified but not found
+# * find out why warning do not print
+# * test on 64 bits XP + VS 2005 (and VS 6 if possible)
+# * SDK
+# * Assembly
+__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Module for Visual C/C++ detection and configuration.
+"""
+import SCons.compat
+
+import os
+import platform
+
+import SCons.Warnings
+
+import common
+
+debug = common.debug
+
+class VisualCException(Exception):
+ pass
+
+class UnsupportedVersion(VisualCException):
+ pass
+
+class UnsupportedArch(VisualCException):
+ pass
+
+class MissingConfiguration(VisualCException):
+ pass
+
+class NoVersionFound(VisualCException):
+ pass
+
+class BatchFileExecutionError(VisualCException):
+ pass
+
+# Dict to 'canonalize' the arch
+_ARCH_TO_CANONICAL = {
+ "x86": "x86",
+ "amd64": "amd64",
+ "i386": "x86",
+ "emt64": "amd64",
+ "x86_64": "amd64",
+ "itanium": "ia64",
+ "ia64": "ia64",
+}
+
+# Given a (host, target) tuple, return the argument for the bat file. Both host
+# and targets should be canonalized.
+_HOST_TARGET_ARCH_TO_BAT_ARCH = {
+ ("x86", "x86"): "x86",
+ ("x86", "amd64"): "x86_amd64",
+ ("amd64", "amd64"): "amd64",
+ ("amd64", "x86"): "x86",
+ ("x86", "ia64"): "x86_ia64"
+}
+
+def get_host_target(env):
+ host_platform = env.get('HOST_ARCH')
+ if not host_platform:
+ host_platform = platform.machine()
+ # TODO(2.5): the native Python platform.machine() function returns
+ # '' on all Python versions before 2.6, after which it also uses
+ # PROCESSOR_ARCHITECTURE.
+ if not host_platform:
+ host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '')
+ target_platform = env.get('TARGET_ARCH')
+ if not target_platform:
+ target_platform = host_platform
+
+ try:
+ host = _ARCH_TO_CANONICAL[host_platform]
+ except KeyError, e:
+ msg = "Unrecognized host architecture %s"
+ raise ValueError(msg % repr(host_platform))
+
+ try:
+ target = _ARCH_TO_CANONICAL[target_platform]
+ except KeyError, e:
+ raise ValueError("Unrecognized target architecture %s" % target_platform)
+
+ return (host, target)
+
+_VCVER = ["10.0", "9.0", "8.0", "7.1", "7.0", "6.0"]
+
+_VCVER_TO_PRODUCT_DIR = {
+ '10.0': [
+ r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'],
+ '9.0': [
+ r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',
+ r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'],
+ '8.0': [
+ r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir',
+ r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'],
+ '7.1': [
+ r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'],
+ '7.0': [
+ r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'],
+ '6.0': [
+ r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir']
+}
+
+def msvc_version_to_maj_min(msvc_version):
+ t = msvc_version.split(".")
+ if not len(t) == 2:
+ raise ValueError("Unrecognized version %s" % msvc_version)
+ try:
+ maj = int(t[0])
+ min = int(t[1])
+ return maj, min
+ except ValueError, e:
+ raise ValueError("Unrecognized version %s" % msvc_version)
+
+def is_host_target_supported(host_target, msvc_version):
+ """Return True if the given (host, target) tuple is supported given the
+ msvc version.
+
+ Parameters
+ ----------
+ host_target: tuple
+ tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross
+ compilation from 32 bits windows to 64 bits.
+ msvc_version: str
+ msvc version (major.minor, e.g. 10.0)
+
+ Note
+ ----
+ This only check whether a given version *may* support the given (host,
+ target), not that the toolchain is actually present on the machine.
+ """
+ # We assume that any Visual Studio version supports x86 as a target
+ if host_target[1] != "x86":
+ maj, min = msvc_version_to_maj_min(msvc_version)
+ if maj < 8:
+ return False
+
+ return True
+
+def find_vc_pdir(msvc_version):
+ """Try to find the product directory for the given
+ version.
+
+ Note
+ ----
+ If for some reason the requested version could not be found, an
+ exception which inherits from VisualCException will be raised."""
+ root = 'Software\\'
+ if common.is_win64():
+ root = root + 'Wow6432Node\\'
+ try:
+ hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version]
+ except KeyError:
+ debug("Unknown version of MSVC: %s" % msvc_version)
+ raise UnsupportedVersion("Unknown version %s" % msvc_version)
+
+ for key in hkeys:
+ key = root + key
+ try:
+ comps = common.read_reg(key)
+ except WindowsError, e:
+ debug('find_vc_dir(): no VC registry key %s' % repr(key))
+ else:
+ debug('find_vc_dir(): found VC in registry: %s' % comps)
+ if os.path.exists(comps):
+ return comps
+ else:
+ debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\
+ % comps)
+ raise MissingConfiguration("registry dir %s not found on the filesystem" % comps)
+ return None
+
+def find_batch_file(msvc_version):
+ pdir = find_vc_pdir(msvc_version)
+ if pdir is None:
+ raise NoVersionFound("No version of Visual Studio found")
+
+ vernum = float(msvc_version)
+ if 7 <= vernum < 8:
+ pdir = os.path.join(pdir, os.pardir, "Common7", "Tools")
+ batfilename = os.path.join(pdir, "vsvars32.bat")
+ elif vernum < 7:
+ pdir = os.path.join(pdir, "Bin")
+ batfilename = os.path.join(pdir, "vcvars32.bat")
+ else: # >= 8
+ batfilename = os.path.join(pdir, "vcvarsall.bat")
+
+ if os.path.exists(batfilename):
+ return batfilename
+ else:
+ debug("Not found: %s" % batfilename)
+ return None
+
+__INSTALLED_VCS_RUN = None
+
+def cached_get_installed_vcs():
+ global __INSTALLED_VCS_RUN
+
+ if __INSTALLED_VCS_RUN is None:
+ ret = get_installed_vcs()
+ __INSTALLED_VCS_RUN = ret
+
+ return __INSTALLED_VCS_RUN
+
+def get_installed_vcs():
+ installed_versions = []
+ for ver in _VCVER:
+ debug('trying to find VC %s' % ver)
+ try:
+ if find_vc_pdir(ver):
+ debug('found VC %s' % ver)
+ installed_versions.append(ver)
+ else:
+ debug('find_vc_pdir return None for ver %s' % ver)
+ except VisualCException, e:
+ debug('did not find VC %s: caught exception %s' % (ver, str(e)))
+ return installed_versions
+
+def reset_installed_vcs():
+ """Make it try again to find VC. This is just for the tests."""
+ __INSTALLED_VCS_RUN = None
+
+def script_env(script, args=None):
+ stdout = common.get_output(script, args)
+ # Stupid batch files do not set return code: we take a look at the
+ # beginning of the output for an error message instead
+ olines = stdout.splitlines()
+ if olines[0].startswith("The specified configuration type is missing"):
+ raise BatchFileExecutionError("\n".join(olines[:2]))
+
+ return common.parse_output(stdout)
+
+def get_default_version(env):
+ debug('get_default_version()')
+
+ msvc_version = env.get('MSVC_VERSION')
+ msvs_version = env.get('MSVS_VERSION')
+
+ if msvs_version and not msvc_version:
+ SCons.Warnings.warn(
+ SCons.Warnings.DeprecatedWarning,
+ "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ")
+ return msvs_version
+ elif msvc_version and msvs_version:
+ if not msvc_version == msvs_version:
+ SCons.Warnings.warn(
+ SCons.Warnings.VisualVersionMismatch,
+ "Requested msvc version (%s) and msvs version (%s) do " \
+ "not match: please use MSVC_VERSION only to request a " \
+ "visual studio version, MSVS_VERSION is deprecated" \
+ % (msvc_version, msvs_version))
+ return msvs_version
+ if not msvc_version:
+ installed_vcs = cached_get_installed_vcs()
+ debug('installed_vcs:%s' % installed_vcs)
+ if not installed_vcs:
+ msg = 'No installed VCs'
+ debug('msv %s\n' % repr(msg))
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
+ return None
+ msvc_version = installed_vcs[0]
+ debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version))
+
+ return msvc_version
+
+def msvc_setup_env_once(env):
+ try:
+ has_run = env["MSVC_SETUP_RUN"]
+ except KeyError:
+ has_run = False
+
+ if not has_run:
+ msvc_setup_env(env)
+ env["MSVC_SETUP_RUN"] = True
+
+def msvc_setup_env(env):
+ debug('msvc_setup_env()')
+
+ version = get_default_version(env)
+ if version is None:
+ warn_msg = "No version of Visual Studio compiler found - C/C++ " \
+ "compilers most likely not set correctly"
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ return None
+ debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version))
+
+ # XXX: we set-up both MSVS version for backward
+ # compatibility with the msvs tool
+ env['MSVC_VERSION'] = version
+ env['MSVS_VERSION'] = version
+ env['MSVS'] = {}
+
+ try:
+ script = find_batch_file(version)
+ except VisualCException, e:
+ msg = str(e)
+ debug('Caught exception while looking for batch file (%s)' % msg)
+ warn_msg = "VC version %s not installed. " + \
+ "C/C++ compilers are most likely not set correctly.\n" + \
+ " Installed versions are: %s"
+ warn_msg = warn_msg % (version, cached_get_installed_vcs())
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ return None
+
+ use_script = env.get('MSVC_USE_SCRIPT', True)
+ if SCons.Util.is_String(use_script):
+ debug('use_script 1 %s\n' % repr(use_script))
+ d = script_env(use_script)
+ elif use_script:
+ host_platform, target_platform = get_host_target(env)
+ host_target = (host_platform, target_platform)
+ if not is_host_target_supported(host_target, version):
+ warn_msg = "host, target = %s not supported for MSVC version %s" % \
+ (host_target, version)
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target]
+ debug('use_script 2 %s, args:%s\n' % (repr(script), arg))
+ try:
+ d = script_env(script, args=arg)
+ except BatchFileExecutionError, e:
+ msg = "MSVC error while executing %s with args %s (error was %s)" % \
+ (script, arg, str(e))
+ raise SCons.Errors.UserError(msg)
+ else:
+ debug('MSVC_USE_SCRIPT set to False')
+ warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \
+ "set correctly."
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ return None
+
+ for k, v in d.items():
+ env.PrependENVPath(k, v, delete_existing=True)
+
+def msvc_exists(version=None):
+ vcs = cached_get_installed_vcs()
+ if version is None:
+ return len(vcs) > 0
+ return version in vcs
+
diff --git a/src/engine/SCons/Tool/MSCommon/vc.py.bak b/src/engine/SCons/Tool/MSCommon/vc.py.bak
new file mode 100644
index 0000000..e59092f
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/vc.py.bak
@@ -0,0 +1,394 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py.bak 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Module for Visual C/C++ detection and configuration.
+"""
+
+import os
+import platform
+
+import SCons.Warnings
+
+import common
+
+debug = common.debug
+
+class VisualC:
+ """
+ An base class for finding installed versions of Visual C/C++.
+ """
+ def __init__(self, version, **kw):
+ self.version = version
+ self.__dict__.update(kw)
+ self._cache = {}
+
+ def vcbin_arch(self):
+ if common.is_win64():
+ result = {
+ 'x86_64' : ['amd64', r'BIN\x86_amd64'],
+ 'ia64' : [r'BIN\ia64'],
+ }.get(target_arch, [])
+ else:
+ result = {
+ 'x86_64' : ['x86_amd64'],
+ 'ia64' : ['x86_ia64'],
+ }.get(target_arch, [])
+ # TODO(1.5)
+ #return ';'.join(result)
+ return string.join(result, ';')
+
+ # Support for searching for an appropriate .bat file.
+ # The map is indexed by (target_architecture, host_architecture).
+ # Entries where the host_architecture is None specify the
+ # cross-platform "default" .bat file if there isn't sn entry
+ # specific to the current host architecture.
+
+ batch_file_map = {
+ ('x86_64', 'x86_64') : [
+ r'bin\amd64\vcvarsamd64.bat',
+ r'bin\x86_amd64\vcvarsx86_amd64.bat',
+ r'bin\vcvarsx86_amd64.bat',
+ ],
+ ('x86_64', 'x86') : [
+ r'bin\x86_amd64\vcvarsx86_amd64.bat',
+ ],
+ ('ia64', 'ia64') : [
+ r'bin\ia64\vcvarsia64.bat',
+ r'bin\x86_ia64\vcvarsx86_ia64.bat',
+ ],
+ ('ia64', None) : [
+ r'bin\x86_ia64\vcvarsx86_ia64.bat',
+ ],
+ ('x86', None) : [
+ r'bin\vcvars32.bat',
+ ],
+ }
+
+ def find_batch_file(self, target_architecture, host_architecture):
+ key = (target_architecture, host_architecture)
+ potential_batch_files = self.batch_file_map.get(key)
+ if not potential_batch_files:
+ key = (target_architecture, None)
+ potential_batch_files = self.batch_file_map.get(key)
+ if potential_batch_files:
+ product_dir = self.get_vc_dir()
+ for batch_file in potential_batch_files:
+ bf = os.path.join(product_dir, batch_file)
+ if os.path.isfile(bf):
+ return bf
+ return None
+
+ def find_vc_dir(self):
+ root = 'Software\\'
+ if common.is_win64():
+ root = root + 'Wow6432Node\\'
+ for key in self.hkeys:
+ key = root + key
+ try:
+ comps = common.read_reg(key)
+ except WindowsError, e:
+ debug('find_vc_dir(): no VC registry key %s' % repr(key))
+ else:
+ debug('find_vc_dir(): found VC in registry: %s' % comps)
+ if os.path.exists(comps):
+ return comps
+ else:
+ debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\
+ % comps)
+ return None
+ return None
+
+ #
+
+ def get_batch_file(self, target_architecture, host_architecture):
+ try:
+ return self._cache['batch_file']
+ except KeyError:
+ batch_file = self.find_batch_file(target_architecture, host_architecture)
+ self._cache['batch_file'] = batch_file
+ return batch_file
+
+ def get_vc_dir(self):
+ try:
+ return self._cache['vc_dir']
+ except KeyError:
+ vc_dir = self.find_vc_dir()
+ self._cache['vc_dir'] = vc_dir
+ return vc_dir
+
+ def reset(self):
+ self._cache={}
+
+
+# The list of supported Visual C/C++ versions we know how to detect.
+#
+# The first VC found in the list is the one used by default if there
+# are multiple VC installed. Barring good reasons to the contrary,
+# this means we should list VC with from most recent to oldest.
+#
+# If you update this list, update the documentation in Tool/vc.xml.
+SupportedVCList = [
+ VisualC('9.0',
+ hkeys=[
+ r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',
+ r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir',
+ ],
+ default_install=r'Microsoft Visual Studio 9.0\VC',
+ common_tools_var='VS90COMNTOOLS',
+ vc_subdir=r'\VC',
+ batch_file_base='vcvars',
+ supported_arch=['x86', 'x86_64', 'ia64'],
+ atlmc_include_subdir = [r'ATLMFC\INCLUDE'],
+ atlmfc_lib_subdir = {
+ 'x86' : r'ATLMFC\LIB',
+ 'x86_64' : r'ATLMFC\LIB\amd64',
+ 'ia64' : r'ATLMFC\LIB\ia64',
+ },
+ crt_lib_subdir = {
+ 'x86_64' : r'LIB\amd64',
+ 'ia64' : r'LIB\ia64',
+ },
+ ),
+ VisualC('8.0',
+ hkeys=[
+ r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir',
+ r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir',
+ ],
+ default_install=r'%s\Microsoft Visual Studio 8\VC',
+ common_tools_var='VS80COMNTOOLS',
+ vc_subdir=r'\VC',
+ batch_file_base='vcvars',
+ supported_arch=['x86', 'x86_64', 'ia64'],
+ atlmc_include_subdir = [r'ATLMFC\INCLUDE'],
+ atlmfc_lib_subdir = {
+ 'x86' : r'ATLMFC\LIB',
+ 'x86_64' : r'ATLMFC\LIB\amd64',
+ 'ia64' : r'ATLMFC\LIB\ia64',
+ },
+ crt_lib_subdir = {
+ 'x86_64' : r'LIB\amd64',
+ 'ia64' : r'LIB\ia64',
+ },
+ ),
+ VisualC('7.1',
+ hkeys=[
+ r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir',
+ ],
+ default_install=r'%s\Microsoft Visual Studio 7.1.NET 2003\VC7',
+ common_tools_var='VS71COMNTOOLS',
+ vc_subdir=r'\VC7',
+ batch_file_base='vcvars',
+ supported_arch=['x86'],
+ atlmc_include_subdir = [r'ATLMFC\INCLUDE'],
+ atlmfc_lib_subdir = {
+ 'x86' : r'ATLMFC\LIB',
+ },
+ ),
+ VisualC('7.0',
+ hkeys=[
+ r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir',
+ ],
+ default_install=r'%s\Microsoft Visual Studio .NET\VC7',
+ common_tools_var='VS70COMNTOOLS',
+ vc_subdir=r'\VC7',
+ batch_file_base='vcvars',
+ supported_arch=['x86'],
+ atlmc_include_subdir = [r'ATLMFC\INCLUDE'],
+ atlmfc_lib_subdir = {
+ 'x86' : r'ATLMFC\LIB',
+ },
+ ),
+ VisualC('6.0',
+ hkeys=[
+ r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir',
+ ],
+ default_install=r'%s\Microsoft Visual Studio\VC98',
+ common_tools_var='VS60COMNTOOLS',
+ vc_subdir=r'\VC98',
+ batch_file_base='vcvars',
+ supported_arch=['x86'],
+ atlmc_include_subdir = [r'ATL\INCLUDE', r'MFC\INCLUDE'],
+ atlmfc_lib_subdir = {
+ 'x86' : r'MFC\LIB',
+ },
+ ),
+]
+
+SupportedVCMap = {}
+for vc in SupportedVCList:
+ SupportedVCMap[vc.version] = vc
+
+
+# Finding installed versions of Visual C/C++ isn't cheap, because it goes
+# not only to the registry but also to the disk to sanity-check that there
+# is, in fact, something installed there and that the registry entry isn't
+# just stale. Find this information once, when requested, and cache it.
+
+InstalledVCList = None
+InstalledVCMap = None
+
+def get_installed_vcs():
+ global InstalledVCList
+ global InstalledVCMap
+ if InstalledVCList is None:
+ InstalledVCList = []
+ InstalledVCMap = {}
+ for vc in SupportedVCList:
+ debug('trying to find VC %s' % vc.version)
+ if vc.get_vc_dir():
+ debug('found VC %s' % vc.version)
+ InstalledVCList.append(vc)
+ InstalledVCMap[vc.version] = vc
+ return InstalledVCList
+
+
+def set_vc_by_version(env, msvc):
+ if not SupportedVCMap.has_key(msvc):
+ msg = "VC version %s is not supported" % repr(msvc)
+ raise SCons.Errors.UserError, msg
+ get_installed_vcs()
+ vc = InstalledVCMap.get(msvc)
+ if not vc:
+ msg = "VC version %s is not installed" % repr(msvc)
+ raise SCons.Errors.UserError, msg
+ set_vc_by_directory(env, vc.get_vc_dir())
+
+# New stuff
+
+def script_env(script, args=None):
+ stdout = common.get_output(script, args)
+ return common.parse_output(stdout)
+
+def get_default_version(env):
+ debug('get_default_version()')
+
+ msvc_version = env.get('MSVC_VERSION')
+ if not msvc_version:
+ installed_vcs = get_installed_vcs()
+ debug('InstalledVCMap:%s'%InstalledVCMap)
+ if not installed_vcs:
+ msg = 'No installed VCs'
+ debug('msv %s\n' % repr(msg))
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
+ return None
+ msvc = installed_vcs[0]
+ msvc_version = msvc.version
+ debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version))
+
+ return msvc_version
+
+# Dict to 'canonalize' the arch
+_ARCH_TO_CANONICAL = {
+ "x86": "x86",
+ "amd64": "amd64",
+ "i386": "x86",
+ "emt64": "amd64",
+ "x86_64": "amd64"
+}
+
+# Given a (host, target) tuple, return the argument for the bat file. Both host
+# and targets should be canonalized.
+_HOST_TARGET_ARCH_TO_BAT_ARCH = {
+ ("x86", "x86"): "x86",
+ ("x86", "amd64"): "x86_amd64",
+ ("amd64", "amd64"): "amd64",
+ ("amd64", "x86"): "x86"
+}
+
+def get_host_target(env):
+ host_platform = env.get('HOST_ARCH')
+ if not host_platform:
+ #host_platform = get_default_host_platform()
+ host_platform = platform.machine()
+ target_platform = env.get('TARGET_ARCH')
+ if not target_platform:
+ target_platform = host_platform
+
+ return (_ARCH_TO_CANONICAL[host_platform],
+ _ARCH_TO_CANONICAL[target_platform])
+
+def msvc_setup_env_once(env):
+ try:
+ has_run = env["MSVC_SETUP_RUN"]
+ except KeyError:
+ has_run = False
+
+ if not has_run:
+ msvc_setup_env(env)
+ env["MSVC_SETUP_RUN"] = False
+
+def msvc_setup_env(env):
+ debug('msvc_setup_env()')
+
+ version = get_default_version(env)
+ host_platform, target_platform = get_host_target(env)
+ debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version))
+ env['MSVC_VERSION'] = version
+
+ msvc = InstalledVCMap.get(version)
+ if not msvc:
+ msg = 'VC version %s not installed' % version
+ debug('msv %s\n' % repr(msg))
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
+ return None
+
+ use_script = env.get('MSVC_USE_SCRIPT', True)
+ if SCons.Util.is_String(use_script):
+ debug('use_script 1 %s\n' % repr(use_script))
+ d = script_env(use_script)
+ elif use_script:
+ # XXX: this is VS 2008 specific, fix this
+ script = os.path.join(msvc.find_vc_dir(), "vcvarsall.bat")
+
+ arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[(host_platform, target_platform)]
+ debug('use_script 2 %s, args:%s\n' % (repr(script), arg))
+ d = script_env(script, args=arg)
+ else:
+ debug('msvc.get_default_env()\n')
+ d = msvc.get_default_env()
+
+ for k, v in d.items():
+ env.PrependENVPath(k, v, delete_existing=True)
+
+def msvc_exists(version=None):
+ vcs = get_installed_vcs()
+ if version is None:
+ return len(vcs) > 0
+ return InstalledVCMap.has_key(version)
+
+
+def reset_installed_vcs():
+ global InstalledVCList
+ global InstalledVCMap
+ InstalledVCList = None
+ InstalledVCMap = None
+ for vc in SupportedVCList:
+ vc.reset()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py
new file mode 100644
index 0000000..69003ef
--- /dev/null
+++ b/src/engine/SCons/Tool/MSCommon/vs.py
@@ -0,0 +1,497 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """Module to detect Visual Studio and/or Visual C/C++
+"""
+
+import os
+
+import SCons.Errors
+import SCons.Util
+
+from common import debug, \
+ get_output, \
+ is_win64, \
+ normalize_env, \
+ parse_output, \
+ read_reg
+
+import SCons.Tool.MSCommon.vc
+
+class VisualStudio:
+ """
+ An abstract base class for trying to find installed versions of
+ Visual Studio.
+ """
+ def __init__(self, version, **kw):
+ self.version = version
+ kw['vc_version'] = kw.get('vc_version', version)
+ kw['sdk_version'] = kw.get('sdk_version', version)
+ self.__dict__.update(kw)
+ self._cache = {}
+
+ #
+
+ def find_batch_file(self):
+ vs_dir = self.get_vs_dir()
+ if not vs_dir:
+ debug('find_executable(): no vs_dir')
+ return None
+ batch_file = os.path.join(vs_dir, self.batch_file_path)
+ batch_file = os.path.normpath(batch_file)
+ if not os.path.isfile(batch_file):
+ debug('find_batch_file(): %s not on file system' % batch_file)
+ return None
+ return batch_file
+
+ def find_vs_dir_by_vc(self):
+ SCons.Tool.MSCommon.vc.get_installed_vcs()
+ dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version)
+ if not dir:
+ debug('find_vs_dir(): no installed VC %s' % self.vc_version)
+ return None
+ return dir
+
+ def find_vs_dir_by_reg(self):
+ root = 'Software\\'
+
+ if is_win64():
+ root = root + 'Wow6432Node\\'
+ for key in self.hkeys:
+ if key=='use_dir':
+ return self.find_vs_dir_by_vc()
+ key = root + key
+ try:
+ comps = read_reg(key)
+ except WindowsError, e:
+ debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key))
+ else:
+ debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps)
+ return comps
+ return None
+
+ def find_vs_dir(self):
+ """ Can use registry or location of VC to find vs dir
+ First try to find by registry, and if that fails find via VC dir
+ """
+
+
+ if True:
+ vs_dir=self.find_vs_dir_by_reg()
+ return vs_dir
+ else:
+ return self.find_vs_dir_by_vc()
+
+ def find_executable(self):
+ vs_dir = self.get_vs_dir()
+ if not vs_dir:
+ debug('find_executable(): no vs_dir (%s)'%vs_dir)
+ return None
+ executable = os.path.join(vs_dir, self.executable_path)
+ executable = os.path.normpath(executable)
+ if not os.path.isfile(executable):
+ debug('find_executable(): %s not on file system' % executable)
+ return None
+ return executable
+
+ #
+
+ def get_batch_file(self):
+ try:
+ return self._cache['batch_file']
+ except KeyError:
+ batch_file = self.find_batch_file()
+ self._cache['batch_file'] = batch_file
+ return batch_file
+
+ def get_executable(self):
+ try:
+ debug('get_executable using cache:%s'%self._cache['executable'])
+ return self._cache['executable']
+ except KeyError:
+ executable = self.find_executable()
+ self._cache['executable'] = executable
+ debug('get_executable not in cache:%s'%executable)
+ return executable
+
+ def get_vs_dir(self):
+ try:
+ return self._cache['vs_dir']
+ except KeyError:
+ vs_dir = self.find_vs_dir()
+ self._cache['vs_dir'] = vs_dir
+ return vs_dir
+
+ def get_supported_arch(self):
+ try:
+ return self._cache['supported_arch']
+ except KeyError:
+ # RDEVE: for the time being use hardcoded lists
+ # supported_arch = self.find_supported_arch()
+ self._cache['supported_arch'] = self.supported_arch
+ return self.supported_arch
+
+ def reset(self):
+ self._cache = {}
+
+# The list of supported Visual Studio versions we know how to detect.
+#
+# How to look for .bat file ?
+# - VS 2008 Express (x86):
+# * from registry key productdir, gives the full path to vsvarsall.bat. In
+# HKEY_LOCAL_MACHINE):
+# Software\Microsoft\VCEpress\9.0\Setup\VC\productdir
+# * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC
+# relatively to the path given by the variable.
+#
+# - VS 2008 Express (WoW6432: 32 bits on windows x64):
+# Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir
+#
+# - VS 2005 Express (x86):
+# * from registry key productdir, gives the full path to vsvarsall.bat. In
+# HKEY_LOCAL_MACHINE):
+# Software\Microsoft\VCEpress\8.0\Setup\VC\productdir
+# * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC
+# relatively to the path given by the variable.
+#
+# - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a
+# productdir ?
+#
+# - VS 2003 .Net (pro edition ? x86):
+# * from registry key productdir. The path is then ..\Common7\Tools\
+# relatively to the key. The key is in HKEY_LOCAL_MACHINE):
+# Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir
+# * from environmnent variable VS71COMNTOOLS: the path is the full path to
+# vsvars32.bat
+#
+# - VS 98 (VS 6):
+# * from registry key productdir. The path is then Bin
+# relatively to the key. The key is in HKEY_LOCAL_MACHINE):
+# Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir
+#
+# The first version found in the list is the one used by default if
+# there are multiple versions installed. Barring good reasons to
+# the contrary, this means we should list versions from most recent
+# to oldest. Pro versions get listed before Express versions on the
+# assumption that, by default, you'd rather use the version you paid
+# good money for in preference to whatever Microsoft makes available
+# for free.
+#
+# If you update this list, update the documentation in Tool/msvs.xml.
+
+SupportedVSList = [
+ # Visual Studio 2010
+ # TODO: find the settings, perhaps from someone with a CTP copy?
+ #VisualStudio('TBD',
+ # hkey_root=r'TBD',
+ # common_tools_var='TBD',
+ # executable_path=r'TBD',
+ # default_dirname='TBD',
+ #),
+
+ # Visual Studio 2008
+ # The batch file we look for is in the VC directory,
+ # so the devenv.com executable is up in ..\..\Common7\IDE.
+ VisualStudio('9.0',
+ sdk_version='6.1',
+ hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'],
+ common_tools_var='VS90COMNTOOLS',
+ executable_path=r'Common7\IDE\devenv.com',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio 9',
+ supported_arch=['x86', 'amd64'],
+ ),
+
+ # Visual C++ 2008 Express Edition
+ # The batch file we look for is in the VC directory,
+ # so the VCExpress.exe executable is up in ..\..\Common7\IDE.
+ VisualStudio('9.0Exp',
+ vc_version='9.0',
+ sdk_version='6.1',
+ hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'],
+ common_tools_var='VS90COMNTOOLS',
+ executable_path=r'Common7\IDE\VCExpress.exe',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio 9',
+ supported_arch=['x86'],
+ ),
+
+ # Visual Studio 2005
+ # The batch file we look for is in the VC directory,
+ # so the devenv.com executable is up in ..\..\Common7\IDE.
+ VisualStudio('8.0',
+ sdk_version='6.0A',
+ hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'],
+ common_tools_var='VS80COMNTOOLS',
+ executable_path=r'Common7\IDE\devenv.com',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio 8',
+ supported_arch=['x86', 'amd64'],
+ ),
+
+ # Visual C++ 2005 Express Edition
+ # The batch file we look for is in the VC directory,
+ # so the VCExpress.exe executable is up in ..\..\Common7\IDE.
+ VisualStudio('8.0Exp',
+ vc_version='8.0',
+ sdk_version='6.0A',
+ hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'],
+ common_tools_var='VS80COMNTOOLS',
+ executable_path=r'Common7\IDE\VCExpress.exe',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio 8',
+ supported_arch=['x86'],
+ ),
+
+ # Visual Studio .NET 2003
+ # The batch file we look for is in the Common7\Tools directory,
+ # so the devenv.com executable is next door in ..\IDE.
+ VisualStudio('7.1',
+ sdk_version='6.0',
+ hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'],
+ common_tools_var='VS71COMNTOOLS',
+ executable_path=r'Common7\IDE\devenv.com',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio .NET 2003',
+ supported_arch=['x86'],
+ ),
+
+ # Visual Studio .NET
+ # The batch file we look for is in the Common7\Tools directory,
+ # so the devenv.com executable is next door in ..\IDE.
+ VisualStudio('7.0',
+ sdk_version='2003R2',
+ hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'],
+ common_tools_var='VS70COMNTOOLS',
+ executable_path=r'IDE\devenv.com',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio .NET',
+ supported_arch=['x86'],
+ ),
+
+ # Visual Studio 6.0
+ VisualStudio('6.0',
+ sdk_version='2003R1',
+ hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir',
+ 'use_dir'],
+ common_tools_var='VS60COMNTOOLS',
+ executable_path=r'Common\MSDev98\Bin\MSDEV.COM',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ default_dirname='Microsoft Visual Studio',
+ supported_arch=['x86'],
+ ),
+]
+
+SupportedVSMap = {}
+for vs in SupportedVSList:
+ SupportedVSMap[vs.version] = vs
+
+
+# Finding installed versions of Visual Studio isn't cheap, because it
+# goes not only to the registry but also to the disk to sanity-check
+# that there is, in fact, a Visual Studio directory there and that the
+# registry entry isn't just stale. Find this information once, when
+# requested, and cache it.
+
+InstalledVSList = None
+InstalledVSMap = None
+
+def get_installed_visual_studios():
+ global InstalledVSList
+ global InstalledVSMap
+ if InstalledVSList is None:
+ InstalledVSList = []
+ InstalledVSMap = {}
+ for vs in SupportedVSList:
+ debug('trying to find VS %s' % vs.version)
+ if vs.get_executable():
+ debug('found VS %s' % vs.version)
+ InstalledVSList.append(vs)
+ InstalledVSMap[vs.version] = vs
+ return InstalledVSList
+
+def reset_installed_visual_studios():
+ global InstalledVSList
+ global InstalledVSMap
+ InstalledVSList = None
+ InstalledVSMap = None
+ for vs in SupportedVSList:
+ vs.reset()
+
+ # Need to clear installed VC's as well as they are used in finding
+ # installed VS's
+ SCons.Tool.MSCommon.vc.reset_installed_vcs()
+
+
+# We may be asked to update multiple construction environments with
+# SDK information. When doing this, we check on-disk for whether
+# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk
+# is expensive, cache results by directory.
+
+#SDKEnvironmentUpdates = {}
+#
+#def set_sdk_by_directory(env, sdk_dir):
+# global SDKEnvironmentUpdates
+# try:
+# env_tuple_list = SDKEnvironmentUpdates[sdk_dir]
+# except KeyError:
+# env_tuple_list = []
+# SDKEnvironmentUpdates[sdk_dir] = env_tuple_list
+#
+# include_path = os.path.join(sdk_dir, 'include')
+# mfc_path = os.path.join(include_path, 'mfc')
+# atl_path = os.path.join(include_path, 'atl')
+#
+# if os.path.exists(mfc_path):
+# env_tuple_list.append(('INCLUDE', mfc_path))
+# if os.path.exists(atl_path):
+# env_tuple_list.append(('INCLUDE', atl_path))
+# env_tuple_list.append(('INCLUDE', include_path))
+#
+# env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib')))
+# env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib')))
+# env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin')))
+#
+# for variable, directory in env_tuple_list:
+# env.PrependENVPath(variable, directory)
+
+def msvs_exists():
+ return (len(get_installed_visual_studios()) > 0)
+
+def get_vs_by_version(msvs):
+ global InstalledVSMap
+ global SupportedVSMap
+
+ if not SupportedVSMap.has_key(msvs):
+ msg = "Visual Studio version %s is not supported" % repr(msvs)
+ raise SCons.Errors.UserError, msg
+ get_installed_visual_studios()
+ vs = InstalledVSMap.get(msvs)
+ debug('InstalledVSMap:%s'%InstalledVSMap)
+ # Some check like this would let us provide a useful error message
+ # if they try to set a Visual Studio version that's not installed.
+ # However, we also want to be able to run tests (like the unit
+ # tests) on systems that don't, or won't ever, have it installed.
+ # It might be worth resurrecting this, with some configurable
+ # setting that the tests can use to bypass the check.
+ #if not vs:
+ # msg = "Visual Studio version %s is not installed" % repr(msvs)
+ # raise SCons.Errors.UserError, msg
+ return vs
+
+def get_default_version(env):
+ """Returns the default version string to use for MSVS.
+
+ If no version was requested by the user through the MSVS environment
+ variable, query all the available the visual studios through
+ query_versions, and take the highest one.
+
+ Return
+ ------
+ version: str
+ the default version.
+ """
+ if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']):
+ # TODO(1.5):
+ #versions = [vs.version for vs in get_installed_visual_studios()]
+ versions = map(lambda vs: vs.version, get_installed_visual_studios())
+ env['MSVS'] = {'VERSIONS' : versions}
+ else:
+ versions = env['MSVS'].get('VERSIONS', [])
+
+ if not env.has_key('MSVS_VERSION'):
+ if versions:
+ env['MSVS_VERSION'] = versions[0] #use highest version by default
+ else:
+ env['MSVS_VERSION'] = SupportedVSList[0].version
+
+ env['MSVS']['VERSION'] = env['MSVS_VERSION']
+
+ return env['MSVS_VERSION']
+
+def get_default_arch(env):
+ """Return the default arch to use for MSVS
+
+ if no version was requested by the user through the MSVS_ARCH environment
+ variable, select x86
+
+ Return
+ ------
+ arch: str
+ """
+ arch = env.get('MSVS_ARCH', 'x86')
+
+ msvs = InstalledVSMap.get(env['MSVS_VERSION'])
+
+ if not msvs:
+ arch = 'x86'
+ elif not arch in msvs.get_supported_arch():
+ fmt = "Visual Studio version %s does not support architecture %s"
+ raise SCons.Errors.UserError, fmt % (env['MSVS_VERSION'], arch)
+
+ return arch
+
+def merge_default_version(env):
+ version = get_default_version(env)
+ arch = get_default_arch(env)
+
+def msvs_setup_env(env):
+ batfilename = msvs.get_batch_file()
+ msvs = get_vs_by_version(version)
+ if msvs is None:
+ return
+
+ # XXX: I think this is broken. This will silently set a bogus tool instead
+ # of failing, but there is no other way with the current scons tool
+ # framework
+ if batfilename is not None:
+
+ vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE')
+
+ msvs_list = get_installed_visual_studios()
+ # TODO(1.5):
+ #vscommonvarnames = [ vs.common_tools_var for vs in msvs_list ]
+ vscommonvarnames = map(lambda vs: vs.common_tools_var, msvs_list)
+ nenv = normalize_env(env['ENV'], vscommonvarnames + ['COMSPEC'])
+ output = get_output(batfilename, arch, env=nenv)
+ vars = parse_output(output, vars)
+
+ for k, v in vars.items():
+ env.PrependENVPath(k, v, delete_existing=1)
+
+def query_versions():
+ """Query the system to get available versions of VS. A version is
+ considered when a batfile is found."""
+ msvs_list = get_installed_visual_studios()
+ # TODO(1.5)
+ #versions = [ msvs.version for msvs in msvs_list ]
+ versions = map(lambda msvs: msvs.version, msvs_list)
+ return versions
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/Perforce.py b/src/engine/SCons/Tool/Perforce.py
new file mode 100644
index 0000000..441b2cb
--- /dev/null
+++ b/src/engine/SCons/Tool/Perforce.py
@@ -0,0 +1,104 @@
+"""SCons.Tool.Perforce.py
+
+Tool-specific initialization for Perforce Source Code Management system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/Perforce.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+
+import SCons.Action
+import SCons.Builder
+import SCons.Node.FS
+import SCons.Util
+
+# This function should maybe be moved to SCons.Util?
+from SCons.Tool.PharLapCommon import addPathIfNotExists
+
+
+
+# Variables that we want to import from the base OS environment.
+_import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD',
+ 'P4CHARSET', 'P4LANGUAGE', 'SystemRoot' ]
+
+PerforceAction = SCons.Action.Action('$P4COM', '$P4COMSTR')
+
+def generate(env):
+ """Add a Builder factory function and construction variables for
+ Perforce to an Environment."""
+
+ def PerforceFactory(env=env):
+ """ """
+ return SCons.Builder.Builder(action = PerforceAction, env = env)
+
+ #setattr(env, 'Perforce', PerforceFactory)
+ env.Perforce = PerforceFactory
+
+ env['P4'] = 'p4'
+ env['P4FLAGS'] = SCons.Util.CLVar('')
+ env['P4COM'] = '$P4 $P4FLAGS sync $TARGET'
+ try:
+ environ = env['ENV']
+ except KeyError:
+ environ = {}
+ env['ENV'] = environ
+
+ # Perforce seems to use the PWD environment variable rather than
+ # calling getcwd() for itself, which is odd. If no PWD variable
+ # is present, p4 WILL call getcwd, but this seems to cause problems
+ # with good ol' Windows's tilde-mangling for long file names.
+ environ['PWD'] = env.Dir('#').get_abspath()
+
+ for var in _import_env:
+ v = os.environ.get(var)
+ if v:
+ environ[var] = v
+
+ if SCons.Util.can_read_reg:
+ # If we can read the registry, add the path to Perforce to our environment.
+ try:
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Perforce\\environment')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT')
+ addPathIfNotExists(environ, 'PATH', val)
+ except SCons.Util.RegError:
+ # Can't detect where Perforce is, hope the user has it set in the
+ # PATH.
+ pass
+
+def exists(env):
+ return env.Detect('p4')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/Perforce.xml b/src/engine/SCons/Tool/Perforce.xml
new file mode 100644
index 0000000..7bfcdc0
--- /dev/null
+++ b/src/engine/SCons/Tool/Perforce.xml
@@ -0,0 +1,47 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="Perforce">
+<summary>
+Sets construction variables for interacting with the
+Perforce source code management system.
+</summary>
+<sets>
+P4
+P4FLAGS
+P4COM
+</sets>
+<uses>
+P4COMSTR
+</uses>
+</tool>
+
+<cvar name="P4">
+<summary>
+The Perforce executable.
+</summary>
+</cvar>
+
+<cvar name="P4COM">
+<summary>
+The command line used to
+fetch source files from Perforce.
+</summary>
+</cvar>
+
+<cvar name="P4COMSTR">
+<summary>
+The string displayed when
+fetching a source file from Perforce.
+If this is not set, then &cv-link-P4COM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="P4FLAGS">
+<summary>
+General options that are passed to Perforce.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/PharLapCommon.py b/src/engine/SCons/Tool/PharLapCommon.py
new file mode 100644
index 0000000..dd5ddd1
--- /dev/null
+++ b/src/engine/SCons/Tool/PharLapCommon.py
@@ -0,0 +1,138 @@
+"""SCons.Tool.PharLapCommon
+
+This module contains common code used by all Tools for the
+Phar Lap ETS tool chain. Right now, this is linkloc and
+386asm.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/PharLapCommon.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import SCons.Errors
+import SCons.Util
+import re
+import string
+
+def getPharLapPath():
+ """Reads the registry to find the installed path of the Phar Lap ETS
+ development kit.
+
+ Raises UserError if no installed version of Phar Lap can
+ be found."""
+
+ if not SCons.Util.can_read_reg:
+ raise SCons.Errors.InternalError, "No Windows registry module was found"
+ try:
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
+ 'SOFTWARE\\Pharlap\\ETS')
+ val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir')
+
+ # The following is a hack...there is (not surprisingly)
+ # an odd issue in the Phar Lap plug in that inserts
+ # a bunch of junk data after the phar lap path in the
+ # registry. We must trim it.
+ idx=val.find('\0')
+ if idx >= 0:
+ val = val[:idx]
+
+ return os.path.normpath(val)
+ except SCons.Util.RegError:
+ raise SCons.Errors.UserError, "Cannot find Phar Lap ETS path in the registry. Is it installed properly?"
+
+REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)')
+
+def getPharLapVersion():
+ """Returns the version of the installed ETS Tool Suite as a
+ decimal number. This version comes from the ETS_VER #define in
+ the embkern.h header. For example, '#define ETS_VER 1010' (which
+ is what Phar Lap 10.1 defines) would cause this method to return
+ 1010. Phar Lap 9.1 does not have such a #define, but this method
+ will return 910 as a default.
+
+ Raises UserError if no installed version of Phar Lap can
+ be found."""
+
+ include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h"))
+ if not os.path.exists(include_path):
+ raise SCons.Errors.UserError, "Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?"
+ mo = REGEX_ETS_VER.search(open(include_path, 'r').read())
+ if mo:
+ return int(mo.group(1))
+ # Default return for Phar Lap 9.1
+ return 910
+
+def addPathIfNotExists(env_dict, key, path, sep=os.pathsep):
+ """This function will take 'key' out of the dictionary
+ 'env_dict', then add the path 'path' to that key if it is not
+ already there. This treats the value of env_dict[key] as if it
+ has a similar format to the PATH variable...a list of paths
+ separated by tokens. The 'path' will get added to the list if it
+ is not already there."""
+ try:
+ is_list = 1
+ paths = env_dict[key]
+ if not SCons.Util.is_List(env_dict[key]):
+ paths = string.split(paths, sep)
+ is_list = 0
+ if not os.path.normcase(path) in map(os.path.normcase, paths):
+ paths = [ path ] + paths
+ if is_list:
+ env_dict[key] = paths
+ else:
+ env_dict[key] = string.join(paths, sep)
+ except KeyError:
+ env_dict[key] = path
+
+def addPharLapPaths(env):
+ """This function adds the path to the Phar Lap binaries, includes,
+ and libraries, if they are not already there."""
+ ph_path = getPharLapPath()
+
+ try:
+ env_dict = env['ENV']
+ except KeyError:
+ env_dict = {}
+ env['ENV'] = env_dict
+ addPathIfNotExists(env_dict, 'PATH',
+ os.path.join(ph_path, 'bin'))
+ addPathIfNotExists(env_dict, 'INCLUDE',
+ os.path.join(ph_path, 'include'))
+ addPathIfNotExists(env_dict, 'LIB',
+ os.path.join(ph_path, 'lib'))
+ addPathIfNotExists(env_dict, 'LIB',
+ os.path.join(ph_path, os.path.normpath('lib/vclib')))
+
+ env['PHARLAP_PATH'] = getPharLapPath()
+ env['PHARLAP_VERSION'] = str(getPharLapVersion())
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/PharLapCommonTests.py b/src/engine/SCons/Tool/PharLapCommonTests.py
new file mode 100644
index 0000000..99e7524
--- /dev/null
+++ b/src/engine/SCons/Tool/PharLapCommonTests.py
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/PharLapCommonTests.py 4577 2009/12/27 19:44:43 scons"
+
+import unittest
+import os.path
+import os
+import sys
+
+import SCons.Errors
+from SCons.Tool.PharLapCommon import *
+
+class PharLapCommonTestCase(unittest.TestCase):
+ def test_addPathIfNotExists(self):
+ """Test the addPathIfNotExists() function"""
+ env_dict = { 'FOO' : os.path.normpath('/foo/bar') + os.pathsep + \
+ os.path.normpath('/baz/blat'),
+ 'BAR' : os.path.normpath('/foo/bar') + os.pathsep + \
+ os.path.normpath('/baz/blat'),
+ 'BLAT' : [ os.path.normpath('/foo/bar'),
+ os.path.normpath('/baz/blat') ] }
+ addPathIfNotExists(env_dict, 'FOO', os.path.normpath('/foo/bar'))
+ addPathIfNotExists(env_dict, 'BAR', os.path.normpath('/bar/foo'))
+ addPathIfNotExists(env_dict, 'BAZ', os.path.normpath('/foo/baz'))
+ addPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/blat'))
+ addPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/foo'))
+
+ assert env_dict['FOO'] == os.path.normpath('/foo/bar') + os.pathsep + \
+ os.path.normpath('/baz/blat'), env_dict['FOO']
+ assert env_dict['BAR'] == os.path.normpath('/bar/foo') + os.pathsep + \
+ os.path.normpath('/foo/bar') + os.pathsep + \
+ os.path.normpath('/baz/blat'), env_dict['BAR']
+ assert env_dict['BAZ'] == os.path.normpath('/foo/baz'), env_dict['BAZ']
+ assert env_dict['BLAT'] == [ os.path.normpath('/baz/foo'),
+ os.path.normpath('/foo/bar'),
+ os.path.normpath('/baz/blat') ], env_dict['BLAT' ]
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(PharLapCommonTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/RCS.py b/src/engine/SCons/Tool/RCS.py
new file mode 100644
index 0000000..7605f73
--- /dev/null
+++ b/src/engine/SCons/Tool/RCS.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.RCS.py
+
+Tool-specific initialization for RCS.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/RCS.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+
+def generate(env):
+ """Add a Builder factory function and construction variables for
+ RCS to an Environment."""
+
+ def RCSFactory(env=env):
+ """ """
+ act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR')
+ return SCons.Builder.Builder(action = act, env = env)
+
+ #setattr(env, 'RCS', RCSFactory)
+ env.RCS = RCSFactory
+
+ env['RCS'] = 'rcs'
+ env['RCS_CO'] = 'co'
+ env['RCS_COFLAGS'] = SCons.Util.CLVar('')
+ env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET'
+
+def exists(env):
+ return env.Detect('rcs')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/RCS.xml b/src/engine/SCons/Tool/RCS.xml
new file mode 100644
index 0000000..52d8165
--- /dev/null
+++ b/src/engine/SCons/Tool/RCS.xml
@@ -0,0 +1,61 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="RCS">
+<summary>
+Sets construction variables for the interaction
+with the Revision Control System.
+</summary>
+<sets>
+RCS
+RCS_CO
+RCS_COFLAGS
+RCS_COCOM
+</sets>
+<uses>
+RCS_COCOMSTR
+</uses>
+</tool>
+
+<cvar name="RCS">
+<summary>
+The RCS executable.
+Note that this variable is not actually used
+for the command to fetch source files from RCS;
+see the
+&cv-link-RCS_CO;
+construction variable, below.
+</summary>
+</cvar>
+
+<cvar name="RCS_CO">
+<summary>
+The RCS "checkout" executable,
+used to fetch source files from RCS.
+</summary>
+</cvar>
+
+<cvar name="RCS_COCOM">
+<summary>
+The command line used to
+fetch (checkout) source files from RCS.
+</summary>
+</cvar>
+
+<cvar name="RCS_COCOMSTR">
+<summary>
+The string displayed when fetching
+a source file from RCS.
+If this is not set, then &cv-link-RCS_COCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="RCS_COFLAGS">
+<summary>
+Options that are passed to the &cv-link-RCS_CO; command.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/SCCS.py b/src/engine/SCons/Tool/SCCS.py
new file mode 100644
index 0000000..acbd9aa
--- /dev/null
+++ b/src/engine/SCons/Tool/SCCS.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.SCCS.py
+
+Tool-specific initialization for SCCS.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/SCCS.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+
+def generate(env):
+ """Add a Builder factory function and construction variables for
+ SCCS to an Environment."""
+
+ def SCCSFactory(env=env):
+ """ """
+ act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR')
+ return SCons.Builder.Builder(action = act, env = env)
+
+ #setattr(env, 'SCCS', SCCSFactory)
+ env.SCCS = SCCSFactory
+
+ env['SCCS'] = 'sccs'
+ env['SCCSFLAGS'] = SCons.Util.CLVar('')
+ env['SCCSGETFLAGS'] = SCons.Util.CLVar('')
+ env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET'
+
+def exists(env):
+ return env.Detect('sccs')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/SCCS.xml b/src/engine/SCons/Tool/SCCS.xml
new file mode 100644
index 0000000..83ccec7
--- /dev/null
+++ b/src/engine/SCons/Tool/SCCS.xml
@@ -0,0 +1,58 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="SCCS">
+<summary>
+Sets construction variables for interacting with the
+Source Code Control System.
+</summary>
+<sets>
+SCCS
+SCCSFLAGS
+SCCSGETFLAGS
+SCCSCOM
+</sets>
+<uses>
+SCCSCOMSTR
+</uses>
+</tool>
+
+<cvar name="SCCS">
+<summary>
+The SCCS executable.
+</summary>
+</cvar>
+
+<cvar name="SCCSCOM">
+<summary>
+The command line used to
+fetch source files from SCCS.
+</summary>
+</cvar>
+
+<cvar name="SCCSCOMSTR">
+<summary>
+The string displayed when fetching
+a source file from a CVS repository.
+If this is not set, then &cv-link-SCCSCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SCCSFLAGS">
+<summary>
+General options that are passed to SCCS.
+</summary>
+</cvar>
+
+<cvar name="SCCSGETFLAGS">
+<summary>
+Options that are passed specifically to the SCCS "get" subcommand.
+This can be set, for example, to
+<option>-e</option>
+to check out editable files from SCCS.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/Subversion.py b/src/engine/SCons/Tool/Subversion.py
new file mode 100644
index 0000000..55906bb
--- /dev/null
+++ b/src/engine/SCons/Tool/Subversion.py
@@ -0,0 +1,71 @@
+"""SCons.Tool.Subversion.py
+
+Tool-specific initialization for Subversion.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/Subversion.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+
+def generate(env):
+ """Add a Builder factory function and construction variables for
+ Subversion to an Environment."""
+
+ def SubversionFactory(repos, module='', env=env):
+ """ """
+ # fail if repos is not an absolute path name?
+ if module != '':
+ module = os.path.join(module, '')
+ act = SCons.Action.Action('$SVNCOM', '$SVNCOMSTR')
+ return SCons.Builder.Builder(action = act,
+ env = env,
+ SVNREPOSITORY = repos,
+ SVNMODULE = module)
+
+ #setattr(env, 'Subversion', SubversionFactory)
+ env.Subversion = SubversionFactory
+
+ env['SVN'] = 'svn'
+ env['SVNFLAGS'] = SCons.Util.CLVar('')
+ env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET'
+
+def exists(env):
+ return env.Detect('svn')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/Subversion.xml b/src/engine/SCons/Tool/Subversion.xml
new file mode 100644
index 0000000..40ca399
--- /dev/null
+++ b/src/engine/SCons/Tool/Subversion.xml
@@ -0,0 +1,47 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<!--
+<tool name="Subversion">
+<summary>
+Sets construction variables for interacting with Subversion.
+</summary>
+<sets>
+SVN
+SVNFLAGS
+SVNCOM
+</sets>
+<uses>
+SVNCOMSTR
+</uses>
+</tool>
+-->
+
+<!--
+<cvar name="SVN">
+<summary>
+The Subversion executable (usually named
+<command>svn</command>).
+</summary>
+</cvar>
+-->
+
+<!--
+<cvar name="SVNCOM">
+<summary>
+The command line used to
+fetch source files from a Subversion repository.
+</summary>
+</cvar>
+-->
+
+<!--
+<cvar name="SVNFLAGS">
+<summary>
+General options that are passed to Subversion.
+</summary>
+</cvar>
+-->
diff --git a/src/engine/SCons/Tool/ToolTests.py b/src/engine/SCons/Tool/ToolTests.py
new file mode 100644
index 0000000..0b6463e
--- /dev/null
+++ b/src/engine/SCons/Tool/ToolTests.py
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ToolTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Tool
+
+class ToolTestCase(unittest.TestCase):
+ def test_Tool(self):
+ """Test the Tool() function"""
+ class Environment:
+ def __init__(self):
+ self.dict = {}
+ def Detect(self, progs):
+ if not SCons.Util.is_List(progs):
+ progs = [ progs ]
+ return progs[0]
+ def Append(self, **kw):
+ self.dict.update(kw)
+ def __getitem__(self, key):
+ return self.dict[key]
+ def __setitem__(self, key, val):
+ self.dict[key] = val
+ def has_key(self, key):
+ return self.dict.has_key(key)
+ env = Environment()
+ env['BUILDERS'] = {}
+ env['ENV'] = {}
+ env['PLATFORM'] = 'test'
+ t = SCons.Tool.Tool('g++')
+ t(env)
+ assert (env['CXX'] == 'c++' or env['CXX'] == 'g++'), env['CXX']
+ assert env['INCPREFIX'] == '-I', env['INCPREFIX']
+ assert env['TOOLS'] == ['g++'], env['TOOLS']
+
+ try:
+ SCons.Tool.Tool()
+ except TypeError:
+ pass
+ else:
+ raise
+
+ try:
+ p = SCons.Tool.Tool('_does_not_exist_')
+ except SCons.Errors.EnvironmentError:
+ pass
+ else:
+ raise
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(ToolTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
new file mode 100644
index 0000000..a0eb2e8
--- /dev/null
+++ b/src/engine/SCons/Tool/__init__.py
@@ -0,0 +1,675 @@
+"""SCons.Tool
+
+SCons tool selection.
+
+This looks for modules that define a callable object that can modify
+a construction environment as appropriate for a given tool (or tool
+chain).
+
+Note that because this subsystem just *selects* a callable that can
+modify a construction environment, it's possible for people to define
+their own "tool specification" in an arbitrary callable function. No
+one needs to use or tie in to this subsystem in order to roll their own
+tool definition.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import imp
+import sys
+
+import SCons.Builder
+import SCons.Errors
+import SCons.Node.FS
+import SCons.Scanner
+import SCons.Scanner.C
+import SCons.Scanner.D
+import SCons.Scanner.LaTeX
+import SCons.Scanner.Prog
+
+DefaultToolpath=[]
+
+CScanner = SCons.Scanner.C.CScanner()
+DScanner = SCons.Scanner.D.DScanner()
+LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
+PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
+ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
+SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
+
+CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+ ".h", ".H", ".hxx", ".hpp", ".hh",
+ ".F", ".fpp", ".FPP",
+ ".m", ".mm",
+ ".S", ".spp", ".SPP"]
+
+DSuffixes = ['.d']
+
+IDLSuffixes = [".idl", ".IDL"]
+
+LaTeXSuffixes = [".tex", ".ltx", ".latex"]
+
+for suffix in CSuffixes:
+ SourceFileScanner.add_scanner(suffix, CScanner)
+
+for suffix in DSuffixes:
+ SourceFileScanner.add_scanner(suffix, DScanner)
+
+# FIXME: what should be done here? Two scanners scan the same extensions,
+# but look for different files, e.g., "picture.eps" vs. "picture.pdf".
+# The builders for DVI and PDF explicitly reference their scanners
+# I think that means this is not needed???
+for suffix in LaTeXSuffixes:
+ SourceFileScanner.add_scanner(suffix, LaTeXScanner)
+ SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner)
+
+class Tool:
+ def __init__(self, name, toolpath=[], **kw):
+ self.name = name
+ self.toolpath = toolpath + DefaultToolpath
+ # remember these so we can merge them into the call
+ self.init_kw = kw
+
+ module = self._tool_module()
+ self.generate = module.generate
+ self.exists = module.exists
+ if hasattr(module, 'options'):
+ self.options = module.options
+
+ def _tool_module(self):
+ # TODO: Interchange zipimport with normal initilization for better error reporting
+ oldpythonpath = sys.path
+ sys.path = self.toolpath + sys.path
+
+ try:
+ try:
+ file, path, desc = imp.find_module(self.name, self.toolpath)
+ try:
+ return imp.load_module(self.name, file, path, desc)
+ finally:
+ if file:
+ file.close()
+ except ImportError, e:
+ if str(e)!="No module named %s"%self.name:
+ raise SCons.Errors.EnvironmentError, e
+ try:
+ import zipimport
+ except ImportError:
+ pass
+ else:
+ for aPath in self.toolpath:
+ try:
+ importer = zipimport.zipimporter(aPath)
+ return importer.load_module(self.name)
+ except ImportError, e:
+ pass
+ finally:
+ sys.path = oldpythonpath
+
+ full_name = 'SCons.Tool.' + self.name
+ try:
+ return sys.modules[full_name]
+ except KeyError:
+ try:
+ smpath = sys.modules['SCons.Tool'].__path__
+ try:
+ file, path, desc = imp.find_module(self.name, smpath)
+ module = imp.load_module(full_name, file, path, desc)
+ setattr(SCons.Tool, self.name, module)
+ if file:
+ file.close()
+ return module
+ except ImportError, e:
+ if str(e)!="No module named %s"%self.name:
+ raise SCons.Errors.EnvironmentError, e
+ try:
+ import zipimport
+ importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] )
+ module = importer.load_module(full_name)
+ setattr(SCons.Tool, self.name, module)
+ return module
+ except ImportError, e:
+ m = "No tool named '%s': %s" % (self.name, e)
+ raise SCons.Errors.EnvironmentError, m
+ except ImportError, e:
+ m = "No tool named '%s': %s" % (self.name, e)
+ raise SCons.Errors.EnvironmentError, m
+
+ def __call__(self, env, *args, **kw):
+ if self.init_kw is not None:
+ # Merge call kws into init kws;
+ # but don't bash self.init_kw.
+ if kw is not None:
+ call_kw = kw
+ kw = self.init_kw.copy()
+ kw.update(call_kw)
+ else:
+ kw = self.init_kw
+ env.Append(TOOLS = [ self.name ])
+ if hasattr(self, 'options'):
+ import SCons.Variables
+ if not env.has_key('options'):
+ from SCons.Script import ARGUMENTS
+ env['options']=SCons.Variables.Variables(args=ARGUMENTS)
+ opts=env['options']
+
+ self.options(opts)
+ opts.Update(env)
+
+ apply(self.generate, ( env, ) + args, kw)
+
+ def __str__(self):
+ return self.name
+
+##########################################################################
+# Create common executable program / library / object builders
+
+def createProgBuilder(env):
+ """This is a utility function that creates the Program
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ program = env['BUILDERS']['Program']
+ except KeyError:
+ import SCons.Defaults
+ program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction,
+ emitter = '$PROGEMITTER',
+ prefix = '$PROGPREFIX',
+ suffix = '$PROGSUFFIX',
+ src_suffix = '$OBJSUFFIX',
+ src_builder = 'Object',
+ target_scanner = ProgramScanner)
+ env['BUILDERS']['Program'] = program
+
+ return program
+
+def createStaticLibBuilder(env):
+ """This is a utility function that creates the StaticLibrary
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ static_lib = env['BUILDERS']['StaticLibrary']
+ except KeyError:
+ action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
+ if env.Detect('ranlib'):
+ ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
+ action_list.append(ranlib_action)
+
+ static_lib = SCons.Builder.Builder(action = action_list,
+ emitter = '$LIBEMITTER',
+ prefix = '$LIBPREFIX',
+ suffix = '$LIBSUFFIX',
+ src_suffix = '$OBJSUFFIX',
+ src_builder = 'StaticObject')
+ env['BUILDERS']['StaticLibrary'] = static_lib
+ env['BUILDERS']['Library'] = static_lib
+
+ return static_lib
+
+def createSharedLibBuilder(env):
+ """This is a utility function that creates the SharedLibrary
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ shared_lib = env['BUILDERS']['SharedLibrary']
+ except KeyError:
+ import SCons.Defaults
+ action_list = [ SCons.Defaults.SharedCheck,
+ SCons.Defaults.ShLinkAction ]
+ shared_lib = SCons.Builder.Builder(action = action_list,
+ emitter = "$SHLIBEMITTER",
+ prefix = '$SHLIBPREFIX',
+ suffix = '$SHLIBSUFFIX',
+ target_scanner = ProgramScanner,
+ src_suffix = '$SHOBJSUFFIX',
+ src_builder = 'SharedObject')
+ env['BUILDERS']['SharedLibrary'] = shared_lib
+
+ return shared_lib
+
+def createLoadableModuleBuilder(env):
+ """This is a utility function that creates the LoadableModule
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ ld_module = env['BUILDERS']['LoadableModule']
+ except KeyError:
+ import SCons.Defaults
+ action_list = [ SCons.Defaults.SharedCheck,
+ SCons.Defaults.LdModuleLinkAction ]
+ ld_module = SCons.Builder.Builder(action = action_list,
+ emitter = "$LDMODULEEMITTER",
+ prefix = '$LDMODULEPREFIX',
+ suffix = '$LDMODULESUFFIX',
+ target_scanner = ProgramScanner,
+ src_suffix = '$SHOBJSUFFIX',
+ src_builder = 'SharedObject')
+ env['BUILDERS']['LoadableModule'] = ld_module
+
+ return ld_module
+
+def createObjBuilders(env):
+ """This is a utility function that creates the StaticObject
+ and SharedObject Builders in an Environment if they
+ are not there already.
+
+ If they are there already, we return the existing ones.
+
+ This is a separate function because soooo many Tools
+ use this functionality.
+
+ The return is a 2-tuple of (StaticObject, SharedObject)
+ """
+
+
+ try:
+ static_obj = env['BUILDERS']['StaticObject']
+ except KeyError:
+ static_obj = SCons.Builder.Builder(action = {},
+ emitter = {},
+ prefix = '$OBJPREFIX',
+ suffix = '$OBJSUFFIX',
+ src_builder = ['CFile', 'CXXFile'],
+ source_scanner = SourceFileScanner,
+ single_source = 1)
+ env['BUILDERS']['StaticObject'] = static_obj
+ env['BUILDERS']['Object'] = static_obj
+
+ try:
+ shared_obj = env['BUILDERS']['SharedObject']
+ except KeyError:
+ shared_obj = SCons.Builder.Builder(action = {},
+ emitter = {},
+ prefix = '$SHOBJPREFIX',
+ suffix = '$SHOBJSUFFIX',
+ src_builder = ['CFile', 'CXXFile'],
+ source_scanner = SourceFileScanner,
+ single_source = 1)
+ env['BUILDERS']['SharedObject'] = shared_obj
+
+ return (static_obj, shared_obj)
+
+def createCFileBuilders(env):
+ """This is a utility function that creates the CFile/CXXFile
+ Builders in an Environment if they
+ are not there already.
+
+ If they are there already, we return the existing ones.
+
+ This is a separate function because soooo many Tools
+ use this functionality.
+
+ The return is a 2-tuple of (CFile, CXXFile)
+ """
+
+ try:
+ c_file = env['BUILDERS']['CFile']
+ except KeyError:
+ c_file = SCons.Builder.Builder(action = {},
+ emitter = {},
+ suffix = {None:'$CFILESUFFIX'})
+ env['BUILDERS']['CFile'] = c_file
+
+ env.SetDefault(CFILESUFFIX = '.c')
+
+ try:
+ cxx_file = env['BUILDERS']['CXXFile']
+ except KeyError:
+ cxx_file = SCons.Builder.Builder(action = {},
+ emitter = {},
+ suffix = {None:'$CXXFILESUFFIX'})
+ env['BUILDERS']['CXXFile'] = cxx_file
+ env.SetDefault(CXXFILESUFFIX = '.cc')
+
+ return (c_file, cxx_file)
+
+##########################################################################
+# Create common Java builders
+
+def CreateJarBuilder(env):
+ try:
+ java_jar = env['BUILDERS']['Jar']
+ except KeyError:
+ fs = SCons.Node.FS.get_default_fs()
+ jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR')
+ java_jar = SCons.Builder.Builder(action = jar_com,
+ suffix = '$JARSUFFIX',
+ src_suffix = '$JAVACLASSSUFIX',
+ src_builder = 'JavaClassFile',
+ source_factory = fs.Entry)
+ env['BUILDERS']['Jar'] = java_jar
+ return java_jar
+
+def CreateJavaHBuilder(env):
+ try:
+ java_javah = env['BUILDERS']['JavaH']
+ except KeyError:
+ fs = SCons.Node.FS.get_default_fs()
+ java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR')
+ java_javah = SCons.Builder.Builder(action = java_javah_com,
+ src_suffix = '$JAVACLASSSUFFIX',
+ target_factory = fs.Entry,
+ source_factory = fs.File,
+ src_builder = 'JavaClassFile')
+ env['BUILDERS']['JavaH'] = java_javah
+ return java_javah
+
+def CreateJavaClassFileBuilder(env):
+ try:
+ java_class_file = env['BUILDERS']['JavaClassFile']
+ except KeyError:
+ fs = SCons.Node.FS.get_default_fs()
+ javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
+ java_class_file = SCons.Builder.Builder(action = javac_com,
+ emitter = {},
+ #suffix = '$JAVACLASSSUFFIX',
+ src_suffix = '$JAVASUFFIX',
+ src_builder = ['JavaFile'],
+ target_factory = fs.Entry,
+ source_factory = fs.File)
+ env['BUILDERS']['JavaClassFile'] = java_class_file
+ return java_class_file
+
+def CreateJavaClassDirBuilder(env):
+ try:
+ java_class_dir = env['BUILDERS']['JavaClassDir']
+ except KeyError:
+ fs = SCons.Node.FS.get_default_fs()
+ javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
+ java_class_dir = SCons.Builder.Builder(action = javac_com,
+ emitter = {},
+ target_factory = fs.Dir,
+ source_factory = fs.Dir)
+ env['BUILDERS']['JavaClassDir'] = java_class_dir
+ return java_class_dir
+
+def CreateJavaFileBuilder(env):
+ try:
+ java_file = env['BUILDERS']['JavaFile']
+ except KeyError:
+ java_file = SCons.Builder.Builder(action = {},
+ emitter = {},
+ suffix = {None:'$JAVASUFFIX'})
+ env['BUILDERS']['JavaFile'] = java_file
+ env['JAVASUFFIX'] = '.java'
+ return java_file
+
+class ToolInitializerMethod:
+ """
+ This is added to a construction environment in place of a
+ method(s) normally called for a Builder (env.Object, env.StaticObject,
+ etc.). When called, it has its associated ToolInitializer
+ object search the specified list of tools and apply the first
+ one that exists to the construction environment. It then calls
+ whatever builder was (presumably) added to the construction
+ environment in place of this particular instance.
+ """
+ def __init__(self, name, initializer):
+ """
+ Note: we store the tool name as __name__ so it can be used by
+ the class that attaches this to a construction environment.
+ """
+ self.__name__ = name
+ self.initializer = initializer
+
+ def get_builder(self, env):
+ """
+ Returns the appropriate real Builder for this method name
+ after having the associated ToolInitializer object apply
+ the appropriate Tool module.
+ """
+ builder = getattr(env, self.__name__)
+
+ self.initializer.apply_tools(env)
+
+ builder = getattr(env, self.__name__)
+ if builder is self:
+ # There was no Builder added, which means no valid Tool
+ # for this name was found (or possibly there's a mismatch
+ # between the name we were called by and the Builder name
+ # added by the Tool module).
+ return None
+
+ self.initializer.remove_methods(env)
+
+ return builder
+
+ def __call__(self, env, *args, **kw):
+ """
+ """
+ builder = self.get_builder(env)
+ if builder is None:
+ return [], []
+ return apply(builder, args, kw)
+
+class ToolInitializer:
+ """
+ A class for delayed initialization of Tools modules.
+
+ Instances of this class associate a list of Tool modules with
+ a list of Builder method names that will be added by those Tool
+ modules. As part of instantiating this object for a particular
+ construction environment, we also add the appropriate
+ ToolInitializerMethod objects for the various Builder methods
+ that we want to use to delay Tool searches until necessary.
+ """
+ def __init__(self, env, tools, names):
+ if not SCons.Util.is_List(tools):
+ tools = [tools]
+ if not SCons.Util.is_List(names):
+ names = [names]
+ self.env = env
+ self.tools = tools
+ self.names = names
+ self.methods = {}
+ for name in names:
+ method = ToolInitializerMethod(name, self)
+ self.methods[name] = method
+ env.AddMethod(method)
+
+ def remove_methods(self, env):
+ """
+ Removes the methods that were added by the tool initialization
+ so we no longer copy and re-bind them when the construction
+ environment gets cloned.
+ """
+ for method in self.methods.values():
+ env.RemoveMethod(method)
+
+ def apply_tools(self, env):
+ """
+ Searches the list of associated Tool modules for one that
+ exists, and applies that to the construction environment.
+ """
+ for t in self.tools:
+ tool = SCons.Tool.Tool(t)
+ if tool.exists(env):
+ env.Tool(tool)
+ return
+
+ # If we fall through here, there was no tool module found.
+ # This is where we can put an informative error message
+ # about the inability to find the tool. We'll start doing
+ # this as we cut over more pre-defined Builder+Tools to use
+ # the ToolInitializer class.
+
+def Initializers(env):
+ ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs'])
+ def Install(self, *args, **kw):
+ return apply(self._InternalInstall, args, kw)
+ def InstallAs(self, *args, **kw):
+ return apply(self._InternalInstallAs, args, kw)
+ env.AddMethod(Install)
+ env.AddMethod(InstallAs)
+
+def FindTool(tools, env):
+ for tool in tools:
+ t = Tool(tool)
+ if t.exists(env):
+ return tool
+ return None
+
+def FindAllTools(tools, env):
+ def ToolExists(tool, env=env):
+ return Tool(tool).exists(env)
+ return filter (ToolExists, tools)
+
+def tool_list(platform, env):
+
+ other_plat_tools=[]
+ # XXX this logic about what tool to prefer on which platform
+ # should be moved into either the platform files or
+ # the tool files themselves.
+ # The search orders here are described in the man page. If you
+ # change these search orders, update the man page as well.
+ if str(platform) == 'win32':
+ "prefer Microsoft tools on Windows"
+ linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ]
+ c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ]
+ cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ]
+ assemblers = ['masm', 'nasm', 'gas', '386asm' ]
+ fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran']
+ ars = ['mslib', 'ar', 'tlib']
+ other_plat_tools=['msvs','midl']
+ elif str(platform) == 'os2':
+ "prefer IBM tools on OS/2"
+ linkers = ['ilink', 'gnulink', ]#'mslink']
+ c_compilers = ['icc', 'gcc',]# 'msvc', 'cc']
+ cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++']
+ assemblers = ['nasm',]# 'masm', 'gas']
+ fortran_compilers = ['ifl', 'g77']
+ ars = ['ar',]# 'mslib']
+ elif str(platform) == 'irix':
+ "prefer MIPSPro on IRIX"
+ linkers = ['sgilink', 'gnulink']
+ c_compilers = ['sgicc', 'gcc', 'cc']
+ cxx_compilers = ['sgic++', 'g++', 'c++']
+ assemblers = ['as', 'gas']
+ fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
+ ars = ['sgiar']
+ elif str(platform) == 'sunos':
+ "prefer Forte tools on SunOS"
+ linkers = ['sunlink', 'gnulink']
+ c_compilers = ['suncc', 'gcc', 'cc']
+ cxx_compilers = ['sunc++', 'g++', 'c++']
+ assemblers = ['as', 'gas']
+ fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77',
+ 'gfortran', 'g77', 'fortran']
+ ars = ['sunar']
+ elif str(platform) == 'hpux':
+ "prefer aCC tools on HP-UX"
+ linkers = ['hplink', 'gnulink']
+ c_compilers = ['hpcc', 'gcc', 'cc']
+ cxx_compilers = ['hpc++', 'g++', 'c++']
+ assemblers = ['as', 'gas']
+ fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
+ ars = ['ar']
+ elif str(platform) == 'aix':
+ "prefer AIX Visual Age tools on AIX"
+ linkers = ['aixlink', 'gnulink']
+ c_compilers = ['aixcc', 'gcc', 'cc']
+ cxx_compilers = ['aixc++', 'g++', 'c++']
+ assemblers = ['as', 'gas']
+ fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran']
+ ars = ['ar']
+ elif str(platform) == 'darwin':
+ "prefer GNU tools on Mac OS X, except for some linkers and IBM tools"
+ linkers = ['applelink', 'gnulink']
+ c_compilers = ['gcc', 'cc']
+ cxx_compilers = ['g++', 'c++']
+ assemblers = ['as']
+ fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
+ ars = ['ar']
+ else:
+ "prefer GNU tools on all other platforms"
+ linkers = ['gnulink', 'mslink', 'ilink']
+ c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
+ cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
+ assemblers = ['gas', 'nasm', 'masm']
+ fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
+ ars = ['ar', 'mslib']
+
+ c_compiler = FindTool(c_compilers, env) or c_compilers[0]
+
+ # XXX this logic about what tool provides what should somehow be
+ # moved into the tool files themselves.
+ if c_compiler and c_compiler == 'mingw':
+ # MinGW contains a linker, C compiler, C++ compiler,
+ # Fortran compiler, archiver and assembler:
+ cxx_compiler = None
+ linker = None
+ assembler = None
+ fortran_compiler = None
+ ar = None
+ else:
+ # Don't use g++ if the C compiler has built-in C++ support:
+ if c_compiler in ('msvc', 'intelc', 'icc'):
+ cxx_compiler = None
+ else:
+ cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
+ linker = FindTool(linkers, env) or linkers[0]
+ assembler = FindTool(assemblers, env) or assemblers[0]
+ fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
+ ar = FindTool(ars, env) or ars[0]
+
+ other_tools = FindAllTools(['BitKeeper', 'CVS',
+ 'dmd',
+ 'filesystem',
+ 'dvipdf', 'dvips', 'gs',
+ 'jar', 'javac', 'javah',
+ 'latex', 'lex',
+ 'm4', #'midl', 'msvs',
+ 'pdflatex', 'pdftex', 'Perforce',
+ 'RCS', 'rmic', 'rpcgen',
+ 'SCCS',
+ # 'Subversion',
+ 'swig',
+ 'tar', 'tex',
+ 'yacc', 'zip', 'rpm', 'wix']+other_plat_tools,
+ env)
+
+ tools = ([linker, c_compiler, cxx_compiler,
+ fortran_compiler, assembler, ar]
+ + other_tools)
+
+ return filter(lambda x: x, tools)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml
new file mode 100644
index 0000000..fa3e37a
--- /dev/null
+++ b/src/engine/SCons/Tool/__init__.xml
@@ -0,0 +1,367 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<builder name="CFile">
+<summary>
+Builds a C source file given a lex (<filename>.l</filename>)
+or yacc (<filename>.y</filename>) input file.
+The suffix specified by the &cv-link-CFILESUFFIX; construction variable
+(<filename>.c</filename> by default)
+is automatically added to the target
+if it is not already present.
+Example:
+
+<example>
+# builds foo.c
+env.CFile(target = 'foo.c', source = 'foo.l')
+# builds bar.c
+env.CFile(target = 'bar', source = 'bar.y')
+</example>
+</summary>
+</builder>
+
+<builder name="CXXFile">
+<summary>
+Builds a C++ source file given a lex (<filename>.ll</filename>)
+or yacc (<filename>.yy</filename>)
+input file.
+The suffix specified by the &cv-link-CXXFILESUFFIX; construction variable
+(<filename>.cc</filename> by default)
+is automatically added to the target
+if it is not already present.
+Example:
+
+<example>
+# builds foo.cc
+env.CXXFile(target = 'foo.cc', source = 'foo.ll')
+# builds bar.cc
+env.CXXFile(target = 'bar', source = 'bar.yy')
+</example>
+</summary>
+</builder>
+
+<builder name="Library">
+<summary>
+A synonym for the
+&b-StaticLibrary;
+builder method.
+</summary>
+</builder>
+
+<builder name="LoadableModule">
+<summary>
+On most systems,
+this is the same as
+&b-SharedLibrary;.
+On Mac OS X (Darwin) platforms,
+this creates a loadable module bundle.
+</summary>
+</builder>
+
+<builder name="Object">
+<summary>
+A synonym for the
+&b-StaticObject;
+builder method.
+</summary>
+</builder>
+
+<builder name="Program">
+<summary>
+Builds an executable given one or more object files
+or C, C++, D, or Fortran source files.
+If any C, C++, D or Fortran source files are specified,
+then they will be automatically
+compiled to object files using the
+&b-Object;
+builder method;
+see that builder method's description for
+a list of legal source file suffixes
+and how they are interpreted.
+The target executable file prefix
+(specified by the &cv-link-PROGPREFIX; construction variable; nothing by default)
+and suffix
+(specified by the &cv-link-PROGSUFFIX; construction variable;
+by default, <filename>.exe</filename> on Windows systems,
+nothing on POSIX systems)
+are automatically added to the target if not already present.
+Example:
+
+<example>
+env.Program(target = 'foo', source = ['foo.o', 'bar.c', 'baz.f'])
+</example>
+</summary>
+</builder>
+
+<builder name="SharedLibrary">
+<summary>
+Builds a shared library
+(<filename>.so</filename> on a POSIX system,
+<filename>.dll</filename> on Windows)
+given one or more object files
+or C, C++, D or Fortran source files.
+If any source files are given,
+then they will be automatically
+compiled to object files.
+The static library prefix and suffix (if any)
+are automatically added to the target.
+The target library file prefix
+(specified by the &cv-link-SHLIBPREFIX; construction variable;
+by default, <filename>lib</filename> on POSIX systems,
+nothing on Windows systems)
+and suffix
+(specified by the &cv-link-SHLIBSUFFIX; construction variable;
+by default, <filename>.dll</filename> on Windows systems,
+<filename>.so</filename> on POSIX systems)
+are automatically added to the target if not already present.
+Example:
+
+<example>
+env.SharedLibrary(target = 'bar', source = ['bar.c', 'foo.o'])
+</example>
+
+On Windows systems, the
+&b-SharedLibrary;
+builder method will always build an import
+(<filename>.lib</filename>) library
+in addition to the shared (<filename>.dll</filename>) library,
+adding a <filename>.lib</filename> library with the same basename
+if there is not already a <filename>.lib</filename> file explicitly
+listed in the targets.
+
+Any object files listed in the
+<literal>source</literal>
+must have been built for a shared library
+(that is, using the
+&b-SharedObject;
+builder method).
+&scons;
+will raise an error if there is any mismatch.
+
+On some platforms, there is a distinction between a shared library
+(loaded automatically by the system to resolve external references)
+and a loadable module (explicitly loaded by user action).
+For maximum portability, use the &b-LoadableModule; builder for the latter.
+
+On Windows systems, specifying
+<literal>register=1</literal>
+will cause the <filename>.dll</filename> to be
+registered after it is built using REGSVR32.
+The command that is run
+("regsvr32" by default) is determined by &cv-link-REGSVR; construction
+variable, and the flags passed are determined by &cv-link-REGSVRFLAGS;. By
+default, &cv-link-REGSVRFLAGS; includes the <option>/s</option> option,
+to prevent dialogs from popping
+up and requiring user attention when it is run. If you change
+&cv-link-REGSVRFLAGS;, be sure to include the <option>/s</option> option.
+For example,
+
+<example>
+env.SharedLibrary(target = 'bar',
+ source = ['bar.cxx', 'foo.obj'],
+ register=1)
+</example>
+
+will register <filename>bar.dll</filename> as a COM object
+when it is done linking it.
+</summary>
+</builder>
+
+<builder name="SharedObject">
+<summary>
+Builds an object file for
+inclusion in a shared library.
+Source files must have one of the same set of extensions
+specified above for the
+&b-StaticObject;
+builder method.
+On some platforms building a shared object requires additional
+compiler option
+(e.g. <option>-fPIC</option> for gcc)
+in addition to those needed to build a
+normal (static) object, but on some platforms there is no difference between a
+shared object and a normal (static) one. When there is a difference, SCons
+will only allow shared objects to be linked into a shared library, and will
+use a different suffix for shared objects. On platforms where there is no
+difference, SCons will allow both normal (static)
+and shared objects to be linked into a
+shared library, and will use the same suffix for shared and normal
+(static) objects.
+The target object file prefix
+(specified by the &cv-link-SHOBJPREFIX; construction variable;
+by default, the same as &cv-link-OBJPREFIX;)
+and suffix
+(specified by the &cv-link-SHOBJSUFFIX; construction variable)
+are automatically added to the target if not already present.
+Examples:
+
+<example>
+env.SharedObject(target = 'ddd', source = 'ddd.c')
+env.SharedObject(target = 'eee.o', source = 'eee.cpp')
+env.SharedObject(target = 'fff.obj', source = 'fff.for')
+</example>
+
+Note that the source files will be scanned
+according to the suffix mappings in the
+<literal>SourceFileScanner</literal>
+object.
+See the section "Scanner Objects,"
+below, for a more information.
+</summary>
+</builder>
+
+<builder name="StaticLibrary">
+<summary>
+Builds a static library given one or more object files
+or C, C++, D or Fortran source files.
+If any source files are given,
+then they will be automatically
+compiled to object files.
+The static library prefix and suffix (if any)
+are automatically added to the target.
+The target library file prefix
+(specified by the &cv-link-LIBPREFIX; construction variable;
+by default, <filename>lib</filename> on POSIX systems,
+nothing on Windows systems)
+and suffix
+(specified by the &cv-link-LIBSUFFIX; construction variable;
+by default, <filename>.lib</filename> on Windows systems,
+<filename>.a</filename> on POSIX systems)
+are automatically added to the target if not already present.
+Example:
+
+<example>
+env.StaticLibrary(target = 'bar', source = ['bar.c', 'foo.o'])
+</example>
+
+Any object files listed in the
+<literal>source</literal>
+must have been built for a static library
+(that is, using the
+&b-StaticObject;
+builder method).
+&scons;
+will raise an error if there is any mismatch.
+</summary>
+</builder>
+
+<builder name="StaticObject">
+<summary>
+Builds a static object file
+from one or more C, C++, D, or Fortran source files.
+Source files must have one of the following extensions:
+
+<example>
+ .asm assembly language file
+ .ASM assembly language file
+ .c C file
+ .C Windows: C file
+ POSIX: C++ file
+ .cc C++ file
+ .cpp C++ file
+ .cxx C++ file
+ .cxx C++ file
+ .c++ C++ file
+ .C++ C++ file
+ .d D file
+ .f Fortran file
+ .F Windows: Fortran file
+ POSIX: Fortran file + C pre-processor
+ .for Fortran file
+ .FOR Fortran file
+ .fpp Fortran file + C pre-processor
+ .FPP Fortran file + C pre-processor
+ .m Object C file
+ .mm Object C++ file
+ .s assembly language file
+ .S Windows: assembly language file
+ ARM: CodeSourcery Sourcery Lite
+ .sx assembly language file + C pre-processor
+ POSIX: assembly language file + C pre-processor
+ .spp assembly language file + C pre-processor
+ .SPP assembly language file + C pre-processor
+</example>
+
+The target object file prefix
+(specified by the &cv-link-OBJPREFIX; construction variable; nothing by default)
+and suffix
+(specified by the &cv-link-OBJSUFFIX; construction variable;
+<filename>.obj</filename> on Windows systems,
+<filename>.o</filename> on POSIX systems)
+are automatically added to the target if not already present.
+Examples:
+
+<example>
+env.StaticObject(target = 'aaa', source = 'aaa.c')
+env.StaticObject(target = 'bbb.o', source = 'bbb.c++')
+env.StaticObject(target = 'ccc.obj', source = 'ccc.f')
+</example>
+
+Note that the source files will be scanned
+according to the suffix mappings in
+<literal>SourceFileScanner</literal>
+object.
+See the section "Scanner Objects,"
+below, for a more information.
+</summary>
+</builder>
+
+<cvar name="CCVERSION">
+<summary>
+The version number of the C compiler.
+This may or may not be set,
+depending on the specific C compiler being used.
+</summary>
+</cvar>
+
+<cvar name="CFILESUFFIX">
+<summary>
+The suffix for C source files.
+This is used by the internal CFile builder
+when generating C files from Lex (.l) or YACC (.y) input files.
+The default suffix, of course, is
+<filename>.c</filename>
+(lower case).
+On case-insensitive systems (like Windows),
+SCons also treats
+<filename>.C</filename>
+(upper case) files
+as C files.
+</summary>
+</cvar>
+
+<cvar name="CXXVERSION">
+<summary>
+The version number of the C++ compiler.
+This may or may not be set,
+depending on the specific C++ compiler being used.
+</summary>
+</cvar>
+
+<cvar name="CXXFILESUFFIX">
+<summary>
+The suffix for C++ source files.
+This is used by the internal CXXFile builder
+when generating C++ files from Lex (.ll) or YACC (.yy) input files.
+The default suffix is
+<filename>.cc</filename>.
+SCons also treats files with the suffixes
+<filename>.cpp</filename>,
+<filename>.cxx</filename>,
+<filename>.c++</filename>,
+and
+<filename>.C++</filename>
+as C++ files,
+and files with
+<filename>.mm</filename>
+suffixes as Objective C++ files.
+On case-sensitive systems (Linux, UNIX, and other POSIX-alikes),
+SCons also treats
+<filename>.C</filename>
+(upper case) files
+as C++ files.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/aixc++.py b/src/engine/SCons/Tool/aixc++.py
new file mode 100644
index 0000000..c141cec
--- /dev/null
+++ b/src/engine/SCons/Tool/aixc++.py
@@ -0,0 +1,82 @@
+"""SCons.Tool.aixc++
+
+Tool-specific initialization for IBM xlC / Visual Age C++ compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/aixc++.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import SCons.Platform.aix
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp']
+
+def get_xlc(env):
+ xlc = env.get('CXX', 'xlC')
+ xlc_r = env.get('SHCXX', 'xlC_r')
+ return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages)
+
+def smart_cxxflags(source, target, env, for_signature):
+ build_dir = env.GetBuildPath()
+ if build_dir:
+ return '-qtempinc=' + os.path.join(build_dir, 'tempinc')
+ return ''
+
+def generate(env):
+ """Add Builders and construction variables for xlC / Visual Age
+ suite to an Environment."""
+ path, _cxx, _shcxx, version = get_xlc(env)
+ if path:
+ _cxx = os.path.join(path, _cxx)
+ _shcxx = os.path.join(path, _shcxx)
+
+ cplusplus.generate(env)
+
+ env['CXX'] = _cxx
+ env['SHCXX'] = _shcxx
+ env['CXXVERSION'] = version
+ env['SHOBJSUFFIX'] = '.pic.o'
+
+def exists(env):
+ path, _cxx, _shcxx, version = get_xlc(env)
+ if path and _cxx:
+ xlc = os.path.join(path, _cxx)
+ if os.path.exists(xlc):
+ return xlc
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/aixc++.xml b/src/engine/SCons/Tool/aixc++.xml
new file mode 100644
index 0000000..d4fcdaf
--- /dev/null
+++ b/src/engine/SCons/Tool/aixc++.xml
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="aixc++">
+<summary>
+Sets construction variables for the IMB xlc / Visual Age C++ compiler.
+</summary>
+<sets>
+CXX
+SHCXX
+CXXVERSION
+SHOBJSUFFIX
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/aixcc.py b/src/engine/SCons/Tool/aixcc.py
new file mode 100644
index 0000000..23ef705
--- /dev/null
+++ b/src/engine/SCons/Tool/aixcc.py
@@ -0,0 +1,74 @@
+"""SCons.Tool.aixcc
+
+Tool-specific initialization for IBM xlc / Visual Age C compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/aixcc.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import SCons.Platform.aix
+
+import cc
+
+packages = ['vac.C', 'ibmcxx.cmp']
+
+def get_xlc(env):
+ xlc = env.get('CC', 'xlc')
+ xlc_r = env.get('SHCC', 'xlc_r')
+ return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages)
+
+def generate(env):
+ """Add Builders and construction variables for xlc / Visual Age
+ suite to an Environment."""
+ path, _cc, _shcc, version = get_xlc(env)
+ if path:
+ _cc = os.path.join(path, _cc)
+ _shcc = os.path.join(path, _shcc)
+
+ cc.generate(env)
+
+ env['CC'] = _cc
+ env['SHCC'] = _shcc
+ env['CCVERSION'] = version
+
+def exists(env):
+ path, _cc, _shcc, version = get_xlc(env)
+ if path and _cc:
+ xlc = os.path.join(path, _cc)
+ if os.path.exists(xlc):
+ return xlc
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/aixcc.xml b/src/engine/SCons/Tool/aixcc.xml
new file mode 100644
index 0000000..df1c4f3
--- /dev/null
+++ b/src/engine/SCons/Tool/aixcc.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="aixcc">
+<summary>
+Sets construction variables for the IBM xlc / Visual Age C compiler.
+</summary>
+<sets>
+CC
+SHCC
+CCVERSION
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/aixf77.py b/src/engine/SCons/Tool/aixf77.py
new file mode 100644
index 0000000..5c0a3a7
--- /dev/null
+++ b/src/engine/SCons/Tool/aixf77.py
@@ -0,0 +1,80 @@
+"""engine.SCons.Tool.aixf77
+
+Tool-specific initialization for IBM Visual Age f77 Fortran compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/aixf77.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+#import SCons.Platform.aix
+
+import f77
+
+# It would be good to look for the AIX F77 package the same way we're now
+# looking for the C and C++ packages. This should be as easy as supplying
+# the correct package names in the following list and uncommenting the
+# SCons.Platform.aix_get_xlc() call the in the function below.
+packages = []
+
+def get_xlf77(env):
+ xlf77 = env.get('F77', 'xlf77')
+ xlf77_r = env.get('SHF77', 'xlf77_r')
+ #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages)
+ return (None, xlf77, xlf77_r, None)
+
+def generate(env):
+ """
+ Add Builders and construction variables for the Visual Age FORTRAN
+ compiler to an Environment.
+ """
+ path, _f77, _shf77, version = get_xlf77(env)
+ if path:
+ _f77 = os.path.join(path, _f77)
+ _shf77 = os.path.join(path, _shf77)
+
+ f77.generate(env)
+
+ env['F77'] = _f77
+ env['SHF77'] = _shf77
+
+def exists(env):
+ path, _f77, _shf77, version = get_xlf77(env)
+ if path and _f77:
+ xlf77 = os.path.join(path, _f77)
+ if os.path.exists(xlf77):
+ return xlf77
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/aixf77.xml b/src/engine/SCons/Tool/aixf77.xml
new file mode 100644
index 0000000..aa428c4
--- /dev/null
+++ b/src/engine/SCons/Tool/aixf77.xml
@@ -0,0 +1,17 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="aixf77">
+<summary>
+Sets construction variables for the IBM Visual Age f77 Fortran compiler.
+</summary>
+<sets>
+F77
+SHF77
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/aixlink.py b/src/engine/SCons/Tool/aixlink.py
new file mode 100644
index 0000000..2c036ff
--- /dev/null
+++ b/src/engine/SCons/Tool/aixlink.py
@@ -0,0 +1,76 @@
+"""SCons.Tool.aixlink
+
+Tool-specific initialization for the IBM Visual Age linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/aixlink.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+
+import SCons.Util
+
+import aixcc
+import link
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+def smart_linkflags(source, target, env, for_signature):
+ if cplusplus.iscplusplus(source):
+ build_dir = env.subst('$BUILDDIR', target=target, source=source)
+ if build_dir:
+ return '-qtempinc=' + os.path.join(build_dir, 'tempinc')
+ return ''
+
+def generate(env):
+ """
+ Add Builders and construction variables for Visual Age linker to
+ an Environment.
+ """
+ link.generate(env)
+
+ env['SMARTLINKFLAGS'] = smart_linkflags
+ env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS')
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218')
+ env['SHLIBSUFFIX'] = '.a'
+
+def exists(env):
+ path, _cc, _shcc, version = aixcc.get_xlc(env)
+ if path and _cc:
+ xlc = os.path.join(path, _cc)
+ if os.path.exists(xlc):
+ return xlc
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/aixlink.xml b/src/engine/SCons/Tool/aixlink.xml
new file mode 100644
index 0000000..b2ef46e
--- /dev/null
+++ b/src/engine/SCons/Tool/aixlink.xml
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="aixlink">
+<summary>
+Sets construction variables for the IBM Visual Age linker.
+</summary>
+<sets>
+<!--SMARTLINKFLAGS-->
+LINKFLAGS
+SHLINKFLAGS
+SHLIBSUFFIX
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py
new file mode 100644
index 0000000..4e3b802
--- /dev/null
+++ b/src/engine/SCons/Tool/applelink.py
@@ -0,0 +1,71 @@
+"""SCons.Tool.applelink
+
+Tool-specific initialization for the Apple gnu-like linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/applelink.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+# Even though the Mac is based on the GNU toolchain, it doesn't understand
+# the -rpath option, so we use the "link" tool instead of "gnulink".
+import link
+
+def generate(env):
+ """Add Builders and construction variables for applelink to an
+ Environment."""
+ link.generate(env)
+
+ env['FRAMEWORKPATHPREFIX'] = '-F'
+ env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}'
+ env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}'
+ env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib')
+ env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS'
+
+ # override the default for loadable modules, which are different
+ # on OS X than dynamic shared libs. echoing what XCode does for
+ # pre/suffixes:
+ env['LDMODULEPREFIX'] = ''
+ env['LDMODULESUFFIX'] = ''
+ env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle')
+ env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS'
+
+
+
+def exists(env):
+ return env['PLATFORM'] == 'darwin'
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/applelink.xml b/src/engine/SCons/Tool/applelink.xml
new file mode 100644
index 0000000..29c8183
--- /dev/null
+++ b/src/engine/SCons/Tool/applelink.xml
@@ -0,0 +1,114 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="applelink">
+<summary>
+Sets construction variables for the Apple linker
+(similar to the GNU linker).
+</summary>
+<sets>
+FRAMEWORKPATHPREFIX
+_FRAMEWORKPATH
+_FRAMEWORKS
+LINKCOM
+SHLINKFLAGS
+SHLINKCOM
+LDMODULEPREFIX
+LDMODULESUFFIX
+LDMODULEFLAGS
+LDMODULECOM
+</sets>
+<uses>
+FRAMEWORKSFLAGS
+</uses>
+</tool>
+
+<cvar name="FRAMEWORKSFLAGS">">
+<summary>
+On Mac OS X with gcc,
+general user-supplied frameworks options to be added at
+the end of a command
+line building a loadable module.
+(This has been largely superceded by
+the &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKPATHPREFIX;,
+&cv-link-FRAMEWORKPREFIX; and &cv-link-FRAMEWORKS; variables
+described above.)
+</summary>
+</cvar>
+
+<cvar name="FRAMEWORKS">
+<summary>
+On Mac OS X with gcc, a list of the framework names to be linked into a
+program or shared library or bundle.
+The default value is the empty list.
+For example:
+
+<example>
+ env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration'))
+</example>
+
+</summary>
+</cvar>
+
+<cvar name="FRAMEWORKPREFIX">
+<summary>
+On Mac OS X with gcc,
+the prefix to be used for linking in frameworks
+(see &cv-link-FRAMEWORKS;).
+The default value is
+<option>-framework</option>.
+</summary>
+</cvar>
+
+<cvar name="_FRAMEWORKS">
+<summary>
+On Mac OS X with gcc,
+an automatically-generated construction variable
+containing the linker command-line options
+for linking with FRAMEWORKS.
+</summary>
+</cvar>
+
+<cvar name="FRAMEWORKPATH">
+<summary>
+On Mac OS X with gcc,
+a list containing the paths to search for frameworks.
+Used by the compiler to find framework-style includes like
+#include &lt;Fmwk/Header.h&gt;.
+Used by the linker to find user-specified frameworks when linking (see
+&cv-link-FRAMEWORKS;).
+For example:
+
+<example>
+ env.AppendUnique(FRAMEWORKPATH='#myframeworkdir')
+</example>
+
+will add
+
+<example>
+ ... -Fmyframeworkdir
+</example>
+
+to the compiler and linker command lines.
+</summary>
+</cvar>
+
+<cvar name="FRAMEWORKPATHPREFIX">
+<summary>
+On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries.
+(see &cv-link-FRAMEWORKPATH;).
+The default value is
+<option>-F</option>.
+</summary>
+</cvar>
+
+<cvar name="_FRAMEWORKPATH">
+<summary>
+On Mac OS X with gcc, an automatically-generated construction variable
+containing the linker command-line options corresponding to
+&cv-link-FRAMEWORKPATH;.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/ar.py b/src/engine/SCons/Tool/ar.py
new file mode 100644
index 0000000..cdffcdf
--- /dev/null
+++ b/src/engine/SCons/Tool/ar.py
@@ -0,0 +1,63 @@
+"""SCons.Tool.ar
+
+Tool-specific initialization for ar (library archive).
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ar.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+
+def generate(env):
+ """Add Builders and construction variables for ar to an Environment."""
+ SCons.Tool.createStaticLibBuilder(env)
+
+ env['AR'] = 'ar'
+ env['ARFLAGS'] = SCons.Util.CLVar('rc')
+ env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES'
+ env['LIBPREFIX'] = 'lib'
+ env['LIBSUFFIX'] = '.a'
+
+ if env.Detect('ranlib'):
+ env['RANLIB'] = 'ranlib'
+ env['RANLIBFLAGS'] = SCons.Util.CLVar('')
+ env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET'
+
+def exists(env):
+ return env.Detect('ar')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/ar.xml b/src/engine/SCons/Tool/ar.xml
new file mode 100644
index 0000000..a359823
--- /dev/null
+++ b/src/engine/SCons/Tool/ar.xml
@@ -0,0 +1,82 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="ar">
+<summary>
+Sets construction variables for the &ar; library archiver.
+</summary>
+<sets>
+AR
+ARFLAGS
+ARCOM
+LIBPREFIX
+LIBSUFFIX
+RANLIB
+RANLIBFLAGS
+RANLIBCOM
+</sets>
+<uses>
+</uses>
+</tool>
+
+<cvar name="AR">
+<summary>
+The static library archiver.
+</summary>
+</cvar>
+
+<cvar name="ARCOM">
+<summary>
+The command line used to generate a static library from object files.
+</summary>
+</cvar>
+
+<cvar name="ARCOMSTR">
+<summary>
+The string displayed when an object file
+is generated from an assembly-language source file.
+If this is not set, then &cv-link-ARCOM; (the command line) is displayed.
+
+<example>
+env = Environment(ARCOMSTR = "Archiving $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="ARFLAGS">
+<summary>
+General options passed to the static library archiver.
+</summary>
+</cvar>
+
+<cvar name="RANLIB">
+<summary>
+The archive indexer.
+</summary>
+</cvar>
+
+<cvar name="RANLIBCOM">
+<summary>
+The command line used to index a static library archive.
+</summary>
+</cvar>
+
+<cvar name="RANLIBCOMSTR">
+<summary>
+The string displayed when a static library archive is indexed.
+If this is not set, then &cv-link-RANLIBCOM; (the command line) is displayed.
+
+<example>
+env = Environment(RANLIBCOMSTR = "Indexing $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="RANLIBFLAGS">
+<summary>
+General options passed to the archive indexer.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/as.py b/src/engine/SCons/Tool/as.py
new file mode 100644
index 0000000..2e180ce
--- /dev/null
+++ b/src/engine/SCons/Tool/as.py
@@ -0,0 +1,78 @@
+"""SCons.Tool.as
+
+Tool-specific initialization for as, the generic Posix assembler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/as.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+assemblers = ['as']
+
+ASSuffixes = ['.s', '.asm', '.ASM']
+ASPPSuffixes = ['.spp', '.SPP', '.sx']
+if SCons.Util.case_sensitive_suffixes('.s', '.S'):
+ ASPPSuffixes.extend(['.S'])
+else:
+ ASSuffixes.extend(['.S'])
+
+def generate(env):
+ """Add Builders and construction variables for as to an Environment."""
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in ASSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.ASAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ASAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ for suffix in ASPPSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.ASPPAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ASPPAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ env['AS'] = env.Detect(assemblers) or 'as'
+ env['ASFLAGS'] = SCons.Util.CLVar('')
+ env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES'
+ env['ASPPFLAGS'] = '$ASFLAGS'
+ env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES'
+
+def exists(env):
+ return env.Detect(assemblers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/as.xml b/src/engine/SCons/Tool/as.xml
new file mode 100644
index 0000000..94af6b6
--- /dev/null
+++ b/src/engine/SCons/Tool/as.xml
@@ -0,0 +1,88 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="as">
+<summary>
+Sets construction variables for the &as; assembler.
+</summary>
+<sets>
+AS
+ASFLAGS
+ASCOM
+ASPPFLAGS
+ASPPCOM
+</sets>
+<uses>
+CC
+CPPFLAGS
+_CPPDEFFLAGS
+_CPPINCFLAGS
+</uses>
+</tool>
+
+<cvar name="AS">
+<summary>
+The assembler.
+</summary>
+</cvar>
+
+<cvar name="ASCOM">
+<summary>
+The command line used to generate an object file
+from an assembly-language source file.
+</summary>
+</cvar>
+
+<cvar name="ASCOMSTR">
+<summary>
+The string displayed when an object file
+is generated from an assembly-language source file.
+If this is not set, then &cv-link-ASCOM; (the command line) is displayed.
+
+<example>
+env = Environment(ASCOMSTR = "Assembling $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="ASFLAGS">
+<summary>
+General options passed to the assembler.
+</summary>
+</cvar>
+
+<cvar name="ASPPCOM">
+<summary>
+The command line used to assemble an assembly-language
+source file into an object file
+after first running the file through the C preprocessor.
+Any options specified
+in the &cv-link-ASFLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="ASPPCOMSTR">
+<summary>
+The string displayed when an object file
+is generated from an assembly-language source file
+after first running the file through the C preprocessor.
+If this is not set, then &cv-link-ASPPCOM; (the command line) is displayed.
+
+<example>
+env = Environment(ASPPCOMSTR = "Assembling $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="ASPPFLAGS">
+<summary>
+General options when an assembling an assembly-language
+source file into an object file
+after first running the file through the C preprocessor.
+The default is to use the value of &cv-link-ASFLAGS;.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/bcc32.py b/src/engine/SCons/Tool/bcc32.py
new file mode 100644
index 0000000..71daa2c
--- /dev/null
+++ b/src/engine/SCons/Tool/bcc32.py
@@ -0,0 +1,82 @@
+"""SCons.Tool.bcc32
+
+XXX
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/bcc32.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+def findIt(program, env):
+ # First search in the SCons path and then the OS path:
+ borwin = env.WhereIs(program) or SCons.Util.WhereIs(program)
+ if borwin:
+ dir = os.path.dirname(borwin)
+ env.PrependENVPath('PATH', dir)
+ return borwin
+
+def generate(env):
+ findIt('bcc32', env)
+ """Add Builders and construction variables for bcc to an
+ Environment."""
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+ for suffix in ['.c', '.cpp']:
+ static_obj.add_action(suffix, SCons.Defaults.CAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ env['CC'] = 'bcc32'
+ env['CCFLAGS'] = SCons.Util.CLVar('')
+ env['CFLAGS'] = SCons.Util.CLVar('')
+ env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES'
+ env['SHCC'] = '$CC'
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+ env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
+ env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES'
+ env['CPPDEFPREFIX'] = '-D'
+ env['CPPDEFSUFFIX'] = ''
+ env['INCPREFIX'] = '-I'
+ env['INCSUFFIX'] = ''
+ env['SHOBJSUFFIX'] = '.dll'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0
+ env['CFILESUFFIX'] = '.cpp'
+
+def exists(env):
+ return findIt('bcc32', env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/bcc32.xml b/src/engine/SCons/Tool/bcc32.xml
new file mode 100644
index 0000000..c7f3155
--- /dev/null
+++ b/src/engine/SCons/Tool/bcc32.xml
@@ -0,0 +1,32 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="bcc32">
+<summary>
+Sets construction variables for the bcc32 compiler.
+</summary>
+<sets>
+CC
+CCFLAGS
+CFLAGS
+CCCOM
+SHCC
+SHCCFLAGS
+SHCFLAGS
+SHCCCOM
+CPPDEFPREFIX
+CPPDEFSUFFIX
+INCPREFIX
+INCSUFFIX
+SHOBJSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+CFILESUFFIX
+</sets>
+<uses>
+_CPPDEFFLAGS
+_CPPINCFLAGS
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/c++.py b/src/engine/SCons/Tool/c++.py
new file mode 100644
index 0000000..ad7fa13
--- /dev/null
+++ b/src/engine/SCons/Tool/c++.py
@@ -0,0 +1,99 @@
+"""SCons.Tool.c++
+
+Tool-specific initialization for generic Posix C++ compilers.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/c++.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import SCons.Tool
+import SCons.Defaults
+import SCons.Util
+
+compilers = ['CC', 'c++']
+
+CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm']
+if SCons.Util.case_sensitive_suffixes('.c', '.C'):
+ CXXSuffixes.append('.C')
+
+def iscplusplus(source):
+ if not source:
+ # Source might be None for unusual cases like SConf.
+ return 0
+ for s in source:
+ if s.sources:
+ ext = os.path.splitext(str(s.sources[0]))[1]
+ if ext in CXXSuffixes:
+ return 1
+ return 0
+
+def generate(env):
+ """
+ Add Builders and construction variables for Visual Age C++ compilers
+ to an Environment.
+ """
+ import SCons.Tool
+ import SCons.Tool.cc
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in CXXSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.CXXAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ SCons.Tool.cc.add_common_cc_variables(env)
+
+ env['CXX'] = 'c++'
+ env['CXXFLAGS'] = SCons.Util.CLVar('')
+ env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
+ env['SHCXX'] = '$CXX'
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
+ env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
+
+ env['CPPDEFPREFIX'] = '-D'
+ env['CPPDEFSUFFIX'] = ''
+ env['INCPREFIX'] = '-I'
+ env['INCSUFFIX'] = ''
+ env['SHOBJSUFFIX'] = '.os'
+ env['OBJSUFFIX'] = '.o'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0
+
+ env['CXXFILESUFFIX'] = '.cc'
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/c++.xml b/src/engine/SCons/Tool/c++.xml
new file mode 100644
index 0000000..d9dfd57
--- /dev/null
+++ b/src/engine/SCons/Tool/c++.xml
@@ -0,0 +1,102 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="cXX">
+<summary>
+Sets construction variables for generic POSIX C++ compilers.
+</summary>
+<sets>
+CXX
+CXXFLAGS
+CXXCOM
+SHCXX
+SHCXXFLAGS
+SHCXXCOM
+CPPDEFPREFIX
+CPPDEFSUFFIX
+INCPREFIX
+INCSUFFIX
+SHOBJSUFFIX
+OBJSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+CXXFILESUFFIX
+</sets>
+<uses>
+CXXCOMSTR
+</uses>
+</tool>
+
+<cvar name="CXX">
+<summary>
+The C++ compiler.
+</summary>
+</cvar>
+
+<cvar name="CXXCOM">
+<summary>
+The command line used to compile a C++ source file to an object file.
+Any options specified in the &cv-link-CXXFLAGS; and
+&cv-link-CPPFLAGS; construction variables
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="CXXCOMSTR">
+<summary>
+The string displayed when a C++ source file
+is compiled to a (static) object file.
+If this is not set, then &cv-link-CXXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(CXXCOMSTR = "Compiling static object $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="CXXFLAGS">
+<summary>
+General options that are passed to the C++ compiler.
+By default, this includes the value of &cv-link-CCFLAGS;,
+so that setting &cv-CCFLAGS; affects both C and C++ compilation.
+If you want to add C++-specific flags,
+you must set or override the value of &cv-link-CXXFLAGS;.
+</summary>
+</cvar>
+
+<cvar name="SHCXX">
+<summary>
+The C++ compiler used for generating shared-library objects.
+</summary>
+</cvar>
+
+<cvar name="SHCXXCOM">
+<summary>
+The command line used to compile a C++ source file
+to a shared-library object file.
+Any options specified in the &cv-link-SHCXXFLAGS; and
+&cv-link-CPPFLAGS; construction variables
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="SHCXXCOMSTR">
+<summary>
+The string displayed when a C++ source file
+is compiled to a shared object file.
+If this is not set, then &cv-link-SHCXXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="SHCXXFLAGS">
+<summary>
+Options that are passed to the C++ compiler
+to generate shared-library objects.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py
new file mode 100644
index 0000000..0ba3856
--- /dev/null
+++ b/src/engine/SCons/Tool/cc.py
@@ -0,0 +1,114 @@
+"""SCons.Tool.cc
+
+Tool-specific initialization for generic Posix C compilers.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/cc.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Tool
+import SCons.Defaults
+import SCons.Util
+
+CSuffixes = ['.c', '.m']
+if not SCons.Util.case_sensitive_suffixes('.c', '.C'):
+ CSuffixes.append('.C')
+
+def add_common_cc_variables(env):
+ """
+ Add underlying common "C compiler" variables that
+ are used by multiple tools (specifically, c++).
+ """
+ if not env.has_key('_CCCOMCOM'):
+ env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
+ # It's a hack to test for darwin here, but the alternative
+ # of creating an applecc.py to contain this seems overkill.
+ # Maybe someday the Apple platform will require more setup and
+ # this logic will be moved.
+ env['FRAMEWORKS'] = SCons.Util.CLVar('')
+ env['FRAMEWORKPATH'] = SCons.Util.CLVar('')
+ if env['PLATFORM'] == 'darwin':
+ env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH'
+
+ if not env.has_key('CCFLAGS'):
+ env['CCFLAGS'] = SCons.Util.CLVar('')
+
+ if not env.has_key('SHCCFLAGS'):
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+
+def generate(env):
+ """
+ Add Builders and construction variables for C compilers to an Environment.
+ """
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in CSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.CAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+#<<<<<<< .working
+#
+# env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
+# # It's a hack to test for darwin here, but the alternative of creating
+# # an applecc.py to contain this seems overkill. Maybe someday the Apple
+# # platform will require more setup and this logic will be moved.
+# env['FRAMEWORKS'] = SCons.Util.CLVar('')
+# env['FRAMEWORKPATH'] = SCons.Util.CLVar('')
+# if env['PLATFORM'] == 'darwin':
+# env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH'
+#=======
+#>>>>>>> .merge-right.r1907
+
+ add_common_cc_variables(env)
+
+ env['CC'] = 'cc'
+ env['CFLAGS'] = SCons.Util.CLVar('')
+ env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
+ env['SHCC'] = '$CC'
+ env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
+ env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
+
+ env['CPPDEFPREFIX'] = '-D'
+ env['CPPDEFSUFFIX'] = ''
+ env['INCPREFIX'] = '-I'
+ env['INCSUFFIX'] = ''
+ env['SHOBJSUFFIX'] = '.os'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0
+
+ env['CFILESUFFIX'] = '.c'
+
+def exists(env):
+ return env.Detect('cc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/cc.xml b/src/engine/SCons/Tool/cc.xml
new file mode 100644
index 0000000..fbfcb06
--- /dev/null
+++ b/src/engine/SCons/Tool/cc.xml
@@ -0,0 +1,161 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="cc">
+<summary>
+Sets construction variables for generic POSIX C copmilers.
+</summary>
+<sets>
+<!--_CCCOMCOM-->
+FRAMEWORKS
+FRAMEWORKPATH
+CC
+CFLAGS
+CCFLAGS
+CCCOM
+SHCC
+SHCFLAGS
+SHCCFLAGS
+SHCCCOM
+CPPDEFPREFIX
+CPPDEFSUFFIX
+INCPREFIX
+INCSUFFIX
+SHOBJSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+CFILESUFFIX
+</sets>
+<uses>
+PLATFORM
+</uses>
+</tool>
+
+<cvar name="CC">
+<summary>
+The C compiler.
+</summary>
+</cvar>
+
+<cvar name="CCCOM">
+<summary>
+The command line used to compile a C source file to a (static) object
+file. Any options specified in the &cv-link-CFLAGS;, &cv-link-CCFLAGS; and
+&cv-link-CPPFLAGS; construction variables are included on this command
+line.
+</summary>
+</cvar>
+
+<cvar name="CCCOMSTR">
+<summary>
+The string displayed when a C source file
+is compiled to a (static) object file.
+If this is not set, then &cv-link-CCCOM; (the command line) is displayed.
+
+<example>
+env = Environment(CCCOMSTR = "Compiling static object $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="CCFLAGS">
+<summary>
+General options that are passed to the C and C++ compilers.
+</summary>
+</cvar>
+
+<cvar name="CFLAGS">
+<summary>
+General options that are passed to the C compiler (C only; not C++).
+</summary>
+</cvar>
+
+<cvar name="CPPFLAGS">
+<summary>
+User-specified C preprocessor options.
+These will be included in any command that uses the C preprocessor,
+including not just compilation of C and C++ source files
+via the &cv-link-CCCOM;,
+&cv-link-SHCCCOM;,
+&cv-link-CXXCOM; and
+&cv-link-SHCXXCOM; command lines,
+but also the &cv-link-FORTRANPPCOM;,
+&cv-link-SHFORTRANPPCOM;,
+&cv-link-F77PPCOM; and
+&cv-link-SHF77PPCOM; command lines
+used to compile a Fortran source file,
+and the &cv-link-ASPPCOM; command line
+used to assemble an assembly language source file,
+after first running each file through the C preprocessor.
+Note that this variable does
+<emphasis>not</emphasis>
+contain
+<option>-I</option>
+(or similar) include search path options
+that scons generates automatically from &cv-link-CPPPATH;.
+See &cv-link-_CPPINCFLAGS;, below,
+for the variable that expands to those options.
+</summary>
+</cvar>
+
+<cvar name="CPPSUFFIXES">
+<summary>
+The list of suffixes of files that will be scanned
+for C preprocessor implicit dependencies
+(#include lines).
+The default list is:
+
+<example>
+[".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+ ".h", ".H", ".hxx", ".hpp", ".hh",
+ ".F", ".fpp", ".FPP",
+ ".m", ".mm",
+ ".S", ".spp", ".SPP"]
+</example>
+</summary>
+</cvar>
+
+<cvar name="SHCC">
+<summary>
+The C compiler used for generating shared-library objects.
+</summary>
+</cvar>
+
+<cvar name="SHCCCOM">
+<summary>
+The command line used to compile a C source file
+to a shared-library object file.
+Any options specified in the &cv-link-SHCFLAGS;,
+&cv-link-SHCCFLAGS; and
+&cv-link-CPPFLAGS; construction variables
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="SHCCCOMSTR">
+<summary>
+The string displayed when a C source file
+is compiled to a shared object file.
+If this is not set, then &cv-link-SHCCCOM; (the command line) is displayed.
+
+<example>
+env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="SHCCFLAGS">
+<summary>
+Options that are passed to the C and C++ compilers
+to generate shared-library objects.
+</summary>
+</cvar>
+
+<cvar name="SHCFLAGS">
+<summary>
+Options that are passed to the C compiler (only; not C++)
+to generate shared-library objects.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/cvf.py b/src/engine/SCons/Tool/cvf.py
new file mode 100644
index 0000000..1d7332b
--- /dev/null
+++ b/src/engine/SCons/Tool/cvf.py
@@ -0,0 +1,58 @@
+"""engine.SCons.Tool.cvf
+
+Tool-specific initialization for the Compaq Visual Fortran compiler.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/cvf.py 4577 2009/12/27 19:44:43 scons"
+
+import fortran
+
+compilers = ['f90']
+
+def generate(env):
+ """Add Builders and construction variables for compaq visual fortran to an Environment."""
+
+ fortran.generate(env)
+
+ env['FORTRAN'] = 'f90'
+ env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}'
+ env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}'
+ env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}'
+ env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}'
+ env['OBJSUFFIX'] = '.obj'
+ env['FORTRANMODDIR'] = '${TARGET.dir}'
+ env['FORTRANMODDIRPREFIX'] = '/module:'
+ env['FORTRANMODDIRSUFFIX'] = ''
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/cvf.xml b/src/engine/SCons/Tool/cvf.xml
new file mode 100644
index 0000000..6c27e6b
--- /dev/null
+++ b/src/engine/SCons/Tool/cvf.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="cvf">
+<summary>
+Sets construction variables for the Compaq Visual Fortran compiler.
+</summary>
+<sets>
+FORTRAN
+FORTRANCOM
+FORTRANPPCOM
+SHFORTRANCOM
+SHFORTRANPPCOM
+OBJSUFFIX
+FORTRANMODDIR
+FORTRANMODDIRPREFIX
+FORTRANMODDIRSUFFIX
+</sets>
+<uses>
+FORTRANFLAGS
+SHFORTRANFLAGS
+_FORTRANMODFLAG
+_FORTRANINCFLAGS
+CPPFLAGS
+_CPPDEFFLAGS
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/default.py b/src/engine/SCons/Tool/default.py
new file mode 100644
index 0000000..0640d8c
--- /dev/null
+++ b/src/engine/SCons/Tool/default.py
@@ -0,0 +1,50 @@
+"""SCons.Tool.default
+
+Initialization with a default tool list.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/default.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Tool
+
+def generate(env):
+ """Add default tools."""
+ for t in SCons.Tool.tool_list(env['PLATFORM'], env):
+ SCons.Tool.Tool(t)(env)
+
+def exists(env):
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/default.xml b/src/engine/SCons/Tool/default.xml
new file mode 100644
index 0000000..5c36b41
--- /dev/null
+++ b/src/engine/SCons/Tool/default.xml
@@ -0,0 +1,12 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="default">
+<summary>
+Sets variables by calling a default list of Tool modules
+for the platform on which SCons is running.
+</summary>
+</tool>
diff --git a/src/engine/SCons/Tool/dmd.py b/src/engine/SCons/Tool/dmd.py
new file mode 100644
index 0000000..0c74877
--- /dev/null
+++ b/src/engine/SCons/Tool/dmd.py
@@ -0,0 +1,224 @@
+"""SCons.Tool.dmd
+
+Tool-specific initialization for the Digital Mars D compiler.
+(http://digitalmars.com/d)
+
+Coded by Andy Friesen (andy@ikagames.com)
+15 November 2003
+
+There are a number of problems with this script at this point in time.
+The one that irritates me the most is the Windows linker setup. The D
+linker doesn't have a way to add lib paths on the commandline, as far
+as I can see. You have to specify paths relative to the SConscript or
+use absolute paths. To hack around it, add '#/blah'. This will link
+blah.lib from the directory where SConstruct resides.
+
+Compiler variables:
+ DC - The name of the D compiler to use. Defaults to dmd or gdmd,
+ whichever is found.
+ DPATH - List of paths to search for import modules.
+ DVERSIONS - List of version tags to enable when compiling.
+ DDEBUG - List of debug tags to enable when compiling.
+
+Linker related variables:
+ LIBS - List of library files to link in.
+ DLINK - Name of the linker to use. Defaults to dmd or gdmd.
+ DLINKFLAGS - List of linker flags.
+
+Lib tool variables:
+ DLIB - Name of the lib tool to use. Defaults to lib.
+ DLIBFLAGS - List of flags to pass to the lib tool.
+ LIBS - Same as for the linker. (libraries to pull into the .lib)
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/dmd.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import string
+
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Scanner.D
+import SCons.Tool
+
+# Adapted from c++.py
+def isD(source):
+ if not source:
+ return 0
+
+ for s in source:
+ if s.sources:
+ ext = os.path.splitext(str(s.sources[0]))[1]
+ if ext == '.d':
+ return 1
+ return 0
+
+smart_link = {}
+
+smart_lib = {}
+
+def generate(env):
+ global smart_link
+ global smart_lib
+
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ DAction = SCons.Action.Action('$DCOM', '$DCOMSTR')
+
+ static_obj.add_action('.d', DAction)
+ shared_obj.add_action('.d', DAction)
+ static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter)
+
+ dc = env.Detect(['dmd', 'gdmd'])
+ env['DC'] = dc
+ env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES'
+ env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+ env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)'
+ env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)'
+ env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)'
+
+ env['DPATH'] = ['#/']
+ env['DFLAGS'] = []
+ env['DVERSIONS'] = []
+ env['DDEBUG'] = []
+
+ if dc:
+ # Add the path to the standard library.
+ # This is merely for the convenience of the dependency scanner.
+ dmd_path = env.WhereIs(dc)
+ if dmd_path:
+ x = string.rindex(dmd_path, dc)
+ phobosDir = dmd_path[:x] + '/../src/phobos'
+ if os.path.isdir(phobosDir):
+ env.Append(DPATH = [phobosDir])
+
+ env['DINCPREFIX'] = '-I'
+ env['DINCSUFFIX'] = ''
+ env['DVERPREFIX'] = '-version='
+ env['DVERSUFFIX'] = ''
+ env['DDEBUGPREFIX'] = '-debug='
+ env['DDEBUGSUFFIX'] = ''
+ env['DFLAGPREFIX'] = '-'
+ env['DFLAGSUFFIX'] = ''
+ env['DFILESUFFIX'] = '.d'
+
+ # Need to use the Digital Mars linker/lib on windows.
+ # *nix can just use GNU link.
+ if env['PLATFORM'] == 'win32':
+ env['DLINK'] = '$DC'
+ env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS'
+ env['DLIB'] = 'lib'
+ env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS'
+
+ env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+ env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)'
+ env['DLINKFLAGS'] = []
+ env['DLIBLINKPREFIX'] = ''
+ env['DLIBLINKSUFFIX'] = '.lib'
+ env['DLIBFLAGPREFIX'] = '-'
+ env['DLIBFLAGSUFFIX'] = ''
+ env['DLINKFLAGPREFIX'] = '-'
+ env['DLINKFLAGSUFFIX'] = ''
+
+ SCons.Tool.createStaticLibBuilder(env)
+
+ # Basically, we hijack the link and ar builders with our own.
+ # these builders check for the presence of D source, and swap out
+ # the system's defaults for the Digital Mars tools. If there's no D
+ # source, then we silently return the previous settings.
+ linkcom = env.get('LINKCOM')
+ try:
+ env['SMART_LINKCOM'] = smart_link[linkcom]
+ except KeyError:
+ def _smartLink(source, target, env, for_signature,
+ defaultLinker=linkcom):
+ if isD(source):
+ # XXX I'm not sure how to add a $DLINKCOMSTR variable
+ # so that it works with this _smartLink() logic,
+ # and I don't have a D compiler/linker to try it out,
+ # so we'll leave it alone for now.
+ return '$DLINKCOM'
+ else:
+ return defaultLinker
+ env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink
+
+ arcom = env.get('ARCOM')
+ try:
+ env['SMART_ARCOM'] = smart_lib[arcom]
+ except KeyError:
+ def _smartLib(source, target, env, for_signature,
+ defaultLib=arcom):
+ if isD(source):
+ # XXX I'm not sure how to add a $DLIBCOMSTR variable
+ # so that it works with this _smartLib() logic, and
+ # I don't have a D compiler/archiver to try it out,
+ # so we'll leave it alone for now.
+ return '$DLIBCOM'
+ else:
+ return defaultLib
+ env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib
+
+ # It is worth noting that the final space in these strings is
+ # absolutely pivotal. SCons sees these as actions and not generators
+ # if it is not there. (very bad)
+ env['ARCOM'] = '$SMART_ARCOM '
+ env['LINKCOM'] = '$SMART_LINKCOM '
+ else: # assuming linux
+ linkcom = env.get('LINKCOM')
+ try:
+ env['SMART_LINKCOM'] = smart_link[linkcom]
+ except KeyError:
+ def _smartLink(source, target, env, for_signature,
+ defaultLinker=linkcom, dc=dc):
+ if isD(source):
+ try:
+ libs = env['LIBS']
+ except KeyError:
+ libs = []
+ if 'phobos' not in libs and 'gphobos' not in libs:
+ if dc is 'dmd':
+ env.Append(LIBS = ['phobos'])
+ elif dc is 'gdmd':
+ env.Append(LIBS = ['gphobos'])
+ if 'pthread' not in libs:
+ env.Append(LIBS = ['pthread'])
+ if 'm' not in libs:
+ env.Append(LIBS = ['m'])
+ return defaultLinker
+ env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink
+
+ env['LINKCOM'] = '$SMART_LINKCOM '
+
+def exists(env):
+ return env.Detect(['dmd', 'gdmd'])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/dmd.xml b/src/engine/SCons/Tool/dmd.xml
new file mode 100644
index 0000000..a12b25c
--- /dev/null
+++ b/src/engine/SCons/Tool/dmd.xml
@@ -0,0 +1,53 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="dmd">
+<summary>
+Sets construction variables for D language compilers
+(the Digital Mars D compiler, or GDC).
+</summary>
+<sets>
+<!--
+DC
+DCOM
+_DINCFLAGS
+_DVERFLAGS
+_DDEBUGFLAGS
+_DFLAGS
+DPATH
+DFLAGS
+DVERSIONS
+DDEBUG
+DINCPREFIX
+DINCSUFFIX
+DVERPREFIX
+DVERSUFFIX
+DDEBUGPREFIX
+DDEBUGSUFFIX
+DFLAGPREFIX
+DFLAGSUFFIX
+DFLESUFFIX
+DLINK
+DLINKCOM
+DLIB
+DLIBCOM
+_DLINKLIBFLAGS
+_DLIBFLAGS
+DLINKFLAGS
+DLIBLINKPREFIX
+DLIBLINKSUFFIX
+DLIBFLAGPREFIX
+DLIBFLAGSUFFIX
+DLINKFLAGPREFIX
+DLINKFLAGSUFFIX
+LINKCOM
+ARCOM
+LIBS
+-->
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/dvi.py b/src/engine/SCons/Tool/dvi.py
new file mode 100644
index 0000000..5c859c7
--- /dev/null
+++ b/src/engine/SCons/Tool/dvi.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.dvi
+
+Common DVI Builder definition for various other Tool modules that use it.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/dvi.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Builder
+import SCons.Tool
+
+DVIBuilder = None
+
+def generate(env):
+ try:
+ env['BUILDERS']['DVI']
+ except KeyError:
+ global DVIBuilder
+
+ if DVIBuilder is None:
+ # The suffix is hard-coded to '.dvi', not configurable via a
+ # construction variable like $DVISUFFIX, because the output
+ # file name is hard-coded within TeX.
+ DVIBuilder = SCons.Builder.Builder(action = {},
+ source_scanner = SCons.Tool.LaTeXScanner,
+ suffix = '.dvi',
+ emitter = {},
+ source_ext_match = None)
+
+ env['BUILDERS']['DVI'] = DVIBuilder
+
+def exists(env):
+ # This only puts a skeleton Builder in place, so if someone
+ # references this Tool directly, it's always "available."
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/dvi.xml b/src/engine/SCons/Tool/dvi.xml
new file mode 100644
index 0000000..b0a871f
--- /dev/null
+++ b/src/engine/SCons/Tool/dvi.xml
@@ -0,0 +1,67 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="dvi">
+<summary>
+Attaches the &b-DVI; builder to the
+construction environment.
+</summary>
+<sets>
+</sets>
+<uses>
+</uses>
+</tool>
+
+<builder name="DVI">
+<summary>
+Builds a <filename>.dvi</filename> file
+from a <filename>.tex</filename>,
+<filename>.ltx</filename> or <filename>.latex</filename> input file.
+If the source file suffix is <filename>.tex</filename>,
+&scons;
+will examine the contents of the file;
+if the string
+<literal>\documentclass</literal>
+or
+<literal>\documentstyle</literal>
+is found, the file is assumed to be a LaTeX file and
+the target is built by invoking the &cv-link-LATEXCOM; command line;
+otherwise, the &cv-link-TEXCOM; command line is used.
+If the file is a LaTeX file,
+the
+&b-DVI;
+builder method will also examine the contents
+of the
+<filename>.aux</filename>
+file and invoke the &cv-link-BIBTEX; command line
+if the string
+<literal>bibdata</literal>
+is found,
+start &cv-link-MAKEINDEX; to generate an index if a
+<filename>.ind</filename>
+file is found
+and will examine the contents
+<filename>.log</filename>
+file and re-run the &cv-link-LATEXCOM; command
+if the log file says it is necessary.
+
+The suffix <filename>.dvi</filename>
+(hard-coded within TeX itself)
+is automatically added to the target
+if it is not already present.
+Examples:
+
+<example>
+# builds from aaa.tex
+env.DVI(target = 'aaa.dvi', source = 'aaa.tex')
+# builds bbb.dvi
+env.DVI(target = 'bbb', source = 'bbb.ltx')
+# builds from ccc.latex
+env.DVI(target = 'ccc.dvi', source = 'ccc.latex')
+</example>
+</summary>
+</builder>
+
diff --git a/src/engine/SCons/Tool/dvipdf.py b/src/engine/SCons/Tool/dvipdf.py
new file mode 100644
index 0000000..67137a8
--- /dev/null
+++ b/src/engine/SCons/Tool/dvipdf.py
@@ -0,0 +1,125 @@
+"""SCons.Tool.dvipdf
+
+Tool-specific initialization for dvipdf.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/dvipdf.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Tool.pdf
+import SCons.Tool.tex
+import SCons.Util
+
+_null = SCons.Scanner.LaTeX._null
+
+def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None):
+ """A builder for DVI files that sets the TEXPICTS environment
+ variable before running dvi2ps or dvipdf."""
+
+ try:
+ abspath = source[0].attributes.path
+ except AttributeError :
+ abspath = ''
+
+ saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath)
+
+ result = XXXDviAction(target, source, env)
+
+ if saved_env is _null:
+ try:
+ del env['ENV']['TEXPICTS']
+ except KeyError:
+ pass # was never set
+ else:
+ env['ENV']['TEXPICTS'] = saved_env
+
+ return result
+
+def DviPdfFunction(target = None, source= None, env=None):
+ result = DviPdfPsFunction(PDFAction,target,source,env)
+ return result
+
+def DviPdfStrFunction(target = None, source= None, env=None):
+ """A strfunction for dvipdf that returns the appropriate
+ command string for the no_exec options."""
+ if env.GetOption("no_exec"):
+ result = env.subst('$DVIPDFCOM',0,target,source)
+ else:
+ result = ''
+ return result
+
+PDFAction = None
+DVIPDFAction = None
+
+def PDFEmitter(target, source, env):
+ """Strips any .aux or .log files from the input source list.
+ These are created by the TeX Builder that in all likelihood was
+ used to generate the .dvi file we're using as input, and we only
+ care about the .dvi file.
+ """
+ def strip_suffixes(n):
+ return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log']
+ source = filter(strip_suffixes, source)
+ return (target, source)
+
+def generate(env):
+ """Add Builders and construction variables for dvipdf to an Environment."""
+ global PDFAction
+ if PDFAction is None:
+ PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR')
+
+ global DVIPDFAction
+ if DVIPDFAction is None:
+ DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction)
+
+ import pdf
+ pdf.generate(env)
+
+ bld = env['BUILDERS']['PDF']
+ bld.add_action('.dvi', DVIPDFAction)
+ bld.add_emitter('.dvi', PDFEmitter)
+
+ env['DVIPDF'] = 'dvipdf'
+ env['DVIPDFFLAGS'] = SCons.Util.CLVar('')
+ env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}'
+
+ # Deprecated synonym.
+ env['PDFCOM'] = ['$DVIPDFCOM']
+
+def exists(env):
+ return env.Detect('dvipdf')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/dvipdf.xml b/src/engine/SCons/Tool/dvipdf.xml
new file mode 100644
index 0000000..c3c2a36
--- /dev/null
+++ b/src/engine/SCons/Tool/dvipdf.xml
@@ -0,0 +1,51 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="dvipdf">
+<summary>
+Sets construction variables for the dvipdf utility.
+</summary>
+<sets>
+DVIPDF
+DVIPDFFLAGS
+DVIPDFCOM
+</sets>
+<uses>
+DVIPDFCOMSTR
+</uses>
+</tool>
+
+<cvar name="DVIPDF">
+<summary>
+The TeX DVI file to PDF file converter.
+</summary>
+</cvar>
+
+<cvar name="DVIPDFFLAGS">
+<summary>
+General options passed to the TeX DVI file to PDF file converter.
+</summary>
+</cvar>
+
+<cvar name="DVIPDFCOM">
+<summary>
+The command line used to convert TeX DVI files into a PDF file.
+</summary>
+</cvar>
+
+<cvar name="DVIPDFCOMSTR">
+<summary>
+The string displayed when a TeX DVI file
+is converted into a PDF file.
+If this is not set, then &cv-link-DVIPDFCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="PDFCOM">
+<summary>
+A deprecated synonym for &cv-link-DVIPDFCOM;.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/dvips.py b/src/engine/SCons/Tool/dvips.py
new file mode 100644
index 0000000..da61891
--- /dev/null
+++ b/src/engine/SCons/Tool/dvips.py
@@ -0,0 +1,94 @@
+"""SCons.Tool.dvips
+
+Tool-specific initialization for dvips.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/dvips.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Tool.dvipdf
+import SCons.Util
+
+def DviPsFunction(target = None, source= None, env=None):
+ result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env)
+ return result
+
+def DviPsStrFunction(target = None, source= None, env=None):
+ """A strfunction for dvipdf that returns the appropriate
+ command string for the no_exec options."""
+ if env.GetOption("no_exec"):
+ result = env.subst('$PSCOM',0,target,source)
+ else:
+ result = ''
+ return result
+
+PSAction = None
+DVIPSAction = None
+PSBuilder = None
+
+def generate(env):
+ """Add Builders and construction variables for dvips to an Environment."""
+ global PSAction
+ if PSAction is None:
+ PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR')
+
+ global DVIPSAction
+ if DVIPSAction is None:
+ DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction)
+
+ global PSBuilder
+ if PSBuilder is None:
+ PSBuilder = SCons.Builder.Builder(action = PSAction,
+ prefix = '$PSPREFIX',
+ suffix = '$PSSUFFIX',
+ src_suffix = '.dvi',
+ src_builder = 'DVI',
+ single_source=True)
+
+ env['BUILDERS']['PostScript'] = PSBuilder
+
+ env['DVIPS'] = 'dvips'
+ env['DVIPSFLAGS'] = SCons.Util.CLVar('')
+ # I'm not quite sure I got the directories and filenames right for variant_dir
+ # We need to be in the correct directory for the sake of latex \includegraphics eps included files.
+ env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}'
+ env['PSPREFIX'] = ''
+ env['PSSUFFIX'] = '.ps'
+
+def exists(env):
+ return env.Detect('dvips')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/dvips.xml b/src/engine/SCons/Tool/dvips.xml
new file mode 100644
index 0000000..b7ead77
--- /dev/null
+++ b/src/engine/SCons/Tool/dvips.xml
@@ -0,0 +1,81 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="dvips">
+<summary>
+Sets construction variables for the dvips utility.
+</summary>
+<sets>
+DVIPS
+DVIPSFLAGS
+PSCOM
+PSPREFIX
+PSSUFFIX
+</sets>
+<uses>
+PSCOMSTR
+</uses>
+</tool>
+
+<builder name="PostScript">
+<summary>
+Builds a <filename>.ps</filename> file
+from a <filename>.dvi</filename> input file
+(or, by extension, a <filename>.tex</filename>,
+<filename>.ltx</filename>,
+or
+<filename>.latex</filename> input file).
+The suffix specified by the &cv-link-PSSUFFIX; construction variable
+(<filename>.ps</filename> by default)
+is added automatically to the target
+if it is not already present. Example:
+
+<example>
+# builds from aaa.tex
+env.PostScript(target = 'aaa.ps', source = 'aaa.tex')
+# builds bbb.ps from bbb.dvi
+env.PostScript(target = 'bbb', source = 'bbb.dvi')
+</example>
+</summary>
+</builder>
+
+<cvar name="DVIPS">
+<summary>
+The TeX DVI file to PostScript converter.
+</summary>
+</cvar>
+
+<cvar name="DVIPSFLAGS">
+<summary>
+General options passed to the TeX DVI file to PostScript converter.
+</summary>
+</cvar>
+
+<cvar name="PSCOM">
+<summary>
+The command line used to convert TeX DVI files into a PostScript file.
+</summary>
+</cvar>
+
+<cvar name="PSCOMSTR">
+<summary>
+The string displayed when a TeX DVI file
+is converted into a PostScript file.
+If this is not set, then &cv-link-PSCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="PSPREFIX">
+<summary>
+The prefix used for PostScript file names.
+</summary>
+</cvar>
+
+<cvar name="PSSUFFIX">
+<summary>
+The prefix used for PostScript file names.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/f77.py b/src/engine/SCons/Tool/f77.py
new file mode 100644
index 0000000..3fe3c3f
--- /dev/null
+++ b/src/engine/SCons/Tool/f77.py
@@ -0,0 +1,62 @@
+"""engine.SCons.Tool.f77
+
+Tool-specific initialization for the generic Posix f77 Fortran compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/f77.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Scanner.Fortran
+import SCons.Tool
+import SCons.Util
+from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env
+
+compilers = ['f77']
+
+def generate(env):
+ add_all_to_env(env)
+ add_f77_to_env(env)
+
+ fcomp = env.Detect(compilers) or 'f77'
+ env['F77'] = fcomp
+ env['SHF77'] = fcomp
+
+ env['FORTRAN'] = fcomp
+ env['SHFORTRAN'] = fcomp
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/f77.xml b/src/engine/SCons/Tool/f77.xml
new file mode 100644
index 0000000..c947905
--- /dev/null
+++ b/src/engine/SCons/Tool/f77.xml
@@ -0,0 +1,265 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="f77">
+<summary>
+Set construction variables for generic POSIX Fortran 77 compilers.
+</summary>
+<sets>
+F77
+F77FLAGS
+F77COM
+F77PPCOM
+F77FILESUFFIXES
+F77PPFILESUFFIXES
+FORTRAN
+FORTRANFLAGS
+FORTRANCOM
+SHF77
+SHF77FLAGS
+SHF77COM
+SHF77PPCOM
+SHFORTRAN
+SHFORTRANFLAGS
+SHFORTRANCOM
+SHFORTRANPPCOM
+_F77INCFLAGS
+</sets>
+<uses>
+F77COMSTR
+F77PPCOMSTR
+FORTRANCOMSTR
+FORTRANPPCOMSTR
+SHF77COMSTR
+SHF77PPCOMSTR
+SHFORTRANCOMSTR
+SHFORTRANPPCOMSTR
+</uses>
+</tool>
+
+<cvar name="F77">
+<summary>
+The Fortran 77 compiler.
+You should normally set the &cv-link-FORTRAN; variable,
+which specifies the default Fortran compiler
+for all Fortran versions.
+You only need to set &cv-link-F77; if you need to use a specific compiler
+or compiler version for Fortran 77 files.
+</summary>
+</cvar>
+
+<cvar name="F77COM">
+<summary>
+The command line used to compile a Fortran 77 source file to an object file.
+You only need to set &cv-link-F77COM; if you need to use a specific
+command line for Fortran 77 files.
+You should normally set the &cv-link-FORTRANCOM; variable,
+which specifies the default command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="F77FILESUFFIXES">
+<summary>
+The list of file extensions for which the F77 dialect will be used. By
+default, this is ['.f77']
+</summary>
+</cvar>
+
+<cvar name="F77PPFILESUFFIXES">
+<summary>
+The list of file extensions for which the compilation + preprocessor pass for
+F77 dialect will be used. By default, this is empty
+</summary>
+</cvar>
+
+<cvar name="F77COMSTR">
+<summary>
+The string displayed when a Fortran 77 source file
+is compiled to an object file.
+If this is not set, then &cv-link-F77COM; or &cv-link-FORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="F77FLAGS">
+<summary>
+General user-specified options that are passed to the Fortran 77 compiler.
+Note that this variable does
+<emphasis>not</emphasis>
+contain
+<option>-I</option>
+(or similar) include search path options
+that scons generates automatically from &cv-link-F77PATH;.
+See
+&cv-link-_F77INCFLAGS;
+below,
+for the variable that expands to those options.
+You only need to set &cv-link-F77FLAGS; if you need to define specific
+user options for Fortran 77 files.
+You should normally set the &cv-link-FORTRANFLAGS; variable,
+which specifies the user-specified options
+passed to the default Fortran compiler
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="_F77INCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the Fortran 77 compiler command-line options
+for specifying directories to be searched for include files.
+The value of &cv-link-_F77INCFLAGS; is created
+by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+to the beginning and end
+of each directory in &cv-link-F77PATH;.
+</summary>
+</cvar>
+
+<cvar name="F77PATH">
+<summary>
+The list of directories that the Fortran 77 compiler will search for include
+directories. The implicit dependency scanner will search these
+directories for include files. Don't explicitly put include directory
+arguments in &cv-link-F77FLAGS; because the result will be non-portable
+and the directories will not be searched by the dependency scanner. Note:
+directory names in &cv-link-F77PATH; will be looked-up relative to the SConscript
+directory when they are used in a command. To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+You only need to set &cv-link-F77PATH; if you need to define a specific
+include path for Fortran 77 files.
+You should normally set the &cv-link-FORTRANPATH; variable,
+which specifies the include path
+for the default Fortran compiler
+for all Fortran versions.
+
+<example>
+env = Environment(F77PATH='#/include')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+include = Dir('include')
+env = Environment(F77PATH=include)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-link-_F77INCFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-link-F77PATH;.
+Any command lines you define that need
+the F77PATH directory list should
+include &cv-link-_F77INCFLAGS;:
+
+<example>
+env = Environment(F77COM="my_compiler $_F77INCFLAGS -c -o $TARGET $SOURCE")
+</example>
+</summary>
+</cvar>
+
+<cvar name="F77PPCOM">
+<summary>
+The command line used to compile a Fortran 77 source file to an object file
+after first running the file through the C preprocessor.
+Any options specified in the &cv-link-F77FLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+You only need to set &cv-link-F77PPCOM; if you need to use a specific
+C-preprocessor command line for Fortran 77 files.
+You should normally set the &cv-link-FORTRANPPCOM; variable,
+which specifies the default C-preprocessor command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="F77PPCOMSTR">
+<summary>
+The string displayed when a Fortran 77 source file
+is compiled to an object file
+after first running the file through the C preprocessor.
+If this is not set, then &cv-link-F77PPCOM; or &cv-link-FORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHF77">
+<summary>
+The Fortran 77 compiler used for generating shared-library objects.
+You should normally set the &cv-link-SHFORTRAN; variable,
+which specifies the default Fortran compiler
+for all Fortran versions.
+You only need to set &cv-link-SHF77; if you need to use a specific compiler
+or compiler version for Fortran 77 files.
+</summary>
+</cvar>
+
+<cvar name="SHF77COM">
+<summary>
+The command line used to compile a Fortran 77 source file
+to a shared-library object file.
+You only need to set &cv-link-SHF77COM; if you need to use a specific
+command line for Fortran 77 files.
+You should normally set the &cv-link-SHFORTRANCOM; variable,
+which specifies the default command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF77COMSTR">
+<summary>
+The string displayed when a Fortran 77 source file
+is compiled to a shared-library object file.
+If this is not set, then &cv-link-SHF77COM; or &cv-link-SHFORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHF77FLAGS">
+<summary>
+Options that are passed to the Fortran 77 compiler
+to generated shared-library objects.
+You only need to set &cv-link-SHF77FLAGS; if you need to define specific
+user options for Fortran 77 files.
+You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+which specifies the user-specified options
+passed to the default Fortran compiler
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF77PPCOM">
+<summary>
+The command line used to compile a Fortran 77 source file to a
+shared-library object file
+after first running the file through the C preprocessor.
+Any options specified in the &cv-link-SHF77FLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+You only need to set &cv-link-SHF77PPCOM; if you need to use a specific
+C-preprocessor command line for Fortran 77 files.
+You should normally set the &cv-link-SHFORTRANPPCOM; variable,
+which specifies the default C-preprocessor command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF77PPCOMSTR">
+<summary>
+The string displayed when a Fortran 77 source file
+is compiled to a shared-library object file
+after first running the file through the C preprocessor.
+If this is not set, then &cv-link-SHF77PPCOM; or &cv-link-SHFORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/f90.py b/src/engine/SCons/Tool/f90.py
new file mode 100644
index 0000000..b53014e
--- /dev/null
+++ b/src/engine/SCons/Tool/f90.py
@@ -0,0 +1,62 @@
+"""engine.SCons.Tool.f90
+
+Tool-specific initialization for the generic Posix f90 Fortran compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/f90.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Scanner.Fortran
+import SCons.Tool
+import SCons.Util
+from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env
+
+compilers = ['f90']
+
+def generate(env):
+ add_all_to_env(env)
+ add_f90_to_env(env)
+
+ fc = env.Detect(compilers) or 'f90'
+ env['F90'] = fc
+ env['SHF90'] = fc
+
+ env['FORTRAN'] = fc
+ env['SHFORTRAN'] = fc
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/f90.xml b/src/engine/SCons/Tool/f90.xml
new file mode 100644
index 0000000..665333d
--- /dev/null
+++ b/src/engine/SCons/Tool/f90.xml
@@ -0,0 +1,251 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="f90">
+<summary>
+Set construction variables for generic POSIX Fortran 90 compilers.
+</summary>
+<sets>
+F90
+F90FLAGS
+F90COM
+F90PPCOM
+SHF90
+SHF90FLAGS
+SHF90COM
+SHF90PPCOM
+_F90INCFLAGS
+</sets>
+<uses>
+F90COMSTR
+F90PPCOMSTR
+SHF90COMSTR
+SHF90PPCOMSTR
+</uses>
+</tool>
+
+<cvar name="F90">
+<summary>
+The Fortran 90 compiler.
+You should normally set the &cv-link-FORTRAN; variable,
+which specifies the default Fortran compiler
+for all Fortran versions.
+You only need to set &cv-link-F90; if you need to use a specific compiler
+or compiler version for Fortran 90 files.
+</summary>
+</cvar>
+
+<cvar name="F90COM">
+<summary>
+The command line used to compile a Fortran 90 source file to an object file.
+You only need to set &cv-link-F90COM; if you need to use a specific
+command line for Fortran 90 files.
+You should normally set the &cv-link-FORTRANCOM; variable,
+which specifies the default command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="F90COMSTR">
+<summary>
+The string displayed when a Fortran 90 source file
+is compiled to an object file.
+If this is not set, then &cv-link-F90COM; or &cv-link-FORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="F90FILESUFFIXES">
+<summary>
+The list of file extensions for which the F90 dialect will be used. By
+default, this is ['.f90']
+</summary>
+</cvar>
+
+<cvar name="F90PPFILESUFFIXES">
+<summary>
+The list of file extensions for which the compilation + preprocessor pass for
+F90 dialect will be used. By default, this is empty
+</summary>
+</cvar>
+
+<cvar name="F90FLAGS">
+<summary>
+General user-specified options that are passed to the Fortran 90 compiler.
+Note that this variable does
+<emphasis>not</emphasis>
+contain
+<option>-I</option>
+(or similar) include search path options
+that scons generates automatically from &cv-link-F90PATH;.
+See
+&cv-link-_F90INCFLAGS;
+below,
+for the variable that expands to those options.
+You only need to set &cv-link-F90FLAGS; if you need to define specific
+user options for Fortran 90 files.
+You should normally set the &cv-link-FORTRANFLAGS; variable,
+which specifies the user-specified options
+passed to the default Fortran compiler
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="_F90INCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the Fortran 90 compiler command-line options
+for specifying directories to be searched for include files.
+The value of &cv-link-_F90INCFLAGS; is created
+by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+to the beginning and end
+of each directory in &cv-link-F90PATH;.
+</summary>
+</cvar>
+
+<cvar name="F90PATH">
+<summary>
+The list of directories that the Fortran 90 compiler will search for include
+directories. The implicit dependency scanner will search these
+directories for include files. Don't explicitly put include directory
+arguments in &cv-link-F90FLAGS; because the result will be non-portable
+and the directories will not be searched by the dependency scanner. Note:
+directory names in &cv-link-F90PATH; will be looked-up relative to the SConscript
+directory when they are used in a command. To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+You only need to set &cv-link-F90PATH; if you need to define a specific
+include path for Fortran 90 files.
+You should normally set the &cv-link-FORTRANPATH; variable,
+which specifies the include path
+for the default Fortran compiler
+for all Fortran versions.
+
+<example>
+env = Environment(F90PATH='#/include')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+include = Dir('include')
+env = Environment(F90PATH=include)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-link-_F90INCFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-link-F90PATH;.
+Any command lines you define that need
+the F90PATH directory list should
+include &cv-link-_F90INCFLAGS;:
+
+<example>
+env = Environment(F90COM="my_compiler $_F90INCFLAGS -c -o $TARGET $SOURCE")
+</example>
+</summary>
+</cvar>
+
+<cvar name="F90PPCOM">
+<summary>
+The command line used to compile a Fortran 90 source file to an object file
+after first running the file through the C preprocessor.
+Any options specified in the &cv-link-F90FLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+You only need to set &cv-link-F90PPCOM; if you need to use a specific
+C-preprocessor command line for Fortran 90 files.
+You should normally set the &cv-link-FORTRANPPCOM; variable,
+which specifies the default C-preprocessor command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="F90PPCOMSTR">
+<summary>
+The string displayed when a Fortran 90 source file
+is compiled after first running the file through the C preprocessor.
+If this is not set, then &cv-link-F90PPCOM; or &cv-link-FORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHF90">
+<summary>
+The Fortran 90 compiler used for generating shared-library objects.
+You should normally set the &cv-link-SHFORTRAN; variable,
+which specifies the default Fortran compiler
+for all Fortran versions.
+You only need to set &cv-link-SHF90; if you need to use a specific compiler
+or compiler version for Fortran 90 files.
+</summary>
+</cvar>
+
+<cvar name="SHF90COM">
+<summary>
+The command line used to compile a Fortran 90 source file
+to a shared-library object file.
+You only need to set &cv-link-SHF90COM; if you need to use a specific
+command line for Fortran 90 files.
+You should normally set the &cv-link-SHFORTRANCOM; variable,
+which specifies the default command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF90COMSTR">
+<summary>
+The string displayed when a Fortran 90 source file
+is compiled to a shared-library object file.
+If this is not set, then &cv-link-SHF90COM; or &cv-link-SHFORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHF90FLAGS">
+<summary>
+Options that are passed to the Fortran 90 compiler
+to generated shared-library objects.
+You only need to set &cv-link-SHF90FLAGS; if you need to define specific
+user options for Fortran 90 files.
+You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+which specifies the user-specified options
+passed to the default Fortran compiler
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF90PPCOM">
+<summary>
+The command line used to compile a Fortran 90 source file to a
+shared-library object file
+after first running the file through the C preprocessor.
+Any options specified in the &cv-link-SHF90FLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+You only need to set &cv-link-SHF90PPCOM; if you need to use a specific
+C-preprocessor command line for Fortran 90 files.
+You should normally set the &cv-link-SHFORTRANPPCOM; variable,
+which specifies the default C-preprocessor command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF90PPCOMSTR">
+<summary>
+The string displayed when a Fortran 90 source file
+is compiled to a shared-library object file
+after first running the file through the C preprocessor.
+If this is not set, then &cv-link-SHF90PPCOM; or &cv-link-SHFORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/f95.py b/src/engine/SCons/Tool/f95.py
new file mode 100644
index 0000000..72e8443
--- /dev/null
+++ b/src/engine/SCons/Tool/f95.py
@@ -0,0 +1,63 @@
+"""engine.SCons.Tool.f95
+
+Tool-specific initialization for the generic Posix f95 Fortran compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/f95.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+import fortran
+from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env
+
+compilers = ['f95']
+
+def generate(env):
+ add_all_to_env(env)
+ add_f95_to_env(env)
+
+ fcomp = env.Detect(compilers) or 'f95'
+ env['F95'] = fcomp
+ env['SHF95'] = fcomp
+
+ env['FORTRAN'] = fcomp
+ env['SHFORTRAN'] = fcomp
+
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/f95.xml b/src/engine/SCons/Tool/f95.xml
new file mode 100644
index 0000000..2418cb9
--- /dev/null
+++ b/src/engine/SCons/Tool/f95.xml
@@ -0,0 +1,252 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="f95">
+<summary>
+Set construction variables for generic POSIX Fortran 95 compilers.
+</summary>
+<sets>
+F95
+F95FLAGS
+F95COM
+F95PPCOM
+SHF95
+SHF95FLAGS
+SHF95COM
+SHF95PPCOM
+_F95INCFLAGS
+</sets>
+<uses>
+F95COMSTR
+F95PPCOMSTR
+SHF95COMSTR
+SHF95PPCOMSTR
+</uses>
+</tool>
+
+<cvar name="F95">
+<summary>
+The Fortran 95 compiler.
+You should normally set the &cv-link-FORTRAN; variable,
+which specifies the default Fortran compiler
+for all Fortran versions.
+You only need to set &cv-link-F95; if you need to use a specific compiler
+or compiler version for Fortran 95 files.
+</summary>
+</cvar>
+
+<cvar name="F95COM">
+<summary>
+The command line used to compile a Fortran 95 source file to an object file.
+You only need to set &cv-link-F95COM; if you need to use a specific
+command line for Fortran 95 files.
+You should normally set the &cv-link-FORTRANCOM; variable,
+which specifies the default command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="F95COMSTR">
+<summary>
+The string displayed when a Fortran 95 source file
+is compiled to an object file.
+If this is not set, then &cv-link-F95COM; or &cv-link-FORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="F95FILESUFFIXES">
+<summary>
+The list of file extensions for which the F95 dialect will be used. By
+default, this is ['.f95']
+</summary>
+</cvar>
+
+<cvar name="F95PPFILESUFFIXES">
+<summary>
+The list of file extensions for which the compilation + preprocessor pass for
+F95 dialect will be used. By default, this is empty
+</summary>
+</cvar>
+
+<cvar name="F95FLAGS">
+<summary>
+General user-specified options that are passed to the Fortran 95 compiler.
+Note that this variable does
+<emphasis>not</emphasis>
+contain
+<option>-I</option>
+(or similar) include search path options
+that scons generates automatically from &cv-link-F95PATH;.
+See
+&cv-link-_F95INCFLAGS;
+below,
+for the variable that expands to those options.
+You only need to set &cv-link-F95FLAGS; if you need to define specific
+user options for Fortran 95 files.
+You should normally set the &cv-link-FORTRANFLAGS; variable,
+which specifies the user-specified options
+passed to the default Fortran compiler
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="_F95INCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the Fortran 95 compiler command-line options
+for specifying directories to be searched for include files.
+The value of &cv-link-_F95INCFLAGS; is created
+by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+to the beginning and end
+of each directory in &cv-link-F95PATH;.
+</summary>
+</cvar>
+
+<cvar name="F95PATH">
+<summary>
+The list of directories that the Fortran 95 compiler will search for include
+directories. The implicit dependency scanner will search these
+directories for include files. Don't explicitly put include directory
+arguments in &cv-link-F95FLAGS; because the result will be non-portable
+and the directories will not be searched by the dependency scanner. Note:
+directory names in &cv-link-F95PATH; will be looked-up relative to the SConscript
+directory when they are used in a command. To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+You only need to set &cv-link-F95PATH; if you need to define a specific
+include path for Fortran 95 files.
+You should normally set the &cv-link-FORTRANPATH; variable,
+which specifies the include path
+for the default Fortran compiler
+for all Fortran versions.
+
+<example>
+env = Environment(F95PATH='#/include')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+include = Dir('include')
+env = Environment(F95PATH=include)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-link-_F95INCFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-link-F95PATH;.
+Any command lines you define that need
+the F95PATH directory list should
+include &cv-link-_F95INCFLAGS;:
+
+<example>
+env = Environment(F95COM="my_compiler $_F95INCFLAGS -c -o $TARGET $SOURCE")
+</example>
+</summary>
+</cvar>
+
+<cvar name="F95PPCOM">
+<summary>
+The command line used to compile a Fortran 95 source file to an object file
+after first running the file through the C preprocessor.
+Any options specified in the &cv-link-F95FLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+You only need to set &cv-link-F95PPCOM; if you need to use a specific
+C-preprocessor command line for Fortran 95 files.
+You should normally set the &cv-link-FORTRANPPCOM; variable,
+which specifies the default C-preprocessor command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="F95PPCOMSTR">
+<summary>
+The string displayed when a Fortran 95 source file
+is compiled to an object file
+after first running the file through the C preprocessor.
+If this is not set, then &cv-link-F95PPCOM; or &cv-link-FORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHF95">
+<summary>
+The Fortran 95 compiler used for generating shared-library objects.
+You should normally set the &cv-link-SHFORTRAN; variable,
+which specifies the default Fortran compiler
+for all Fortran versions.
+You only need to set &cv-link-SHF95; if you need to use a specific compiler
+or compiler version for Fortran 95 files.
+</summary>
+</cvar>
+
+<cvar name="SHF95COM">
+<summary>
+The command line used to compile a Fortran 95 source file
+to a shared-library object file.
+You only need to set &cv-link-SHF95COM; if you need to use a specific
+command line for Fortran 95 files.
+You should normally set the &cv-link-SHFORTRANCOM; variable,
+which specifies the default command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF95COMSTR">
+<summary>
+The string displayed when a Fortran 95 source file
+is compiled to a shared-library object file.
+If this is not set, then &cv-link-SHF95COM; or &cv-link-SHFORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHF95FLAGS">
+<summary>
+Options that are passed to the Fortran 95 compiler
+to generated shared-library objects.
+You only need to set &cv-link-SHF95FLAGS; if you need to define specific
+user options for Fortran 95 files.
+You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+which specifies the user-specified options
+passed to the default Fortran compiler
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF95PPCOM">
+<summary>
+The command line used to compile a Fortran 95 source file to a
+shared-library object file
+after first running the file through the C preprocessor.
+Any options specified in the &cv-link-SHF95FLAGS; and &cv-link-CPPFLAGS; construction variables
+are included on this command line.
+You only need to set &cv-link-SHF95PPCOM; if you need to use a specific
+C-preprocessor command line for Fortran 95 files.
+You should normally set the &cv-link-SHFORTRANPPCOM; variable,
+which specifies the default C-preprocessor command line
+for all Fortran versions.
+</summary>
+</cvar>
+
+<cvar name="SHF95PPCOMSTR">
+<summary>
+The string displayed when a Fortran 95 source file
+is compiled to a shared-library object file
+after first running the file through the C preprocessor.
+If this is not set, then &cv-link-SHF95PPCOM; or &cv-link-SHFORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/filesystem.py b/src/engine/SCons/Tool/filesystem.py
new file mode 100644
index 0000000..bd4c803
--- /dev/null
+++ b/src/engine/SCons/Tool/filesystem.py
@@ -0,0 +1,98 @@
+"""SCons.Tool.filesystem
+
+Tool-specific initialization for the filesystem tools.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/filesystem.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons
+from SCons.Tool.install import copyFunc
+
+copyToBuilder, copyAsBuilder = None, None
+
+def copyto_emitter(target, source, env):
+ """ changes the path of the source to be under the target (which
+ are assumed to be directories.
+ """
+ n_target = []
+
+ for t in target:
+ n_target = n_target + map( lambda s, t=t: t.File( str( s ) ), source )
+
+ return (n_target, source)
+
+def copy_action_func(target, source, env):
+ assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(map(str, target),map(str, source))
+
+ for t, s in zip(target, source):
+ if copyFunc(t.get_path(), s.get_path(), env):
+ return 1
+
+ return 0
+
+def copy_action_str(target, source, env):
+ return env.subst_target_source(env['COPYSTR'], 0, target, source)
+
+copy_action = SCons.Action.Action( copy_action_func, copy_action_str )
+
+def generate(env):
+ try:
+ env['BUILDERS']['CopyTo']
+ env['BUILDERS']['CopyAs']
+ except KeyError, e:
+ global copyToBuilder
+ if copyToBuilder is None:
+ copyToBuilder = SCons.Builder.Builder(
+ action = copy_action,
+ target_factory = env.fs.Dir,
+ source_factory = env.fs.Entry,
+ multi = 1,
+ emitter = [ copyto_emitter, ] )
+
+ global copyAsBuilder
+ if copyAsBuilder is None:
+ copyAsBuilder = SCons.Builder.Builder(
+ action = copy_action,
+ target_factory = env.fs.Entry,
+ source_factory = env.fs.Entry )
+
+ env['BUILDERS']['CopyTo'] = copyToBuilder
+ env['BUILDERS']['CopyAs'] = copyAsBuilder
+
+ env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"'
+
+def exists(env):
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/fortran.py b/src/engine/SCons/Tool/fortran.py
new file mode 100644
index 0000000..b88c1a9
--- /dev/null
+++ b/src/engine/SCons/Tool/fortran.py
@@ -0,0 +1,63 @@
+"""SCons.Tool.fortran
+
+Tool-specific initialization for a generic Posix f77/f90 Fortran compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/fortran.py 4577 2009/12/27 19:44:43 scons"
+
+import re
+import string
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Scanner.Fortran
+import SCons.Tool
+import SCons.Util
+from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env
+
+compilers = ['f95', 'f90', 'f77']
+
+def generate(env):
+ add_all_to_env(env)
+ add_fortran_to_env(env)
+
+ fc = env.Detect(compilers) or 'f77'
+ env['SHFORTRAN'] = fc
+ env['FORTRAN'] = fc
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/fortran.xml b/src/engine/SCons/Tool/fortran.xml
new file mode 100644
index 0000000..1800559
--- /dev/null
+++ b/src/engine/SCons/Tool/fortran.xml
@@ -0,0 +1,302 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="fortran">
+<summary>
+Set construction variables for generic POSIX Fortran compilers.
+</summary>
+<sets>
+FORTRAN
+FORTRANFLAGS
+FORTRANCOM
+SHFORTRAN
+SHFORTRANFLAGS
+SHFORTRANCOM
+SHFORTRANPPCOM
+</sets>
+<uses>
+FORTRANCOMSTR
+FORTRANPPCOMSTR
+SHFORTRANCOMSTR
+SHFORTRANPPCOMSTR
+</uses>
+</tool>
+
+<cvar name="FORTRAN">
+<summary>
+The default Fortran compiler
+for all versions of Fortran.
+</summary>
+</cvar>
+
+<cvar name="FORTRANCOM">
+<summary>
+The command line used to compile a Fortran source file to an object file.
+By default, any options specified
+in the &cv-link-FORTRANFLAGS;,
+&cv-link-CPPFLAGS;,
+&cv-link-_CPPDEFFLAGS;,
+&cv-link-_FORTRANMODFLAG;, and
+&cv-link-_FORTRANINCFLAGS; construction variables
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="FORTRANCOMSTR">
+<summary>
+The string displayed when a Fortran source file
+is compiled to an object file.
+If this is not set, then &cv-link-FORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="FORTRANFILESUFFIXES">
+<summary>
+The list of file extensions for which the FORTRAN dialect will be used. By
+default, this is ['.f', '.for', '.ftn']
+</summary>
+</cvar>
+
+<cvar name="FORTRANPPFILESUFFIXES">
+<summary>
+The list of file extensions for which the compilation + preprocessor pass for
+FORTRAN dialect will be used. By default, this is ['.fpp', '.FPP']
+</summary>
+</cvar>
+
+<cvar name="FORTRANFLAGS">
+<summary>
+General user-specified options that are passed to the Fortran compiler.
+Note that this variable does
+<emphasis>not</emphasis>
+contain
+<option>-I</option>
+(or similar) include or module search path options
+that scons generates automatically from &cv-link-FORTRANPATH;.
+See
+&cv-link-_FORTRANINCFLAGS; and &cv-link-_FORTRANMODFLAG;,
+below,
+for the variables that expand those options.
+</summary>
+</cvar>
+
+<cvar name="_FORTRANINCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the Fortran compiler command-line options
+for specifying directories to be searched for include
+files and module files.
+The value of &cv-link-_FORTRANINCFLAGS; is created
+by prepending/appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+to the beginning and end
+of each directory in &cv-link-FORTRANPATH;.
+</summary>
+</cvar>
+
+<cvar name="FORTRANMODDIR">
+<summary>
+Directory location where the Fortran compiler should place
+any module files it generates. This variable is empty, by default. Some
+Fortran compilers will internally append this directory in the search path
+for module files, as well.
+</summary>
+</cvar>
+
+<cvar name="FORTRANMODDIRPREFIX">
+<summary>
+The prefix used to specify a module directory on the Fortran compiler command
+line.
+This will be appended to the beginning of the directory
+in the &cv-link-FORTRANMODDIR; construction variables
+when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="FORTRANMODDIRSUFFIX">
+<summary>
+The suffix used to specify a module directory on the Fortran compiler command
+line.
+This will be appended to the beginning of the directory
+in the &cv-link-FORTRANMODDIR; construction variables
+when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="_FORTRANMODFLAG">
+<summary>
+An automatically-generated construction variable
+containing the Fortran compiler command-line option
+for specifying the directory location where the Fortran
+compiler should place any module files that happen to get
+generated during compilation.
+The value of &cv-link-_FORTRANMODFLAG; is created
+by prepending/appending &cv-link-FORTRANMODDIRPREFIX; and
+&cv-link-FORTRANMODDIRSUFFIX;
+to the beginning and end of the directory in &cv-link-FORTRANMODDIR;.
+</summary>
+</cvar>
+
+<cvar name="FORTRANMODPREFIX">
+<summary>
+The module file prefix used by the Fortran compiler. SCons assumes that
+the Fortran compiler follows the quasi-standard naming convention for
+module files of
+<filename>module_name.mod</filename>.
+As a result, this variable is left empty, by default. For situations in
+which the compiler does not necessarily follow the normal convention,
+the user may use this variable. Its value will be appended to every
+module file name as scons attempts to resolve dependencies.
+</summary>
+</cvar>
+
+<cvar name="FORTRANMODSUFFIX">
+<summary>
+The module file suffix used by the Fortran compiler. SCons assumes that
+the Fortran compiler follows the quasi-standard naming convention for
+module files of
+<filename>module_name.mod</filename>.
+As a result, this variable is set to ".mod", by default. For situations
+in which the compiler does not necessarily follow the normal convention,
+the user may use this variable. Its value will be appended to every
+module file name as scons attempts to resolve dependencies.
+</summary>
+</cvar>
+
+<cvar name="FORTRANPATH">
+<summary>
+The list of directories that the Fortran compiler will search for
+include files and (for some compilers) module files. The Fortran implicit
+dependency scanner will search these directories for include files (but
+not module files since they are autogenerated and, as such, may not
+actually exist at the time the scan takes place). Don't explicitly put
+include directory arguments in FORTRANFLAGS because the result will be
+non-portable and the directories will not be searched by the dependency
+scanner. Note: directory names in FORTRANPATH will be looked-up relative
+to the SConscript directory when they are used in a command. To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+
+<example>
+env = Environment(FORTRANPATH='#/include')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+include = Dir('include')
+env = Environment(FORTRANPATH=include)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-link-_FORTRANINCFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-link-FORTRANPATH;.
+Any command lines you define that need
+the FORTRANPATH directory list should
+include &cv-link-_FORTRANINCFLAGS;:
+
+<example>
+env = Environment(FORTRANCOM="my_compiler $_FORTRANINCFLAGS -c -o $TARGET $SOURCE")
+</example>
+</summary>
+</cvar>
+
+<cvar name="FORTRANPPCOM">
+<summary>
+The command line used to compile a Fortran source file to an object file
+after first running the file through the C preprocessor.
+By default, any options specified in the &cv-link-FORTRANFLAGS;,
+&cv-link-CPPFLAGS;,
+&cv-link-_CPPDEFFLAGS;,
+&cv-link-_FORTRANMODFLAG;, and
+&cv-link-_FORTRANINCFLAGS;
+construction variables are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="FORTRANPPCOMSTR">
+<summary>
+The string displayed when a Fortran source file
+is compiled to an object file
+after first running the file throught the C preprocessor.
+If this is not set, then &cv-link-FORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="FORTRANSUFFIXES">
+<summary>
+The list of suffixes of files that will be scanned
+for Fortran implicit dependencies
+(INCLUDE lines and USE statements).
+The default list is:
+
+<example>
+[".f", ".F", ".for", ".FOR", ".ftn", ".FTN", ".fpp", ".FPP",
+".f77", ".F77", ".f90", ".F90", ".f95", ".F95"]
+</example>
+</summary>
+</cvar>
+
+<cvar name="SHFORTRAN">
+<summary>
+The default Fortran compiler used for generating shared-library objects.
+</summary>
+</cvar>
+
+<cvar name="SHFORTRANCOM">
+<summary>
+The command line used to compile a Fortran source file
+to a shared-library object file.
+</summary>
+</cvar>
+
+<cvar name="SHFORTRANCOMSTR">
+<summary>
+The string displayed when a Fortran source file
+is compiled to a shared-library object file.
+If this is not set, then &cv-link-SHFORTRANCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SHFORTRANFLAGS">
+<summary>
+Options that are passed to the Fortran compiler
+to generate shared-library objects.
+</summary>
+</cvar>
+
+<cvar name="SHFORTRANPPCOM">
+<summary>
+The command line used to compile a Fortran source file to a
+shared-library object file
+after first running the file through the C preprocessor.
+Any options specified
+in the &cv-link-SHFORTRANFLAGS; and
+&cv-link-CPPFLAGS; construction variables
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="SHFORTRANPPCOMSTR">
+<summary>
+The string displayed when a Fortran source file
+is compiled to a shared-library object file
+after first running the file throught the C preprocessor.
+If this is not set, then &cv-link-SHFORTRANPPCOM;
+(the command line) is displayed.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/g++.py b/src/engine/SCons/Tool/g++.py
new file mode 100644
index 0000000..1ffb254
--- /dev/null
+++ b/src/engine/SCons/Tool/g++.py
@@ -0,0 +1,90 @@
+"""SCons.Tool.g++
+
+Tool-specific initialization for g++.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/g++.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import re
+import subprocess
+
+import SCons.Tool
+import SCons.Util
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+compilers = ['g++']
+
+def generate(env):
+ """Add Builders and construction variables for g++ to an Environment."""
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ cplusplus.generate(env)
+
+ env['CXX'] = env.Detect(compilers)
+
+ # platform specific settings
+ if env['PLATFORM'] == 'aix':
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc')
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
+ env['SHOBJSUFFIX'] = '$OBJSUFFIX'
+ elif env['PLATFORM'] == 'hpux':
+ env['SHOBJSUFFIX'] = '.pic.o'
+ elif env['PLATFORM'] == 'sunos':
+ env['SHOBJSUFFIX'] = '.pic.o'
+ # determine compiler version
+ if env['CXX']:
+ #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'],
+ pipe = SCons.Action._subproc(env, [env['CXX'], '--version'],
+ stdin = 'devnull',
+ stderr = 'devnull',
+ stdout = subprocess.PIPE)
+ if pipe.wait() != 0: return
+ # -dumpversion was added in GCC 3.0. As long as we're supporting
+ # GCC versions older than that, we should use --version and a
+ # regular expression.
+ #line = pipe.stdout.read().strip()
+ #if line:
+ # env['CXXVERSION'] = line
+ line = pipe.stdout.readline()
+ match = re.search(r'[0-9]+(\.[0-9]+)+', line)
+ if match:
+ env['CXXVERSION'] = match.group(0)
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/g++.xml b/src/engine/SCons/Tool/g++.xml
new file mode 100644
index 0000000..37bd2f8
--- /dev/null
+++ b/src/engine/SCons/Tool/g++.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="g++">
+<summary>
+Set construction variables for the &gXX; C++ compiler.
+</summary>
+<sets>
+CXX
+SHCXXFLAGS
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+SHOBJSUFFIX
+CXXVERSION
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/g77.py b/src/engine/SCons/Tool/g77.py
new file mode 100644
index 0000000..2607b76
--- /dev/null
+++ b/src/engine/SCons/Tool/g77.py
@@ -0,0 +1,73 @@
+"""engine.SCons.Tool.g77
+
+Tool-specific initialization for g77.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/g77.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env
+
+compilers = ['g77', 'f77']
+
+def generate(env):
+ """Add Builders and construction variables for g77 to an Environment."""
+ add_all_to_env(env)
+ add_f77_to_env(env)
+
+ fcomp = env.Detect(compilers) or 'g77'
+ if env['PLATFORM'] in ['cygwin', 'win32']:
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS')
+ env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS')
+ else:
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC')
+ env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC')
+
+ env['FORTRAN'] = fcomp
+ env['SHFORTRAN'] = '$FORTRAN'
+
+ env['F77'] = fcomp
+ env['SHF77'] = '$F77'
+
+ env['INCFORTRANPREFIX'] = "-I"
+ env['INCFORTRANSUFFIX'] = ""
+
+ env['INCF77PREFIX'] = "-I"
+ env['INCF77SUFFIX'] = ""
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/g77.xml b/src/engine/SCons/Tool/g77.xml
new file mode 100644
index 0000000..b694580
--- /dev/null
+++ b/src/engine/SCons/Tool/g77.xml
@@ -0,0 +1,13 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="g77">
+<summary>
+Set construction variables for the &g77; Fortran compiler.
+Calls the &t-f77; Tool module
+to set variables.
+</summary>
+</tool>
diff --git a/src/engine/SCons/Tool/gas.py b/src/engine/SCons/Tool/gas.py
new file mode 100644
index 0000000..7ac4d71
--- /dev/null
+++ b/src/engine/SCons/Tool/gas.py
@@ -0,0 +1,53 @@
+"""SCons.Tool.gas
+
+Tool-specific initialization for as, the Gnu assembler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/gas.py 4577 2009/12/27 19:44:43 scons"
+
+as_module = __import__('as', globals(), locals(), [])
+
+assemblers = ['as', 'gas']
+
+def generate(env):
+ """Add Builders and construction variables for as to an Environment."""
+ as_module.generate(env)
+
+ env['AS'] = env.Detect(assemblers) or 'as'
+
+def exists(env):
+ return env.Detect(assemblers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/gas.xml b/src/engine/SCons/Tool/gas.xml
new file mode 100644
index 0000000..6b60b33
--- /dev/null
+++ b/src/engine/SCons/Tool/gas.xml
@@ -0,0 +1,15 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="gas">
+<summary>
+Sets construction variables for the &gas; assembler.
+Calls the &t-as; module.
+</summary>
+<sets>
+AS
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/gcc.py b/src/engine/SCons/Tool/gcc.py
new file mode 100644
index 0000000..58597a6
--- /dev/null
+++ b/src/engine/SCons/Tool/gcc.py
@@ -0,0 +1,80 @@
+"""SCons.Tool.gcc
+
+Tool-specific initialization for gcc.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/gcc.py 4577 2009/12/27 19:44:43 scons"
+
+import cc
+import os
+import re
+import subprocess
+
+import SCons.Util
+
+compilers = ['gcc', 'cc']
+
+def generate(env):
+ """Add Builders and construction variables for gcc to an Environment."""
+ cc.generate(env)
+
+ env['CC'] = env.Detect(compilers) or 'gcc'
+ if env['PLATFORM'] in ['cygwin', 'win32']:
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+ else:
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC')
+ # determine compiler version
+ if env['CC']:
+ #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'],
+ pipe = SCons.Action._subproc(env, [env['CC'], '--version'],
+ stdin = 'devnull',
+ stderr = 'devnull',
+ stdout = subprocess.PIPE)
+ if pipe.wait() != 0: return
+ # -dumpversion was added in GCC 3.0. As long as we're supporting
+ # GCC versions older than that, we should use --version and a
+ # regular expression.
+ #line = pipe.stdout.read().strip()
+ #if line:
+ # env['CCVERSION'] = line
+ line = pipe.stdout.readline()
+ match = re.search(r'[0-9]+(\.[0-9]+)+', line)
+ if match:
+ env['CCVERSION'] = match.group(0)
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/gcc.xml b/src/engine/SCons/Tool/gcc.xml
new file mode 100644
index 0000000..ac78a98
--- /dev/null
+++ b/src/engine/SCons/Tool/gcc.xml
@@ -0,0 +1,16 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="gcc">
+<summary>
+Set construction variables for the &gcc; C compiler.
+</summary>
+<sets>
+CC
+SHCCFLAGS
+CCVERSION
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/gfortran.py b/src/engine/SCons/Tool/gfortran.py
new file mode 100644
index 0000000..79e8817
--- /dev/null
+++ b/src/engine/SCons/Tool/gfortran.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.gfortran
+
+Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran
+2003 compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/gfortran.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+import fortran
+
+def generate(env):
+ """Add Builders and construction variables for gfortran to an
+ Environment."""
+ fortran.generate(env)
+
+ for dialect in ['F77', 'F90', 'FORTRAN', 'F95']:
+ env['%s' % dialect] = 'gfortran'
+ env['SH%s' % dialect] = '$%s' % dialect
+ if env['PLATFORM'] in ['cygwin', 'win32']:
+ env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
+ else:
+ env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect)
+
+ env['INC%sPREFIX' % dialect] = "-I"
+ env['INC%sSUFFIX' % dialect] = ""
+
+def exists(env):
+ return env.Detect('gfortran')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/gfortran.xml b/src/engine/SCons/Tool/gfortran.xml
new file mode 100644
index 0000000..cf6bb9a
--- /dev/null
+++ b/src/engine/SCons/Tool/gfortran.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="gfortran">
+<summary>
+Sets construction variables for the GNU F95/F2003 GNU compiler.
+</summary>
+<sets>
+FORTRAN
+F77
+F90
+F95
+SHFORTRAN
+SHF77
+SHF90
+SHF95
+SHFORTRANFLAGS
+SHF77FLAGS
+SHF90FLAGS
+SHF95FLAGS
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py
new file mode 100644
index 0000000..9a12f82
--- /dev/null
+++ b/src/engine/SCons/Tool/gnulink.py
@@ -0,0 +1,63 @@
+"""SCons.Tool.gnulink
+
+Tool-specific initialization for the gnu linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/gnulink.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+import link
+
+linkers = ['g++', 'gcc']
+
+def generate(env):
+ """Add Builders and construction variables for gnulink to an Environment."""
+ link.generate(env)
+
+ if env['PLATFORM'] == 'hpux':
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC')
+
+ # __RPATH is set to $_RPATH in the platform specification if that
+ # platform supports it.
+ env.Append(LINKFLAGS=['$__RPATH'])
+ env['RPATHPREFIX'] = '-Wl,-rpath='
+ env['RPATHSUFFIX'] = ''
+ env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}'
+
+def exists(env):
+ return env.Detect(linkers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/gnulink.xml b/src/engine/SCons/Tool/gnulink.xml
new file mode 100644
index 0000000..0f499de
--- /dev/null
+++ b/src/engine/SCons/Tool/gnulink.xml
@@ -0,0 +1,16 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="gnulink">
+<summary>
+Set construction variables for GNU linker/loader.
+</summary>
+<sets>
+SHLINKFLAGS
+RPATHPREFIX
+RPATHSUFFIX
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/gs.py b/src/engine/SCons/Tool/gs.py
new file mode 100644
index 0000000..2c0db92
--- /dev/null
+++ b/src/engine/SCons/Tool/gs.py
@@ -0,0 +1,81 @@
+"""SCons.Tool.gs
+
+Tool-specific initialization for Ghostscript.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/gs.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Platform
+import SCons.Util
+
+# Ghostscript goes by different names on different platforms...
+platform = SCons.Platform.platform_default()
+
+if platform == 'os2':
+ gs = 'gsos2'
+elif platform == 'win32':
+ gs = 'gswin32c'
+else:
+ gs = 'gs'
+
+GhostscriptAction = None
+
+def generate(env):
+ """Add Builders and construction variables for Ghostscript to an
+ Environment."""
+
+ global GhostscriptAction
+ if GhostscriptAction is None:
+ GhostscriptAction = SCons.Action.Action('$GSCOM', '$GSCOMSTR')
+
+ import pdf
+ pdf.generate(env)
+
+ bld = env['BUILDERS']['PDF']
+ bld.add_action('.ps', GhostscriptAction)
+
+ env['GS'] = gs
+ env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite')
+ env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES'
+
+
+def exists(env):
+ if env.has_key('PS2PDF'):
+ return env.Detect(env['PS2PDF'])
+ else:
+ return env.Detect(gs) or SCons.Util.WhereIs(gs)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/gs.xml b/src/engine/SCons/Tool/gs.xml
new file mode 100644
index 0000000..e1817d5
--- /dev/null
+++ b/src/engine/SCons/Tool/gs.xml
@@ -0,0 +1,47 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="gs">
+<summary>
+Set construction variables for Ghostscript.
+</summary>
+<sets>
+GS
+GSFLAGS
+GSCOM
+</sets>
+<uses>
+GSCOMSTR
+</uses>
+</tool>
+
+<cvar name="GS">
+<summary>
+The Ghostscript program used to convert PostScript to PDF files.
+</summary>
+</cvar>
+
+<cvar name="GSCOM">
+<summary>
+The Ghostscript command line used to convert PostScript to PDF files.
+</summary>
+</cvar>
+
+<cvar name="GSCOMSTR">
+<summary>
+The string displayed when
+Ghostscript is used to convert
+a PostScript file to a PDF file.
+If this is not set, then &cv-link-GSCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="GSFLAGS">
+<summary>
+General options passed to the Ghostscript program
+when converting PostScript to PDF files.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/hpc++.py b/src/engine/SCons/Tool/hpc++.py
new file mode 100644
index 0000000..cd7e041
--- /dev/null
+++ b/src/engine/SCons/Tool/hpc++.py
@@ -0,0 +1,85 @@
+"""SCons.Tool.hpc++
+
+Tool-specific initialization for c++ on HP/UX.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/hpc++.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+
+import SCons.Util
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+acc = None
+
+# search for the acc compiler and linker front end
+
+try:
+ dirs = os.listdir('/opt')
+except (IOError, OSError):
+ # Not being able to read the directory because it doesn't exist
+ # (IOError) or isn't readable (OSError) is okay.
+ dirs = []
+
+for dir in dirs:
+ cc = '/opt/' + dir + '/bin/aCC'
+ if os.path.exists(cc):
+ acc = cc
+ break
+
+
+def generate(env):
+ """Add Builders and construction variables for g++ to an Environment."""
+ cplusplus.generate(env)
+
+ if acc:
+ env['CXX'] = acc or 'aCC'
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z')
+ # determine version of aCC
+ line = os.popen(acc + ' -V 2>&1').readline().rstrip()
+ if string.find(line, 'aCC: HP ANSI C++') == 0:
+ env['CXXVERSION'] = string.split(line)[-1]
+
+ if env['PLATFORM'] == 'cygwin':
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
+ else:
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z')
+
+def exists(env):
+ return acc
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/hpc++.xml b/src/engine/SCons/Tool/hpc++.xml
new file mode 100644
index 0000000..3829ae7
--- /dev/null
+++ b/src/engine/SCons/Tool/hpc++.xml
@@ -0,0 +1,11 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="hpc++">
+<summary>
+Set construction variables for the compilers aCC on HP/UX systems.
+</summary>
+</tool>
diff --git a/src/engine/SCons/Tool/hpcc.py b/src/engine/SCons/Tool/hpcc.py
new file mode 100644
index 0000000..122e3aa
--- /dev/null
+++ b/src/engine/SCons/Tool/hpcc.py
@@ -0,0 +1,53 @@
+"""SCons.Tool.hpcc
+
+Tool-specific initialization for HP aCC and cc.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/hpcc.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+import cc
+
+def generate(env):
+ """Add Builders and construction variables for aCC & cc to an Environment."""
+ cc.generate(env)
+
+ env['CXX'] = 'aCC'
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS +Z')
+
+def exists(env):
+ return env.Detect('aCC')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/hpcc.xml b/src/engine/SCons/Tool/hpcc.xml
new file mode 100644
index 0000000..f103384
--- /dev/null
+++ b/src/engine/SCons/Tool/hpcc.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="hpcc">
+<summary>
+Set construction variables for the
+<application>aCC</application> on HP/UX systems.
+Calls the &t-cXX; tool for additional variables.
+</summary>
+<sets>
+CXX
+SHCXXFLAGS
+CXXVERSION
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/hplink.py b/src/engine/SCons/Tool/hplink.py
new file mode 100644
index 0000000..32c59a5
--- /dev/null
+++ b/src/engine/SCons/Tool/hplink.py
@@ -0,0 +1,77 @@
+"""SCons.Tool.hplink
+
+Tool-specific initialization for the HP linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/hplink.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+
+import SCons.Util
+
+import link
+
+ccLinker = None
+
+# search for the acc compiler and linker front end
+
+try:
+ dirs = os.listdir('/opt')
+except (IOError, OSError):
+ # Not being able to read the directory because it doesn't exist
+ # (IOError) or isn't readable (OSError) is okay.
+ dirs = []
+
+for dir in dirs:
+ linker = '/opt/' + dir + '/bin/aCC'
+ if os.path.exists(linker):
+ ccLinker = linker
+ break
+
+def generate(env):
+ """
+ Add Builders and construction variables for Visual Age linker to
+ an Environment.
+ """
+ link.generate(env)
+
+ env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings')
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b')
+ env['SHLIBSUFFIX'] = '.sl'
+
+def exists(env):
+ return ccLinker
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/hplink.xml b/src/engine/SCons/Tool/hplink.xml
new file mode 100644
index 0000000..d4c29fb
--- /dev/null
+++ b/src/engine/SCons/Tool/hplink.xml
@@ -0,0 +1,16 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="hplink">
+<summary>
+Sets construction variables for the linker on HP/UX systems.
+</summary>
+<sets>
+LINKFLAGS
+SHLINKFLAGS
+SHLIBSUFFIX
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/icc.py b/src/engine/SCons/Tool/icc.py
new file mode 100644
index 0000000..78f8606
--- /dev/null
+++ b/src/engine/SCons/Tool/icc.py
@@ -0,0 +1,59 @@
+"""engine.SCons.Tool.icc
+
+Tool-specific initialization for the OS/2 icc compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/icc.py 4577 2009/12/27 19:44:43 scons"
+
+import cc
+
+def generate(env):
+ """Add Builders and construction variables for the OS/2 to an Environment."""
+ cc.generate(env)
+
+ env['CC'] = 'icc'
+ env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['CPPDEFPREFIX'] = '/D'
+ env['CPPDEFSUFFIX'] = ''
+ env['INCPREFIX'] = '/I'
+ env['INCSUFFIX'] = ''
+ env['CFILESUFFIX'] = '.c'
+ env['CXXFILESUFFIX'] = '.cc'
+
+def exists(env):
+ return env.Detect('icc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/icc.xml b/src/engine/SCons/Tool/icc.xml
new file mode 100644
index 0000000..6b03105
--- /dev/null
+++ b/src/engine/SCons/Tool/icc.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="icc">
+<summary>
+Sets construction variables for the
+<application>icc</application> compiler on OS/2 systems.
+</summary>
+<sets>
+CC
+CCCOM
+CXXCOM
+CPPDEFPREFIX
+CPPDEFSUFFIX
+INCPREFIX
+INCSUFFIX
+CFILESUFFIX
+CXXFILESUFFIX
+</sets>
+<uses>
+CFLAGS
+CCFLAGS
+CPPFLAGS
+_CPPDEFFLAGS
+_CPPINCFLAGS
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/icl.py b/src/engine/SCons/Tool/icl.py
new file mode 100644
index 0000000..3a43da7
--- /dev/null
+++ b/src/engine/SCons/Tool/icl.py
@@ -0,0 +1,52 @@
+"""engine.SCons.Tool.icl
+
+Tool-specific initialization for the Intel C/C++ compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/icl.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Tool.intelc
+
+# This has been completely superceded by intelc.py, which can
+# handle both Windows and Linux versions.
+
+def generate(*args, **kw):
+ """Add Builders and construction variables for icl to an Environment."""
+ return apply(SCons.Tool.intelc.generate, args, kw)
+
+def exists(*args, **kw):
+ return apply(SCons.Tool.intelc.exists, args, kw)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/icl.xml b/src/engine/SCons/Tool/icl.xml
new file mode 100644
index 0000000..06dc7af
--- /dev/null
+++ b/src/engine/SCons/Tool/icl.xml
@@ -0,0 +1,12 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="icl">
+<summary>
+Sets construction variables for the Intel C/C++ compiler.
+Calls the &t-intelc; Tool module to set its variables.
+</summary>
+</tool>
diff --git a/src/engine/SCons/Tool/ifl.py b/src/engine/SCons/Tool/ifl.py
new file mode 100644
index 0000000..8fe8c3a
--- /dev/null
+++ b/src/engine/SCons/Tool/ifl.py
@@ -0,0 +1,72 @@
+"""SCons.Tool.ifl
+
+Tool-specific initialization for the Intel Fortran compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ifl.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+from SCons.Scanner.Fortran import FortranScan
+from FortranCommon import add_all_to_env
+
+def generate(env):
+ """Add Builders and construction variables for ifl to an Environment."""
+ fscan = FortranScan("FORTRANPATH")
+ SCons.Tool.SourceFileScanner.add_scanner('.i', fscan)
+ SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan)
+
+ if not env.has_key('FORTRANFILESUFFIXES'):
+ env['FORTRANFILESUFFIXES'] = ['.i']
+ else:
+ env['FORTRANFILESUFFIXES'].append('.i')
+
+ if not env.has_key('F90FILESUFFIXES'):
+ env['F90FILESUFFIXES'] = ['.i90']
+ else:
+ env['F90FILESUFFIXES'].append('.i90')
+
+ add_all_to_env(env)
+
+ env['FORTRAN'] = 'ifl'
+ env['SHFORTRAN'] = '$FORTRAN'
+ env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET'
+
+def exists(env):
+ return env.Detect('ifl')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/ifl.xml b/src/engine/SCons/Tool/ifl.xml
new file mode 100644
index 0000000..3456d36
--- /dev/null
+++ b/src/engine/SCons/Tool/ifl.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="ifl">
+<summary>
+Sets construction variables for the Intel Fortran compiler.
+</summary>
+<sets>
+FORTRAN
+FORTRANCOM
+FORTRANPPCOM
+SHFORTRANCOM
+SHFORTRANPPCOM
+</sets>
+<uses>
+FORTRANFLAGS
+_FORTRANINCFLAGS
+CPPFLAGS
+_CPPDEFFLAGS
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/ifort.py b/src/engine/SCons/Tool/ifort.py
new file mode 100644
index 0000000..250d875
--- /dev/null
+++ b/src/engine/SCons/Tool/ifort.py
@@ -0,0 +1,90 @@
+"""SCons.Tool.ifort
+
+Tool-specific initialization for newer versions of the Intel Fortran Compiler
+for Linux/Windows (and possibly Mac OS X).
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ifort.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+
+import SCons.Defaults
+from SCons.Scanner.Fortran import FortranScan
+from FortranCommon import add_all_to_env
+
+def generate(env):
+ """Add Builders and construction variables for ifort to an Environment."""
+ # ifort supports Fortran 90 and Fortran 95
+ # Additionally, ifort recognizes more file extensions.
+ fscan = FortranScan("FORTRANPATH")
+ SCons.Tool.SourceFileScanner.add_scanner('.i', fscan)
+ SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan)
+
+ if not env.has_key('FORTRANFILESUFFIXES'):
+ env['FORTRANFILESUFFIXES'] = ['.i']
+ else:
+ env['FORTRANFILESUFFIXES'].append('.i')
+
+ if not env.has_key('F90FILESUFFIXES'):
+ env['F90FILESUFFIXES'] = ['.i90']
+ else:
+ env['F90FILESUFFIXES'].append('.i90')
+
+ add_all_to_env(env)
+
+ fc = 'ifort'
+
+ for dialect in ['F77', 'F90', 'FORTRAN', 'F95']:
+ env['%s' % dialect] = fc
+ env['SH%s' % dialect] = '$%s' % dialect
+ if env['PLATFORM'] == 'posix':
+ env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect)
+
+ if env['PLATFORM'] == 'win32':
+ # On Windows, the ifort compiler specifies the object on the
+ # command line with -object:, not -o. Massage the necessary
+ # command-line construction variables.
+ for dialect in ['F77', 'F90', 'FORTRAN', 'F95']:
+ for var in ['%sCOM' % dialect, '%sPPCOM' % dialect,
+ 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]:
+ env[var] = string.replace(env[var], '-o $TARGET', '-object:$TARGET')
+ env['FORTRANMODDIRPREFIX'] = "/module:"
+ else:
+ env['FORTRANMODDIRPREFIX'] = "-module "
+
+def exists(env):
+ return env.Detect('ifort')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/ifort.xml b/src/engine/SCons/Tool/ifort.xml
new file mode 100644
index 0000000..fc348ab
--- /dev/null
+++ b/src/engine/SCons/Tool/ifort.xml
@@ -0,0 +1,26 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="ifort">
+<summary>
+Sets construction variables for newer versions
+of the Intel Fortran compiler for Linux.
+</summary>
+<sets>
+FORTRAN
+F77
+F90
+F95
+SHFORTRAN
+SHF77
+SHF90
+SHF95
+SHFORTRANFLAGS
+SHF77FLAGS
+SHF90FLAGS
+SHF95FLAGS
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/ilink.py b/src/engine/SCons/Tool/ilink.py
new file mode 100644
index 0000000..c8d74f7
--- /dev/null
+++ b/src/engine/SCons/Tool/ilink.py
@@ -0,0 +1,59 @@
+"""SCons.Tool.ilink
+
+Tool-specific initialization for the OS/2 ilink linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ilink.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+def generate(env):
+ """Add Builders and construction variables for ilink to an Environment."""
+ SCons.Tool.createProgBuilder(env)
+
+ env['LINK'] = 'ilink'
+ env['LINKFLAGS'] = SCons.Util.CLVar('')
+ env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['LIBDIRPREFIX']='/LIBPATH:'
+ env['LIBDIRSUFFIX']=''
+ env['LIBLINKPREFIX']=''
+ env['LIBLINKSUFFIX']='$LIBSUFFIX'
+
+def exists(env):
+ return env.Detect('ilink')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/ilink.xml b/src/engine/SCons/Tool/ilink.xml
new file mode 100644
index 0000000..a5f25a6
--- /dev/null
+++ b/src/engine/SCons/Tool/ilink.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="ilink">
+<summary>
+Sets construction variables for the
+<application>ilink</application> linker on OS/2 systems.
+</summary>
+<sets>
+LINK
+LINKFLAGS
+LINKCOM
+LIBDIRPREFIX
+LIBDIRSUFFIX
+LIBLINKPREFIX
+LIBLINKSUFFIX
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/ilink32.py b/src/engine/SCons/Tool/ilink32.py
new file mode 100644
index 0000000..f0fd9f8
--- /dev/null
+++ b/src/engine/SCons/Tool/ilink32.py
@@ -0,0 +1,60 @@
+"""SCons.Tool.ilink32
+
+XXX
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ilink32.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Tool
+import SCons.Tool.bcc32
+import SCons.Util
+
+def generate(env):
+ """Add Builders and construction variables for Borland ilink to an
+ Environment."""
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
+
+ env['LINK'] = '$CC'
+ env['LINKFLAGS'] = SCons.Util.CLVar('')
+ env['LINKCOM'] = '$LINK -q $LINKFLAGS -e$TARGET $SOURCES $LIBS'
+ env['LIBDIRPREFIX']=''
+ env['LIBDIRSUFFIX']=''
+ env['LIBLINKPREFIX']=''
+ env['LIBLINKSUFFIX']='$LIBSUFFIX'
+
+
+def exists(env):
+ # Uses bcc32 to do linking as it generally knows where the standard
+ # LIBS are and set up the linking correctly
+ return SCons.Tool.bcc32.findIt('bcc32', env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/ilink32.xml b/src/engine/SCons/Tool/ilink32.xml
new file mode 100644
index 0000000..0528487
--- /dev/null
+++ b/src/engine/SCons/Tool/ilink32.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="ilink32">
+<summary>
+Sets construction variables for the Borland
+<application>ilink32</application> linker.
+</summary>
+<sets>
+LINK
+LINKFLAGS
+LINKCOM
+LIBDIRPREFIX
+LIBDIRSUFFIX
+LIBLINKPREFIX
+LIBLINKSUFFIX
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py
new file mode 100644
index 0000000..4c2ac2e
--- /dev/null
+++ b/src/engine/SCons/Tool/install.py
@@ -0,0 +1,229 @@
+"""SCons.Tool.install
+
+Tool-specific initialization for the install tool.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/install.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import shutil
+import stat
+
+import SCons.Action
+from SCons.Util import make_path_relative
+
+#
+# We keep track of *all* installed files.
+_INSTALLED_FILES = []
+_UNIQUE_INSTALLED_FILES = None
+
+#
+# Functions doing the actual work of the Install Builder.
+#
+def copyFunc(dest, source, env):
+ """Install a source file or directory into a destination by copying,
+ (including copying permission/mode bits)."""
+
+ if os.path.isdir(source):
+ if os.path.exists(dest):
+ if not os.path.isdir(dest):
+ raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))
+ else:
+ parent = os.path.split(dest)[0]
+ if not os.path.exists(parent):
+ os.makedirs(parent)
+ shutil.copytree(source, dest)
+ else:
+ shutil.copy2(source, dest)
+ st = os.stat(source)
+ os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+
+ return 0
+
+def installFunc(target, source, env):
+ """Install a source file into a target using the function specified
+ as the INSTALL construction variable."""
+ try:
+ install = env['INSTALL']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing INSTALL construction variable.')
+
+ assert len(target)==len(source), \
+ "Installing source %s into target %s: target and source lists must have same length."%(map(str, source), map(str, target))
+ for t,s in zip(target,source):
+ if install(t.get_path(),s.get_path(),env):
+ return 1
+
+ return 0
+
+def stringFunc(target, source, env):
+ installstr = env.get('INSTALLSTR')
+ if installstr:
+ return env.subst_target_source(installstr, 0, target, source)
+ target = str(target[0])
+ source = str(source[0])
+ if os.path.isdir(source):
+ type = 'directory'
+ else:
+ type = 'file'
+ return 'Install %s: "%s" as "%s"' % (type, source, target)
+
+#
+# Emitter functions
+#
+def add_targets_to_INSTALLED_FILES(target, source, env):
+ """ an emitter that adds all target files to the list stored in the
+ _INSTALLED_FILES global variable. This way all installed files of one
+ scons call will be collected.
+ """
+ global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES
+ _INSTALLED_FILES.extend(target)
+ _UNIQUE_INSTALLED_FILES = None
+ return (target, source)
+
+class DESTDIR_factory:
+ """ a node factory, where all files will be relative to the dir supplied
+ in the constructor.
+ """
+ def __init__(self, env, dir):
+ self.env = env
+ self.dir = env.arg2nodes( dir, env.fs.Dir )[0]
+
+ def Entry(self, name):
+ name = make_path_relative(name)
+ return self.dir.Entry(name)
+
+ def Dir(self, name):
+ name = make_path_relative(name)
+ return self.dir.Dir(name)
+
+#
+# The Builder Definition
+#
+install_action = SCons.Action.Action(installFunc, stringFunc)
+installas_action = SCons.Action.Action(installFunc, stringFunc)
+
+BaseInstallBuilder = None
+
+def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw):
+ if target and dir:
+ import SCons.Errors
+ raise SCons.Errors.UserError, "Both target and dir defined for Install(), only one may be defined."
+ if not dir:
+ dir=target
+
+ import SCons.Script
+ install_sandbox = SCons.Script.GetOption('install_sandbox')
+ if install_sandbox:
+ target_factory = DESTDIR_factory(env, install_sandbox)
+ else:
+ target_factory = env.fs
+
+ try:
+ dnodes = env.arg2nodes(dir, target_factory.Dir)
+ except TypeError:
+ raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)
+ sources = env.arg2nodes(source, env.fs.Entry)
+ tgt = []
+ for dnode in dnodes:
+ for src in sources:
+ # Prepend './' so the lookup doesn't interpret an initial
+ # '#' on the file name portion as meaning the Node should
+ # be relative to the top-level SConstruct directory.
+ target = env.fs.Entry('.'+os.sep+src.name, dnode)
+ #tgt.extend(BaseInstallBuilder(env, target, src, **kw))
+ tgt.extend(apply(BaseInstallBuilder, (env, target, src), kw))
+ return tgt
+
+def InstallAsBuilderWrapper(env, target=None, source=None, **kw):
+ result = []
+ for src, tgt in map(lambda x, y: (x, y), source, target):
+ #result.extend(BaseInstallBuilder(env, tgt, src, **kw))
+ result.extend(apply(BaseInstallBuilder, (env, tgt, src), kw))
+ return result
+
+added = None
+
+def generate(env):
+
+ from SCons.Script import AddOption, GetOption
+ global added
+ if not added:
+ added = 1
+ AddOption('--install-sandbox',
+ dest='install_sandbox',
+ type="string",
+ action="store",
+ help='A directory under which all installed files will be placed.')
+
+ global BaseInstallBuilder
+ if BaseInstallBuilder is None:
+ install_sandbox = GetOption('install_sandbox')
+ if install_sandbox:
+ target_factory = DESTDIR_factory(env, install_sandbox)
+ else:
+ target_factory = env.fs
+
+ BaseInstallBuilder = SCons.Builder.Builder(
+ action = install_action,
+ target_factory = target_factory.Entry,
+ source_factory = env.fs.Entry,
+ multi = 1,
+ emitter = [ add_targets_to_INSTALLED_FILES, ],
+ name = 'InstallBuilder')
+
+ env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper
+ env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper
+
+ # We'd like to initialize this doing something like the following,
+ # but there isn't yet support for a ${SOURCE.type} expansion that
+ # will print "file" or "directory" depending on what's being
+ # installed. For now we punt by not initializing it, and letting
+ # the stringFunc() that we put in the action fall back to the
+ # hand-crafted default string if it's not set.
+ #
+ #try:
+ # env['INSTALLSTR']
+ #except KeyError:
+ # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"'
+
+ try:
+ env['INSTALL']
+ except KeyError:
+ env['INSTALL'] = copyFunc
+
+def exists(env):
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml
new file mode 100644
index 0000000..c9eea00
--- /dev/null
+++ b/src/engine/SCons/Tool/install.xml
@@ -0,0 +1,52 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+
+<tool name="install">
+<summary>
+Sets construction variables for file
+and directory installation.
+</summary>
+<sets>
+INSTALL
+INSTALLSTR
+</sets>
+</tool>
+
+<builder name="Install">
+<summary>
+Installs one or more source files or directories
+in the specified target,
+which must be a directory.
+The names of the specified source files or directories
+remain the same within the destination directory.
+
+<example>
+env.Install('/usr/local/bin', source = ['foo', 'bar'])
+</example>
+</summary>
+</builder>
+
+<builder name="InstallAs">
+<summary>
+Installs one or more source files or directories
+to specific names,
+allowing changing a file or directory name
+as part of the installation.
+It is an error if the
+target
+and
+source
+arguments list different numbers of files or directories.
+
+<example>
+env.InstallAs(target = '/usr/local/bin/foo',
+ source = 'foo_debug')
+env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'],
+ source = ['libFOO.a', 'libBAR.a'])
+</example>
+</summary>
+</builder>
diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py
new file mode 100644
index 0000000..7add013
--- /dev/null
+++ b/src/engine/SCons/Tool/intelc.py
@@ -0,0 +1,490 @@
+"""SCons.Tool.icl
+
+Tool-specific initialization for the Intel C/C++ compiler.
+Supports Linux and Windows compilers, v7 and up.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/intelc.py 4577 2009/12/27 19:44:43 scons"
+
+import math, sys, os.path, glob, string, re
+
+is_windows = sys.platform == 'win32'
+is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or
+ (os.environ.has_key('PROCESSOR_ARCHITEW6432') and
+ os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64'))
+is_linux = sys.platform == 'linux2'
+is_mac = sys.platform == 'darwin'
+
+if is_windows:
+ import SCons.Tool.msvc
+elif is_linux:
+ import SCons.Tool.gcc
+elif is_mac:
+ import SCons.Tool.gcc
+import SCons.Util
+import SCons.Warnings
+
+# Exceptions for this tool
+class IntelCError(SCons.Errors.InternalError):
+ pass
+class MissingRegistryError(IntelCError): # missing registry entry
+ pass
+class MissingDirError(IntelCError): # dir not found
+ pass
+class NoRegistryModuleError(IntelCError): # can't read registry at all
+ pass
+
+def uniquify(s):
+ """Return a sequence containing only one copy of each unique element from input sequence s.
+ Does not preserve order.
+ Input sequence must be hashable (i.e. must be usable as a dictionary key)."""
+ u = {}
+ for x in s:
+ u[x] = 1
+ return u.keys()
+
+def linux_ver_normalize(vstr):
+ """Normalize a Linux compiler version number.
+ Intel changed from "80" to "9.0" in 2005, so we assume if the number
+ is greater than 60 it's an old-style number and otherwise new-style.
+ Always returns an old-style float like 80 or 90 for compatibility with Windows.
+ Shades of Y2K!"""
+ # Check for version number like 9.1.026: return 91.026
+ m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr)
+ if m:
+ vmaj,vmin,build = m.groups()
+ return float(vmaj) * 10 + float(vmin) + float(build) / 1000.;
+ else:
+ f = float(vstr)
+ if is_windows:
+ return f
+ else:
+ if f < 60: return f * 10.0
+ else: return f
+
+def check_abi(abi):
+ """Check for valid ABI (application binary interface) name,
+ and map into canonical one"""
+ if not abi:
+ return None
+ abi = abi.lower()
+ # valid_abis maps input name to canonical name
+ if is_windows:
+ valid_abis = {'ia32' : 'ia32',
+ 'x86' : 'ia32',
+ 'ia64' : 'ia64',
+ 'em64t' : 'em64t',
+ 'amd64' : 'em64t'}
+ if is_linux:
+ valid_abis = {'ia32' : 'ia32',
+ 'x86' : 'ia32',
+ 'x86_64' : 'x86_64',
+ 'em64t' : 'x86_64',
+ 'amd64' : 'x86_64'}
+ if is_mac:
+ valid_abis = {'ia32' : 'ia32',
+ 'x86' : 'ia32',
+ 'x86_64' : 'x86_64',
+ 'em64t' : 'x86_64'}
+ try:
+ abi = valid_abis[abi]
+ except KeyError:
+ raise SCons.Errors.UserError, \
+ "Intel compiler: Invalid ABI %s, valid values are %s"% \
+ (abi, valid_abis.keys())
+ return abi
+
+def vercmp(a, b):
+ """Compare strings as floats,
+ but Intel changed Linux naming convention at 9.0"""
+ return cmp(linux_ver_normalize(b), linux_ver_normalize(a))
+
+def get_version_from_list(v, vlist):
+ """See if we can match v (string) in vlist (list of strings)
+ Linux has to match in a fuzzy way."""
+ if is_windows:
+ # Simple case, just find it in the list
+ if v in vlist: return v
+ else: return None
+ else:
+ # Fuzzy match: normalize version number first, but still return
+ # original non-normalized form.
+ fuzz = 0.001
+ for vi in vlist:
+ if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz:
+ return vi
+ # Not found
+ return None
+
+def get_intel_registry_value(valuename, version=None, abi=None):
+ """
+ Return a value from the Intel compiler registry tree. (Windows only)
+ """
+ # Open the key:
+ if is_win64:
+ K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper()
+ else:
+ K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper()
+ try:
+ k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K)
+ except SCons.Util.RegError:
+ raise MissingRegistryError, \
+ "%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)
+
+ # Get the value:
+ try:
+ v = SCons.Util.RegQueryValueEx(k, valuename)[0]
+ return v # or v.encode('iso-8859-1', 'replace') to remove unicode?
+ except SCons.Util.RegError:
+ raise MissingRegistryError, \
+ "%s\\%s was not found in the registry."%(K, valuename)
+
+
+def get_all_compiler_versions():
+ """Returns a sorted list of strings, like "70" or "80" or "9.0"
+ with most recent compiler version first.
+ """
+ versions=[]
+ if is_windows:
+ if is_win64:
+ keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++'
+ else:
+ keyname = 'Software\\Intel\\Compilers\\C++'
+ try:
+ k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
+ keyname)
+ except WindowsError:
+ return []
+ i = 0
+ versions = []
+ try:
+ while i < 100:
+ subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError
+ # Check that this refers to an existing dir.
+ # This is not 100% perfect but should catch common
+ # installation issues like when the compiler was installed
+ # and then the install directory deleted or moved (rather
+ # than uninstalling properly), so the registry values
+ # are still there.
+ ok = False
+ for try_abi in ('IA32', 'IA32e', 'IA64', 'EM64T'):
+ try:
+ d = get_intel_registry_value('ProductDir', subkey, try_abi)
+ except MissingRegistryError:
+ continue # not found in reg, keep going
+ if os.path.exists(d): ok = True
+ if ok:
+ versions.append(subkey)
+ else:
+ try:
+ # Registry points to nonexistent dir. Ignore this
+ # version.
+ value = get_intel_registry_value('ProductDir', subkey, 'IA32')
+ except MissingRegistryError, e:
+
+ # Registry key is left dangling (potentially
+ # after uninstalling).
+
+ print \
+ "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \
+ "scons: *** It seems that the compiler was uninstalled and that the registry\n" \
+ "scons: *** was not cleaned up properly.\n" % subkey
+ else:
+ print "scons: *** Ignoring "+str(value)
+
+ i = i + 1
+ except EnvironmentError:
+ # no more subkeys
+ pass
+ elif is_linux:
+ for d in glob.glob('/opt/intel_cc_*'):
+ # Typical dir here is /opt/intel_cc_80.
+ m = re.search(r'cc_(.*)$', d)
+ if m:
+ versions.append(m.group(1))
+ for d in glob.glob('/opt/intel/cc*/*'):
+ # Typical dir here is /opt/intel/cc/9.0 for IA32,
+ # /opt/intel/cce/9.0 for EMT64 (AMD64)
+ m = re.search(r'([0-9.]+)$', d)
+ if m:
+ versions.append(m.group(1))
+ elif is_mac:
+ for d in glob.glob('/opt/intel/cc*/*'):
+ # Typical dir here is /opt/intel/cc/9.0 for IA32,
+ # /opt/intel/cce/9.0 for EMT64 (AMD64)
+ m = re.search(r'([0-9.]+)$', d)
+ if m:
+ versions.append(m.group(1))
+ versions = uniquify(versions) # remove dups
+ versions.sort(vercmp)
+ return versions
+
+def get_intel_compiler_top(version, abi):
+ """
+ Return the main path to the top-level dir of the Intel compiler,
+ using the given version.
+ The compiler will be in <top>/bin/icl.exe (icc on linux),
+ the include dir is <top>/include, etc.
+ """
+
+ if is_windows:
+ if not SCons.Util.can_read_reg:
+ raise NoRegistryModuleError, "No Windows registry module was found"
+ top = get_intel_registry_value('ProductDir', version, abi)
+ # pre-11, icl was in Bin. 11 and later, it's in Bin/<abi> apparently.
+ if not os.path.exists(os.path.join(top, "Bin", "icl.exe")) \
+ and not os.path.exists(os.path.join(top, "Bin", abi, "icl.exe")):
+ raise MissingDirError, \
+ "Can't find Intel compiler in %s"%(top)
+ elif is_mac or is_linux:
+ # first dir is new (>=9.0) style, second is old (8.0) style.
+ dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s')
+ if abi == 'x86_64':
+ dirs=('/opt/intel/cce/%s',) # 'e' stands for 'em64t', aka x86_64 aka amd64
+ top=None
+ for d in dirs:
+ if os.path.exists(os.path.join(d%version, "bin", "icc")):
+ top = d%version
+ break
+ if not top:
+ raise MissingDirError, \
+ "Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi)
+ return top
+
+
+def generate(env, version=None, abi=None, topdir=None, verbose=0):
+ """Add Builders and construction variables for Intel C/C++ compiler
+ to an Environment.
+ args:
+ version: (string) compiler version to use, like "80"
+ abi: (string) 'win32' or whatever Itanium version wants
+ topdir: (string) compiler top dir, like
+ "c:\Program Files\Intel\Compiler70"
+ If topdir is used, version and abi are ignored.
+ verbose: (int) if >0, prints compiler version used.
+ """
+ if not (is_mac or is_linux or is_windows):
+ # can't handle this platform
+ return
+
+ if is_windows:
+ SCons.Tool.msvc.generate(env)
+ elif is_linux:
+ SCons.Tool.gcc.generate(env)
+ elif is_mac:
+ SCons.Tool.gcc.generate(env)
+
+ # if version is unspecified, use latest
+ vlist = get_all_compiler_versions()
+ if not version:
+ if vlist:
+ version = vlist[0]
+ else:
+ # User may have specified '90' but we need to get actual dirname '9.0'.
+ # get_version_from_list does that mapping.
+ v = get_version_from_list(version, vlist)
+ if not v:
+ raise SCons.Errors.UserError, \
+ "Invalid Intel compiler version %s: "%version + \
+ "installed versions are %s"%(', '.join(vlist))
+ version = v
+
+ # if abi is unspecified, use ia32
+ # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here)
+ abi = check_abi(abi)
+ if abi is None:
+ if is_mac or is_linux:
+ # Check if we are on 64-bit linux, default to 64 then.
+ uname_m = os.uname()[4]
+ if uname_m == 'x86_64':
+ abi = 'x86_64'
+ else:
+ abi = 'ia32'
+ else:
+ if is_win64:
+ abi = 'em64t'
+ else:
+ abi = 'ia32'
+
+ if version and not topdir:
+ try:
+ topdir = get_intel_compiler_top(version, abi)
+ except (SCons.Util.RegError, IntelCError):
+ topdir = None
+
+ if not topdir:
+ # Normally this is an error, but it might not be if the compiler is
+ # on $PATH and the user is importing their env.
+ class ICLTopDirWarning(SCons.Warnings.Warning):
+ pass
+ if (is_mac or is_linux) and not env.Detect('icc') or \
+ is_windows and not env.Detect('icl'):
+
+ SCons.Warnings.enableWarningClass(ICLTopDirWarning)
+ SCons.Warnings.warn(ICLTopDirWarning,
+ "Failed to find Intel compiler for version='%s', abi='%s'"%
+ (str(version), str(abi)))
+ else:
+ # should be cleaned up to say what this other version is
+ # since in this case we have some other Intel compiler installed
+ SCons.Warnings.enableWarningClass(ICLTopDirWarning)
+ SCons.Warnings.warn(ICLTopDirWarning,
+ "Can't find Intel compiler top dir for version='%s', abi='%s'"%
+ (str(version), str(abi)))
+
+ if topdir:
+ if verbose:
+ print "Intel C compiler: using version %s (%g), abi %s, in '%s'"%\
+ (repr(version), linux_ver_normalize(version),abi,topdir)
+ if is_linux:
+ # Show the actual compiler version by running the compiler.
+ os.system('%s/bin/icc --version'%topdir)
+ if is_mac:
+ # Show the actual compiler version by running the compiler.
+ os.system('%s/bin/icc --version'%topdir)
+
+ env['INTEL_C_COMPILER_TOP'] = topdir
+ if is_linux:
+ paths={'INCLUDE' : 'include',
+ 'LIB' : 'lib',
+ 'PATH' : 'bin',
+ 'LD_LIBRARY_PATH' : 'lib'}
+ for p in paths.keys():
+ env.PrependENVPath(p, os.path.join(topdir, paths[p]))
+ if is_mac:
+ paths={'INCLUDE' : 'include',
+ 'LIB' : 'lib',
+ 'PATH' : 'bin',
+ 'LD_LIBRARY_PATH' : 'lib'}
+ for p in paths.keys():
+ env.PrependENVPath(p, os.path.join(topdir, paths[p]))
+ if is_windows:
+ # env key reg valname default subdir of top
+ paths=(('INCLUDE', 'IncludeDir', 'Include'),
+ ('LIB' , 'LibDir', 'Lib'),
+ ('PATH' , 'BinDir', 'Bin'))
+ # We are supposed to ignore version if topdir is set, so set
+ # it to the emptry string if it's not already set.
+ if version is None:
+ version = ''
+ # Each path has a registry entry, use that or default to subdir
+ for p in paths:
+ try:
+ path=get_intel_registry_value(p[1], version, abi)
+ # These paths may have $(ICInstallDir)
+ # which needs to be substituted with the topdir.
+ path=path.replace('$(ICInstallDir)', topdir + os.sep)
+ except IntelCError:
+ # Couldn't get it from registry: use default subdir of topdir
+ env.PrependENVPath(p[0], os.path.join(topdir, p[2]))
+ else:
+ env.PrependENVPath(p[0], string.split(path, os.pathsep))
+ # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]]))
+
+ if is_windows:
+ env['CC'] = 'icl'
+ env['CXX'] = 'icl'
+ env['LINK'] = 'xilink'
+ else:
+ env['CC'] = 'icc'
+ env['CXX'] = 'icpc'
+ # Don't reset LINK here;
+ # use smart_link which should already be here from link.py.
+ #env['LINK'] = '$CC'
+ env['AR'] = 'xiar'
+ env['LD'] = 'xild' # not used by default
+
+ # This is not the exact (detailed) compiler version,
+ # just the major version as determined above or specified
+ # by the user. It is a float like 80 or 90, in normalized form for Linux
+ # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0)
+ if version:
+ env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version)
+
+ if is_windows:
+ # Look for license file dir
+ # in system environment, registry, and default location.
+ envlicdir = os.environ.get("INTEL_LICENSE_FILE", '')
+ K = ('SOFTWARE\Intel\Licenses')
+ try:
+ k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K)
+ reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0]
+ except (AttributeError, SCons.Util.RegError):
+ reglicdir = ""
+ defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses'
+
+ licdir = None
+ for ld in [envlicdir, reglicdir]:
+ # If the string contains an '@', then assume it's a network
+ # license (port@system) and good by definition.
+ if ld and (string.find(ld, '@') != -1 or os.path.exists(ld)):
+ licdir = ld
+ break
+ if not licdir:
+ licdir = defaultlicdir
+ if not os.path.exists(licdir):
+ class ICLLicenseDirWarning(SCons.Warnings.Warning):
+ pass
+ SCons.Warnings.enableWarningClass(ICLLicenseDirWarning)
+ SCons.Warnings.warn(ICLLicenseDirWarning,
+ "Intel license dir was not found."
+ " Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)."
+ " Using the default path as a last resort."
+ % (envlicdir, reglicdir, defaultlicdir))
+ env['ENV']['INTEL_LICENSE_FILE'] = licdir
+
+def exists(env):
+ if not (is_mac or is_linux or is_windows):
+ # can't handle this platform
+ return 0
+
+ try:
+ versions = get_all_compiler_versions()
+ except (SCons.Util.RegError, IntelCError):
+ versions = None
+ detected = versions is not None and len(versions) > 0
+ if not detected:
+ # try env.Detect, maybe that will work
+ if is_windows:
+ return env.Detect('icl')
+ elif is_linux:
+ return env.Detect('icc')
+ elif is_mac:
+ return env.Detect('icc')
+ return detected
+
+# end of file
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/intelc.xml b/src/engine/SCons/Tool/intelc.xml
new file mode 100644
index 0000000..61fb8d8
--- /dev/null
+++ b/src/engine/SCons/Tool/intelc.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="intelc">
+<summary>
+Sets construction variables for the Intel C/C++ compiler
+(Linux and Windows, version 7 and later).
+Calls the &t-gcc; or &t-msvc;
+(on Linux and Windows, respectively)
+to set underlying variables.
+</summary>
+<sets>
+CC
+CXX
+LINK
+AR
+<!--LD-->
+INTEL_C_COMPILER_VERSION
+</sets>
+<uses>
+</uses>
+</tool>
+
+<cvar name="INTEL_C_COMPILER_VERSION">
+<summary>
+Set by the "intelc" Tool
+to the major version number of the Intel C compiler
+selected for use.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/ipkg.py b/src/engine/SCons/Tool/ipkg.py
new file mode 100644
index 0000000..b026938
--- /dev/null
+++ b/src/engine/SCons/Tool/ipkg.py
@@ -0,0 +1,71 @@
+"""SCons.Tool.ipkg
+
+Tool-specific initialization for ipkg.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+The ipkg tool calls the ipkg-build. Its only argument should be the
+packages fake_root.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/ipkg.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import string
+
+import SCons.Builder
+
+def generate(env):
+ """Add Builders and construction variables for ipkg to an Environment."""
+ try:
+ bld = env['BUILDERS']['Ipkg']
+ except KeyError:
+ bld = SCons.Builder.Builder( action = '$IPKGCOM',
+ suffix = '$IPKGSUFFIX',
+ source_scanner = None,
+ target_scanner = None)
+ env['BUILDERS']['Ipkg'] = bld
+
+ env['IPKG'] = 'ipkg-build'
+ env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}'
+ # TODO(1.5)
+ #env['IPKGUSER'] = os.popen('id -un').read().strip()
+ #env['IPKGGROUP'] = os.popen('id -gn').read().strip()
+ env['IPKGUSER'] = string.strip(os.popen('id -un').read())
+ env['IPKGGROUP'] = string.strip(os.popen('id -gn').read())
+ env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP')
+ env['IPKGSUFFIX'] = '.ipk'
+
+def exists(env):
+ return env.Detect('ipkg-build')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py
new file mode 100644
index 0000000..1c086c2
--- /dev/null
+++ b/src/engine/SCons/Tool/jar.py
@@ -0,0 +1,110 @@
+"""SCons.Tool.jar
+
+Tool-specific initialization for jar.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/jar.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Subst
+import SCons.Util
+
+def jarSources(target, source, env, for_signature):
+ """Only include sources that are not a manifest file."""
+ try:
+ env['JARCHDIR']
+ except KeyError:
+ jarchdir_set = False
+ else:
+ jarchdir_set = True
+ jarchdir = env.subst('$JARCHDIR', target=target, source=source)
+ if jarchdir:
+ jarchdir = env.fs.Dir(jarchdir)
+ result = []
+ for src in source:
+ contents = src.get_text_contents()
+ if contents[:16] != "Manifest-Version":
+ if jarchdir_set:
+ _chdir = jarchdir
+ else:
+ try:
+ _chdir = src.attributes.java_classdir
+ except AttributeError:
+ _chdir = None
+ if _chdir:
+ # If we are changing the dir with -C, then sources should
+ # be relative to that directory.
+ src = SCons.Subst.Literal(src.get_path(_chdir))
+ result.append('-C')
+ result.append(_chdir)
+ result.append(src)
+ return result
+
+def jarManifest(target, source, env, for_signature):
+ """Look in sources for a manifest file, if any."""
+ for src in source:
+ contents = src.get_text_contents()
+ if contents[:16] == "Manifest-Version":
+ return src
+ return ''
+
+def jarFlags(target, source, env, for_signature):
+ """If we have a manifest, make sure that the 'm'
+ flag is specified."""
+ jarflags = env.subst('$JARFLAGS', target=target, source=source)
+ for src in source:
+ contents = src.get_text_contents()
+ if contents[:16] == "Manifest-Version":
+ if not 'm' in jarflags:
+ return jarflags + 'm'
+ break
+ return jarflags
+
+def generate(env):
+ """Add Builders and construction variables for jar to an Environment."""
+ SCons.Tool.CreateJarBuilder(env)
+
+ env['JAR'] = 'jar'
+ env['JARFLAGS'] = SCons.Util.CLVar('cf')
+ env['_JARFLAGS'] = jarFlags
+ env['_JARMANIFEST'] = jarManifest
+ env['_JARSOURCES'] = jarSources
+ env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES'
+ env['JARCOM'] = "${TEMPFILE('$_JARCOM')}"
+ env['JARSUFFIX'] = '.jar'
+
+def exists(env):
+ return env.Detect('jar')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/jar.xml b/src/engine/SCons/Tool/jar.xml
new file mode 100644
index 0000000..d6a986d
--- /dev/null
+++ b/src/engine/SCons/Tool/jar.xml
@@ -0,0 +1,110 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="jar">
+<summary>
+Sets construction variables for the &jar; utility.
+</summary>
+<sets>
+JAR
+JARFLAGS
+JARCOM
+JARSUFFIX
+</sets>
+<uses>
+JARCOMSTR
+</uses>
+</tool>
+
+<builder name="Jar">
+<summary>
+Builds a Java archive (<filename>.jar</filename>) file
+from the specified list of sources.
+Any directories in the source list
+will be searched for <filename>.class</filename> files).
+Any <filename>.java</filename> files in the source list
+will be compiled to <filename>.class</filename> files
+by calling the &b-link-Java; Builder.
+
+If the &cv-link-JARCHDIR; value is set, the
+&jar;
+command will change to the specified directory using the
+<option>-C</option>
+option.
+If &cv-JARCHDIR; is not set explicitly,
+&SCons; will use the top of any subdirectory tree
+in which Java <filename>.class</filename>
+were built by the &b-link-Java; Builder.
+
+If the contents any of the source files begin with the string
+<literal>Manifest-Version</literal>,
+the file is assumed to be a manifest
+and is passed to the
+&jar;
+command with the
+<option>m</option>
+option set.
+
+<example>
+env.Jar(target = 'foo.jar', source = 'classes')
+
+env.Jar(target = 'bar.jar',
+ source = ['bar1.java', 'bar2.java'])
+</example>
+</summary>
+</builder>
+
+<cvar name="JAR">
+<summary>
+The Java archive tool.
+</summary>
+</cvar>
+
+<cvar name="JARCHDIR">
+<summary>
+The directory to which the Java archive tool should change
+(using the
+<option>-C</option>
+option).
+</summary>
+</cvar>
+
+<cvar name="JARCOM">
+<summary>
+The command line used to call the Java archive tool.
+</summary>
+</cvar>
+
+<cvar name="JARCOMSTR">
+<summary>
+The string displayed when the Java archive tool
+is called
+If this is not set, then &cv-link-JARCOM; (the command line) is displayed.
+
+<example>
+env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="JARFLAGS">
+<summary>
+General options passed to the Java archive tool.
+By default this is set to
+<option>cf</option>
+to create the necessary
+<command>jar</command>
+file.
+</summary>
+</cvar>
+
+<cvar name="JARSUFFIX">
+<summary>
+The suffix for Java archives:
+<filename>.jar</filename>
+by default.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py
new file mode 100644
index 0000000..e51afe2
--- /dev/null
+++ b/src/engine/SCons/Tool/javac.py
@@ -0,0 +1,234 @@
+"""SCons.Tool.javac
+
+Tool-specific initialization for javac.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/javac.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+
+import SCons.Action
+import SCons.Builder
+from SCons.Node.FS import _my_normcase
+from SCons.Tool.JavaCommon import parse_java_file
+import SCons.Util
+
+def classname(path):
+ """Turn a string (path name) into a Java class name."""
+ return string.replace(os.path.normpath(path), os.sep, '.')
+
+def emit_java_classes(target, source, env):
+ """Create and return lists of source java files
+ and their corresponding target class files.
+ """
+ java_suffix = env.get('JAVASUFFIX', '.java')
+ class_suffix = env.get('JAVACLASSSUFFIX', '.class')
+
+ target[0].must_be_same(SCons.Node.FS.Dir)
+ classdir = target[0]
+
+ s = source[0].rentry().disambiguate()
+ if isinstance(s, SCons.Node.FS.File):
+ sourcedir = s.dir.rdir()
+ elif isinstance(s, SCons.Node.FS.Dir):
+ sourcedir = s.rdir()
+ else:
+ raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__)
+
+ slist = []
+ js = _my_normcase(java_suffix)
+ find_java = lambda n, js=js, ljs=len(js): _my_normcase(n[-ljs:]) == js
+ for entry in source:
+ entry = entry.rentry().disambiguate()
+ if isinstance(entry, SCons.Node.FS.File):
+ slist.append(entry)
+ elif isinstance(entry, SCons.Node.FS.Dir):
+ result = SCons.Util.OrderedDict()
+ def visit(arg, dirname, names, fj=find_java, dirnode=entry.rdir()):
+ java_files = filter(fj, names)
+ # The on-disk entries come back in arbitrary order. Sort
+ # them so our target and source lists are determinate.
+ java_files.sort()
+ mydir = dirnode.Dir(dirname)
+ java_paths = map(lambda f, d=mydir: d.File(f), java_files)
+ for jp in java_paths:
+ arg[jp] = True
+
+ os.path.walk(entry.rdir().get_abspath(), visit, result)
+ entry.walk(visit, result)
+
+ slist.extend(result.keys())
+ else:
+ raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__)
+
+ version = env.get('JAVAVERSION', '1.4')
+ full_tlist = []
+ for f in slist:
+ tlist = []
+ source_file_based = True
+ pkg_dir = None
+ if not f.is_derived():
+ pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version)
+ if classes:
+ source_file_based = False
+ if pkg_dir:
+ d = target[0].Dir(pkg_dir)
+ p = pkg_dir + os.sep
+ else:
+ d = target[0]
+ p = ''
+ for c in classes:
+ t = d.File(c + class_suffix)
+ t.attributes.java_classdir = classdir
+ t.attributes.java_sourcedir = sourcedir
+ t.attributes.java_classname = classname(p + c)
+ tlist.append(t)
+
+ if source_file_based:
+ base = f.name[:-len(java_suffix)]
+ if pkg_dir:
+ t = target[0].Dir(pkg_dir).File(base + class_suffix)
+ else:
+ t = target[0].File(base + class_suffix)
+ t.attributes.java_classdir = classdir
+ t.attributes.java_sourcedir = f.dir
+ t.attributes.java_classname = classname(base)
+ tlist.append(t)
+
+ for t in tlist:
+ t.set_specific_source([f])
+
+ full_tlist.extend(tlist)
+
+ return full_tlist, slist
+
+JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
+
+JavaBuilder = SCons.Builder.Builder(action = JavaAction,
+ emitter = emit_java_classes,
+ target_factory = SCons.Node.FS.Entry,
+ source_factory = SCons.Node.FS.Entry)
+
+class pathopt:
+ """
+ Callable object for generating javac-style path options from
+ a construction variable (e.g. -classpath, -sourcepath).
+ """
+ def __init__(self, opt, var, default=None):
+ self.opt = opt
+ self.var = var
+ self.default = default
+
+ def __call__(self, target, source, env, for_signature):
+ path = env[self.var]
+ if path and not SCons.Util.is_List(path):
+ path = [path]
+ if self.default:
+ path = path + [ env[self.default] ]
+ if path:
+ return [self.opt, string.join(path, os.pathsep)]
+ #return self.opt + " " + string.join(path, os.pathsep)
+ else:
+ return []
+ #return ""
+
+def Java(env, target, source, *args, **kw):
+ """
+ A pseudo-Builder wrapper around the separate JavaClass{File,Dir}
+ Builders.
+ """
+ if not SCons.Util.is_List(target):
+ target = [target]
+ if not SCons.Util.is_List(source):
+ source = [source]
+
+ # Pad the target list with repetitions of the last element in the
+ # list so we have a target for every source element.
+ target = target + ([target[-1]] * (len(source) - len(target)))
+
+ java_suffix = env.subst('$JAVASUFFIX')
+ result = []
+
+ for t, s in zip(target, source):
+ if isinstance(s, SCons.Node.FS.Base):
+ if isinstance(s, SCons.Node.FS.File):
+ b = env.JavaClassFile
+ else:
+ b = env.JavaClassDir
+ else:
+ if os.path.isfile(s):
+ b = env.JavaClassFile
+ elif os.path.isdir(s):
+ b = env.JavaClassDir
+ elif s[-len(java_suffix):] == java_suffix:
+ b = env.JavaClassFile
+ else:
+ b = env.JavaClassDir
+ result.extend(apply(b, (t, s) + args, kw))
+
+ return result
+
+def generate(env):
+ """Add Builders and construction variables for javac to an Environment."""
+ java_file = SCons.Tool.CreateJavaFileBuilder(env)
+ java_class = SCons.Tool.CreateJavaClassFileBuilder(env)
+ java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env)
+ java_class.add_emitter(None, emit_java_classes)
+ java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes)
+ java_class_dir.emitter = emit_java_classes
+
+ env.AddMethod(Java)
+
+ env['JAVAC'] = 'javac'
+ env['JAVACFLAGS'] = SCons.Util.CLVar('')
+ env['JAVABOOTCLASSPATH'] = []
+ env['JAVACLASSPATH'] = []
+ env['JAVASOURCEPATH'] = []
+ env['_javapathopt'] = pathopt
+ env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} '
+ env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} '
+ env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} '
+ env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}'
+ env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES'
+ env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}"
+ env['JAVACLASSSUFFIX'] = '.class'
+ env['JAVASUFFIX'] = '.java'
+
+def exists(env):
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/javac.xml b/src/engine/SCons/Tool/javac.xml
new file mode 100644
index 0000000..a55e514
--- /dev/null
+++ b/src/engine/SCons/Tool/javac.xml
@@ -0,0 +1,224 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="javac">
+<summary>
+Sets construction variables for the &javac; compiler.
+</summary>
+<sets>
+JAVAC
+JAVACFLAGS
+JAVACCOM
+JAVACLASSSUFFIX
+JAVASUFFIX
+JAVABOOTCLASSPATH
+JAVACLASSPATH
+JAVASOURCEPATH
+</sets>
+<uses>
+JAVACCOMSTR
+</uses>
+</tool>
+
+<builder name="Java">
+<summary>
+Builds one or more Java class files.
+The sources may be any combination of explicit
+<filename>.java</filename> files,
+or directory trees which will be scanned
+for <filename>.java</filename> files.
+
+SCons will parse each source <filename>.java</filename> file
+to find the classes
+(including inner classes)
+defined within that file,
+and from that figure out the
+target <filename>.class</filename> files that will be created.
+The class files will be placed underneath
+the specified target directory.
+
+SCons will also search each Java file
+for the Java package name,
+which it assumes can be found on a line
+beginning with the string
+<literal>package</literal>
+in the first column;
+the resulting <filename>.class</filename> files
+will be placed in a directory reflecting
+the specified package name.
+For example,
+the file
+<filename>Foo.java</filename>
+defining a single public
+<classname>Foo</classname>
+class and
+containing a package name of
+<classname>sub.dir</classname>
+will generate a corresponding
+<filename>sub/dir/Foo.class</filename>
+class file.
+
+Examples:
+
+<example>
+env.Java(target = 'classes', source = 'src')
+env.Java(target = 'classes', source = ['src1', 'src2'])
+env.Java(target = 'classes', source = ['File1.java', 'File2.java'])
+</example>
+
+Java source files can use the native encoding for the underlying OS.
+Since SCons compiles in simple ASCII mode by default,
+the compiler will generate warnings about unmappable characters,
+which may lead to errors as the file is processed further.
+In this case, the user must specify the <literal>LANG</literal>
+environment variable to tell the compiler what encoding is used.
+For portibility, it's best if the encoding is hard-coded
+so that the compile will work if it is done on a system
+with a different encoding.
+
+<example>
+env = Environment()
+env['ENV']['LANG'] = 'en_GB.UTF-8'
+</example>
+</summary>
+</builder>
+
+<cvar name="JAVABOOTCLASSPATH">
+<summary>
+Specifies the list of directories that
+will be added to the
+&javac; command line
+via the <option>-bootclasspath</option> option.
+The individual directory names will be
+separated by the operating system's path separate character
+(<filename>:</filename> on UNIX/Linux/POSIX,
+<filename>;</filename> on Windows).
+</summary>
+</cvar>
+
+<cvar name="JAVAC">
+<summary>
+The Java compiler.
+</summary>
+</cvar>
+
+<cvar name="JAVACCOM">
+<summary>
+The command line used to compile a directory tree containing
+Java source files to
+corresponding Java class files.
+Any options specified in the &cv-link-JAVACFLAGS; construction variable
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="JAVACCOMSTR">
+<summary>
+The string displayed when compiling
+a directory tree of Java source files to
+corresponding Java class files.
+If this is not set, then &cv-link-JAVACCOM; (the command line) is displayed.
+
+<example>
+env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="JAVACFLAGS">
+<summary>
+General options that are passed to the Java compiler.
+</summary>
+</cvar>
+
+<cvar name="JAVACLASSDIR">
+<summary>
+The directory in which Java class files may be found.
+This is stripped from the beginning of any Java .class
+file names supplied to the
+<literal>JavaH</literal>
+builder.
+</summary>
+</cvar>
+
+<cvar name="JAVACLASSPATH">
+<summary>
+Specifies the list of directories that
+will be searched for Java
+<filename>.class</filename> file.
+The directories in this list will be added to the
+&javac; and &javah; command lines
+via the <option>-classpath</option> option.
+The individual directory names will be
+separated by the operating system's path separate character
+(<filename>:</filename> on UNIX/Linux/POSIX,
+<filename>;</filename> on Windows).
+
+Note that this currently just adds the specified
+directory via the <option>-classpath</option> option.
+&SCons; does not currently search the
+&cv-JAVACLASSPATH; directories for dependency
+<filename>.class</filename> files.
+</summary>
+</cvar>
+
+<cvar name="JAVACLASSSUFFIX">
+<summary>
+The suffix for Java class files;
+<filename>.class</filename>
+by default.
+</summary>
+</cvar>
+
+<cvar name="JAVASOURCEPATH">
+<summary>
+Specifies the list of directories that
+will be searched for input
+<filename>.java</filename> file.
+The directories in this list will be added to the
+&javac; command line
+via the <option>-sourcepath</option> option.
+The individual directory names will be
+separated by the operating system's path separate character
+(<filename>:</filename> on UNIX/Linux/POSIX,
+<filename>;</filename> on Windows).
+
+Note that this currently just adds the specified
+directory via the <option>-sourcepath</option> option.
+&SCons; does not currently search the
+&cv-JAVASOURCEPATH; directories for dependency
+<filename>.java</filename> files.
+</summary>
+</cvar>
+
+<cvar name="JAVASUFFIX">
+<summary>
+The suffix for Java files;
+<filename>.java</filename>
+by default.
+</summary>
+</cvar>
+
+<cvar name="JAVAVERSION">
+<summary>
+Specifies the Java version being used by the &b-Java; builder.
+This is <emphasis>not</emphasis> currently used to select one
+version of the Java compiler vs. another.
+Instead, you should set this to specify the version of Java
+supported by your &javac; compiler.
+The default is <literal>1.4</literal>.
+
+This is sometimes necessary because
+Java 1.5 changed the file names that are created
+for nested anonymous inner classes,
+which can cause a mismatch with the files
+that &SCons; expects will be generated by the &javac; compiler.
+Setting &cv-JAVAVERSION; to <literal>1.5</literal>
+(or <literal>1.6</literal>, as appropriate)
+can make &SCons; realize that a Java 1.5 or 1.6
+build is actually up to date.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/javah.py b/src/engine/SCons/Tool/javah.py
new file mode 100644
index 0000000..0a54005
--- /dev/null
+++ b/src/engine/SCons/Tool/javah.py
@@ -0,0 +1,138 @@
+"""SCons.Tool.javah
+
+Tool-specific initialization for javah.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/javah.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+
+import SCons.Action
+import SCons.Builder
+import SCons.Node.FS
+import SCons.Tool.javac
+import SCons.Util
+
+def emit_java_headers(target, source, env):
+ """Create and return lists of Java stub header files that will
+ be created from a set of class files.
+ """
+ class_suffix = env.get('JAVACLASSSUFFIX', '.class')
+ classdir = env.get('JAVACLASSDIR')
+
+ if not classdir:
+ try:
+ s = source[0]
+ except IndexError:
+ classdir = '.'
+ else:
+ try:
+ classdir = s.attributes.java_classdir
+ except AttributeError:
+ classdir = '.'
+ classdir = env.Dir(classdir).rdir()
+
+ if str(classdir) == '.':
+ c_ = None
+ else:
+ c_ = str(classdir) + os.sep
+
+ slist = []
+ for src in source:
+ try:
+ classname = src.attributes.java_classname
+ except AttributeError:
+ classname = str(src)
+ if c_ and classname[:len(c_)] == c_:
+ classname = classname[len(c_):]
+ if class_suffix and classname[-len(class_suffix):] == class_suffix:
+ classname = classname[:-len(class_suffix)]
+ classname = SCons.Tool.javac.classname(classname)
+ s = src.rfile()
+ s.attributes.java_classname = classname
+ slist.append(s)
+
+ s = source[0].rfile()
+ if not hasattr(s.attributes, 'java_classdir'):
+ s.attributes.java_classdir = classdir
+
+ if target[0].__class__ is SCons.Node.FS.File:
+ tlist = target
+ else:
+ if not isinstance(target[0], SCons.Node.FS.Dir):
+ target[0].__class__ = SCons.Node.FS.Dir
+ target[0]._morph()
+ tlist = []
+ for s in source:
+ fname = string.replace(s.attributes.java_classname, '.', '_') + '.h'
+ t = target[0].File(fname)
+ t.attributes.java_lookupdir = target[0]
+ tlist.append(t)
+
+ return tlist, source
+
+def JavaHOutFlagGenerator(target, source, env, for_signature):
+ try:
+ t = target[0]
+ except (AttributeError, IndexError, TypeError):
+ t = target
+ try:
+ return '-d ' + str(t.attributes.java_lookupdir)
+ except AttributeError:
+ return '-o ' + str(t)
+
+def getJavaHClassPath(env,target, source, for_signature):
+ path = "${SOURCE.attributes.java_classdir}"
+ if env.has_key('JAVACLASSPATH') and env['JAVACLASSPATH']:
+ path = SCons.Util.AppendPath(path, env['JAVACLASSPATH'])
+ return "-classpath %s" % (path)
+
+def generate(env):
+ """Add Builders and construction variables for javah to an Environment."""
+ java_javah = SCons.Tool.CreateJavaHBuilder(env)
+ java_javah.emitter = emit_java_headers
+
+ env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator
+ env['JAVAH'] = 'javah'
+ env['JAVAHFLAGS'] = SCons.Util.CLVar('')
+ env['_JAVAHCLASSPATH'] = getJavaHClassPath
+ env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}'
+ env['JAVACLASSSUFFIX'] = '.class'
+
+def exists(env):
+ return env.Detect('javah')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/javah.xml b/src/engine/SCons/Tool/javah.xml
new file mode 100644
index 0000000..8a31662
--- /dev/null
+++ b/src/engine/SCons/Tool/javah.xml
@@ -0,0 +1,100 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="javah">
+<summary>
+Sets construction variables for the &javah; tool.
+</summary>
+<sets>
+JAVAH
+JAVAHFLAGS
+JAVAHCOM
+JAVACLASSSUFFIX
+</sets>
+<uses>
+JAVAHCOMSTR
+JAVACLASSPATH
+</uses>
+</tool>
+
+<builder name="JavaH">
+<summary>
+Builds C header and source files for
+implementing Java native methods.
+The target can be either a directory
+in which the header files will be written,
+or a header file name which
+will contain all of the definitions.
+The source can be the names of <filename>.class</filename> files,
+the names of <filename>.java</filename> files
+to be compiled into <filename>.class</filename> files
+by calling the &b-link-Java; builder method,
+or the objects returned from the
+&b-Java;
+builder method.
+
+If the construction variable
+&cv-link-JAVACLASSDIR;
+is set, either in the environment
+or in the call to the
+&b-JavaH;
+builder method itself,
+then the value of the variable
+will be stripped from the
+beginning of any <filename>.class</filename> file names.
+
+Examples:
+
+<example>
+# builds java_native.h
+classes = env.Java(target = 'classdir', source = 'src')
+env.JavaH(target = 'java_native.h', source = classes)
+
+# builds include/package_foo.h and include/package_bar.h
+env.JavaH(target = 'include',
+ source = ['package/foo.class', 'package/bar.class'])
+
+# builds export/foo.h and export/bar.h
+env.JavaH(target = 'export',
+ source = ['classes/foo.class', 'classes/bar.class'],
+ JAVACLASSDIR = 'classes')
+</example>
+</summary>
+</builder>
+
+<cvar name="JAVAH">
+<summary>
+The Java generator for C header and stub files.
+</summary>
+</cvar>
+
+<cvar name="JAVAHCOM">
+<summary>
+The command line used to generate C header and stub files
+from Java classes.
+Any options specified in the &cv-link-JAVAHFLAGS; construction variable
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="JAVAHCOMSTR">
+<summary>
+The string displayed when C header and stub files
+are generated from Java classes.
+If this is not set, then &cv-link-JAVAHCOM; (the command line) is displayed.
+
+<example>
+env = Environment(JAVAHCOMSTR = "Generating header/stub file(s) $TARGETS from $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="JAVAHFLAGS">
+<summary>
+General options passed to the C header and stub file generator
+for Java classes.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/latex.py b/src/engine/SCons/Tool/latex.py
new file mode 100644
index 0000000..d5b75ae
--- /dev/null
+++ b/src/engine/SCons/Tool/latex.py
@@ -0,0 +1,79 @@
+"""SCons.Tool.latex
+
+Tool-specific initialization for LaTeX.
+Generates .dvi files from .latex or .ltx files
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/latex.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Scanner.LaTeX
+import SCons.Util
+import SCons.Tool
+import SCons.Tool.tex
+
+def LaTeXAuxFunction(target = None, source= None, env=None):
+ result = SCons.Tool.tex.InternalLaTeXAuxAction( SCons.Tool.tex.LaTeXAction, target, source, env )
+ if result != 0:
+ print env['LATEX']," returned an error, check the log file"
+ return result
+
+LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction,
+ strfunction=SCons.Tool.tex.TeXLaTeXStrFunction)
+
+def generate(env):
+ """Add Builders and construction variables for LaTeX to an Environment."""
+
+ env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes)
+
+ import dvi
+ dvi.generate(env)
+
+ import pdf
+ pdf.generate(env)
+
+ bld = env['BUILDERS']['DVI']
+ bld.add_action('.ltx', LaTeXAuxAction)
+ bld.add_action('.latex', LaTeXAuxAction)
+ bld.add_emitter('.ltx', SCons.Tool.tex.tex_eps_emitter)
+ bld.add_emitter('.latex', SCons.Tool.tex.tex_eps_emitter)
+
+ SCons.Tool.tex.generate_common(env)
+
+def exists(env):
+ return env.Detect('latex')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/latex.xml b/src/engine/SCons/Tool/latex.xml
new file mode 100644
index 0000000..aac9866
--- /dev/null
+++ b/src/engine/SCons/Tool/latex.xml
@@ -0,0 +1,71 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="latex">
+<summary>
+Sets construction variables for the &latex; utility.
+</summary>
+<sets>
+LATEX
+LATEXFLAGS
+LATEXCOM
+</sets>
+<uses>
+LATEXCOMSTR
+</uses>
+</tool>
+
+<cvar name="LATEX">
+<summary>
+The LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="LATEXCOM">
+<summary>
+The command line used to call the LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="LATEXCOMSTR">
+<summary>
+The string displayed when calling
+the LaTeX structured formatter and typesetter.
+If this is not set, then &cv-link-LATEXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="LATEXFLAGS">
+<summary>
+General options passed to the LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="LATEXRETRIES">
+<summary>
+The maximum number of times that LaTeX
+will be re-run if the
+<filename>.log</filename>
+generated by the &cv-link-LATEXCOM; command
+indicates that there are undefined references.
+The default is to try to resolve undefined references
+by re-running LaTeX up to three times.
+</summary>
+</cvar>
+
+<cvar name="TEXINPUTS">
+<summary>
+List of directories that the LaTeX programm will search
+for include directories.
+The LaTeX implicit dependency scanner will search these
+directories for \include and \import files.
+</summary>
+</cvar>
+
diff --git a/src/engine/SCons/Tool/lex.py b/src/engine/SCons/Tool/lex.py
new file mode 100644
index 0000000..57d7cca
--- /dev/null
+++ b/src/engine/SCons/Tool/lex.py
@@ -0,0 +1,99 @@
+"""SCons.Tool.lex
+
+Tool-specific initialization for lex.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/lex.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import string
+
+import SCons.Action
+import SCons.Tool
+import SCons.Util
+
+LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR")
+
+def lexEmitter(target, source, env):
+ sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0]))
+
+ if sourceExt == ".lm": # If using Objective-C
+ target = [sourceBase + ".m"] # the extension is ".m".
+
+ # This emitter essentially tries to add to the target all extra
+ # files generated by flex.
+
+ # Different options that are used to trigger the creation of extra files.
+ fileGenOptions = ["--header-file=", "--tables-file="]
+
+ lexflags = env.subst("$LEXFLAGS", target=target, source=source)
+ for option in SCons.Util.CLVar(lexflags):
+ for fileGenOption in fileGenOptions:
+ l = len(fileGenOption)
+ if option[:l] == fileGenOption:
+ # A file generating option is present, so add the
+ # file name to the target list.
+ fileName = string.strip(option[l:])
+ target.append(fileName)
+ return (target, source)
+
+def generate(env):
+ """Add Builders and construction variables for lex to an Environment."""
+ c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
+
+ # C
+ c_file.add_action(".l", LexAction)
+ c_file.add_emitter(".l", lexEmitter)
+
+ c_file.add_action(".lex", LexAction)
+ c_file.add_emitter(".lex", lexEmitter)
+
+ # Objective-C
+ cxx_file.add_action(".lm", LexAction)
+ cxx_file.add_emitter(".lm", lexEmitter)
+
+ # C++
+ cxx_file.add_action(".ll", LexAction)
+ cxx_file.add_emitter(".ll", lexEmitter)
+
+ env["LEX"] = env.Detect("flex") or "lex"
+ env["LEXFLAGS"] = SCons.Util.CLVar("")
+ env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET"
+
+def exists(env):
+ return env.Detect(["flex", "lex"])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/lex.xml b/src/engine/SCons/Tool/lex.xml
new file mode 100644
index 0000000..8b3fcf8
--- /dev/null
+++ b/src/engine/SCons/Tool/lex.xml
@@ -0,0 +1,50 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="lex">
+<summary>
+Sets construction variables for the &lex; lexical analyser.
+</summary>
+<sets>
+LEX
+LEXFLAGS
+LEXCOM
+</sets>
+<uses>
+LEXCOMSTR
+</uses>
+</tool>
+
+<cvar name="LEX">
+<summary>
+The lexical analyzer generator.
+</summary>
+</cvar>
+
+<cvar name="LEXCOM">
+<summary>
+The command line used to call the lexical analyzer generator
+to generate a source file.
+</summary>
+</cvar>
+
+<cvar name="LEXCOMSTR">
+<summary>
+The string displayed when generating a source file
+using the lexical analyzer generator.
+If this is not set, then &cv-link-LEXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="LEXFLAGS">
+<summary>
+General options passed to the lexical analyzer generator.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py
new file mode 100644
index 0000000..09938d6
--- /dev/null
+++ b/src/engine/SCons/Tool/link.py
@@ -0,0 +1,121 @@
+"""SCons.Tool.link
+
+Tool-specific initialization for the generic Posix linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/link.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+import SCons.Warnings
+
+from SCons.Tool.FortranCommon import isfortran
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+issued_mixed_link_warning = False
+
+def smart_link(source, target, env, for_signature):
+ has_cplusplus = cplusplus.iscplusplus(source)
+ has_fortran = isfortran(env, source)
+ if has_cplusplus and has_fortran:
+ global issued_mixed_link_warning
+ if not issued_mixed_link_warning:
+ msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \
+ "This may generate a buggy executable if the '%s'\n\t" + \
+ "compiler does not know how to deal with Fortran runtimes."
+ SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning,
+ msg % env.subst('$CXX'))
+ issued_mixed_link_warning = True
+ return '$CXX'
+ elif has_fortran:
+ return '$FORTRAN'
+ elif has_cplusplus:
+ return '$CXX'
+ return '$CC'
+
+def shlib_emitter(target, source, env):
+ for tgt in target:
+ tgt.attributes.shared = 1
+ return (target, source)
+
+def generate(env):
+ """Add Builders and construction variables for gnulink to an Environment."""
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
+
+ env['SHLINK'] = '$LINK'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
+ env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ # don't set up the emitter, cause AppendUnique will generate a list
+ # starting with None :-(
+ env.Append(SHLIBEMITTER = [shlib_emitter])
+ env['SMARTLINK'] = smart_link
+ env['LINK'] = "$SMARTLINK"
+ env['LINKFLAGS'] = SCons.Util.CLVar('')
+ env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['LIBDIRPREFIX']='-L'
+ env['LIBDIRSUFFIX']=''
+ env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}'
+ env['LIBLINKPREFIX']='-l'
+ env['LIBLINKSUFFIX']=''
+
+ if env['PLATFORM'] == 'hpux':
+ env['SHLIBSUFFIX'] = '.sl'
+ elif env['PLATFORM'] == 'aix':
+ env['SHLIBSUFFIX'] = '.a'
+
+ # For most platforms, a loadable module is the same as a shared
+ # library. Platforms which are different can override these, but
+ # setting them the same means that LoadableModule works everywhere.
+ SCons.Tool.createLoadableModuleBuilder(env)
+ env['LDMODULE'] = '$SHLINK'
+ # don't set up the emitter, cause AppendUnique will generate a list
+ # starting with None :-(
+ env.Append(LDMODULEEMITTER='$SHLIBEMITTER')
+ env['LDMODULEPREFIX'] = '$SHLIBPREFIX'
+ env['LDMODULESUFFIX'] = '$SHLIBSUFFIX'
+ env['LDMODULEFLAGS'] = '$SHLINKFLAGS'
+ env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+
+
+
+def exists(env):
+ # This module isn't really a Tool on its own, it's common logic for
+ # other linkers.
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml
new file mode 100644
index 0000000..b9a6a11
--- /dev/null
+++ b/src/engine/SCons/Tool/link.xml
@@ -0,0 +1,175 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="link">
+<summary>
+Sets construction variables for generic POSIX linkers.
+</summary>
+<sets>
+SHLINK
+SHLINKFLAGS
+SHLINKCOM
+LINK
+LINKFLAGS
+LINKCOM
+LIBDIRPREFIX
+LIBDIRSUFFIX
+LIBLINKPREFIX
+LIBLINKSUFFIX
+SHLIBSUFFIX
+LDMODULE
+LDMODULEPREFIX
+LDMODULESUFFIX
+LDMODULEFLAGS
+LDMODULECOM
+</sets>
+<uses>
+SHLINKCOMSTR
+LINKCOMSTR
+LDMODULECOMSTR
+</uses>
+</tool>
+
+<cvar name="LDMODULE">
+<summary>
+The linker for building loadable modules.
+By default, this is the same as &cv-link-SHLINK;.
+</summary>
+</cvar>
+
+<cvar name="LDMODULECOM">
+<summary>
+The command line for building loadable modules.
+On Mac OS X, this uses the &cv-link-LDMODULE;,
+&cv-link-LDMODULEFLAGS; and
+&cv-link-FRAMEWORKSFLAGS; variables.
+On other systems, this is the same as &cv-link-SHLINK;.
+</summary>
+</cvar>
+
+<cvar name="LDMODULECOMSTR">
+<summary>
+The string displayed when building loadable modules.
+If this is not set, then &cv-link-LDMODULECOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="LDMODULEFLAGS">
+<summary>
+General user options passed to the linker for building loadable modules.
+</summary>
+</cvar>
+
+<cvar name="LDMODULEPREFIX">
+<summary>
+The prefix used for loadable module file names.
+On Mac OS X, this is null;
+on other systems, this is
+the same as &cv-link-SHLIBPREFIX;.
+</summary>
+</cvar>
+
+<cvar name="LDMODULESUFFIX">
+<summary>
+The suffix used for loadable module file names.
+On Mac OS X, this is null;
+on other systems, this is
+the same as $SHLIBSUFFIX.
+</summary>
+</cvar>
+
+<cvar name="LINK">
+<summary>
+The linker.
+</summary>
+</cvar>
+
+<cvar name="LINKCOM">
+<summary>
+The command line used to link object files into an executable.
+</summary>
+</cvar>
+
+<cvar name="LINKCOMSTR">
+<summary>
+The string displayed when object files
+are linked into an executable.
+If this is not set, then &cv-link-LINKCOM; (the command line) is displayed.
+
+<example>
+env = Environment(LINKCOMSTR = "Linking $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="LINKFLAGS">
+<summary>
+General user options passed to the linker.
+Note that this variable should
+<emphasis>not</emphasis>
+contain
+<option>-l</option>
+(or similar) options for linking with the libraries listed in &cv-link-LIBS;,
+nor
+<option>-L</option>
+(or similar) library search path options
+that scons generates automatically from &cv-link-LIBPATH;.
+See
+&cv-link-_LIBFLAGS;
+above,
+for the variable that expands to library-link options,
+and
+&cv-link-_LIBDIRFLAGS;
+above,
+for the variable that expands to library search path options.
+</summary>
+</cvar>
+
+<cvar name="SHLINK">
+<summary>
+The linker for programs that use shared libraries.
+</summary>
+</cvar>
+
+<cvar name="SHLINKCOM">
+<summary>
+The command line used to link programs using shared libaries.
+</summary>
+</cvar>
+
+<cvar name="SHLINKCOMSTR">
+<summary>
+The string displayed when programs using shared libraries are linked.
+If this is not set, then &cv-link-SHLINKCOM; (the command line) is displayed.
+
+<example>
+env = Environment(SHLINKCOMSTR = "Linking shared $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="SHLINKFLAGS">
+<summary>
+General user options passed to the linker for programs using shared libraries.
+Note that this variable should
+<emphasis>not</emphasis>
+contain
+<option>-l</option>
+(or similar) options for linking with the libraries listed in &cv-link-LIBS;,
+nor
+<option>-L</option>
+(or similar) include search path options
+that scons generates automatically from &cv-link-LIBPATH;.
+See
+&cv-link-_LIBFLAGS;
+above,
+for the variable that expands to library-link options,
+and
+&cv-link-_LIBDIRFLAGS;
+above,
+for the variable that expands to library search path options.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py
new file mode 100644
index 0000000..0c50dc8
--- /dev/null
+++ b/src/engine/SCons/Tool/linkloc.py
@@ -0,0 +1,112 @@
+"""SCons.Tool.linkloc
+
+Tool specification for the LinkLoc linker for the Phar Lap ETS embedded
+operating system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/linkloc.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import re
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Errors
+import SCons.Tool
+import SCons.Util
+
+from SCons.Tool.MSCommon import msvs_exists, merge_default_version
+from SCons.Tool.PharLapCommon import addPharLapPaths
+
+_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)')
+
+def repl_linker_command(m):
+ # Replaces any linker command file directives (e.g. "@foo.lnk") with
+ # the actual contents of the file.
+ try:
+ f=open(m.group(2), "r")
+ return m.group(1) + f.read()
+ except IOError:
+ # the linker should return an error if it can't
+ # find the linker command file so we will remain quiet.
+ # However, we will replace the @ with a # so we will not continue
+ # to find it with recursive substitution
+ return m.group(1) + '#' + m.group(2)
+
+class LinklocGenerator:
+ def __init__(self, cmdline):
+ self.cmdline = cmdline
+
+ def __call__(self, env, target, source, for_signature):
+ if for_signature:
+ # Expand the contents of any linker command files recursively
+ subs = 1
+ strsub = env.subst(self.cmdline, target=target, source=source)
+ while subs:
+ strsub, subs = _re_linker_command.subn(repl_linker_command, strsub)
+ return strsub
+ else:
+ return "${TEMPFILE('" + self.cmdline + "')}"
+
+def generate(env):
+ """Add Builders and construction variables for ar to an Environment."""
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
+
+ env['SUBST_CMD_FILE'] = LinklocGenerator
+ env['SHLINK'] = '$LINK'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS')
+ env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}'
+ env['SHLIBEMITTER']= None
+ env['LINK'] = "linkloc"
+ env['LINKFLAGS'] = SCons.Util.CLVar('')
+ env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}'
+ env['LIBDIRPREFIX']='-libpath '
+ env['LIBDIRSUFFIX']=''
+ env['LIBLINKPREFIX']='-lib '
+ env['LIBLINKSUFFIX']='$LIBSUFFIX'
+
+ # Set-up ms tools paths for default version
+ merge_default_version(env)
+
+ addPharLapPaths(env)
+
+def exists(env):
+ if msvs_exists():
+ return env.Detect('linkloc')
+ else:
+ return 0
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/linkloc.xml b/src/engine/SCons/Tool/linkloc.xml
new file mode 100644
index 0000000..bcec6a9
--- /dev/null
+++ b/src/engine/SCons/Tool/linkloc.xml
@@ -0,0 +1,31 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="linkloc">
+<summary>
+Sets construction variables for the
+<application>LinkLoc</application>
+linker for the Phar Lap ETS embedded operating system.
+</summary>
+<sets>
+<!--SUBST_CMD_FILE-->
+SHLINK
+SHLINKFLAGS
+SHLINKCOM
+<!--SHLINKEMITTER-->
+LINK
+LINKFLAGS
+LINKCOM
+LIBDIRPREFIX
+LIBDIRSUFFIX
+LIBLINKPREFIX
+LIBLINKSUFFIX
+</sets>
+<uses>
+SHLINKCOMSTR
+LINKCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/m4.py b/src/engine/SCons/Tool/m4.py
new file mode 100644
index 0000000..f3f1d28
--- /dev/null
+++ b/src/engine/SCons/Tool/m4.py
@@ -0,0 +1,63 @@
+"""SCons.Tool.m4
+
+Tool-specific initialization for m4.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/m4.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+
+def generate(env):
+ """Add Builders and construction variables for m4 to an Environment."""
+ M4Action = SCons.Action.Action('$M4COM', '$M4COMSTR')
+ bld = SCons.Builder.Builder(action = M4Action, src_suffix = '.m4')
+
+ env['BUILDERS']['M4'] = bld
+
+ # .m4 files might include other files, and it would be pretty hard
+ # to write a scanner for it, so let's just cd to the dir of the m4
+ # file and run from there.
+ # The src_suffix setup is like so: file.c.m4 -> file.c,
+ # file.cpp.m4 -> file.cpp etc.
+ env['M4'] = 'm4'
+ env['M4FLAGS'] = SCons.Util.CLVar('-E')
+ env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}'
+
+def exists(env):
+ return env.Detect('m4')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/m4.xml b/src/engine/SCons/Tool/m4.xml
new file mode 100644
index 0000000..dbca146
--- /dev/null
+++ b/src/engine/SCons/Tool/m4.xml
@@ -0,0 +1,61 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="m4">
+<summary>
+Sets construction variables for the &m4; macro processor.
+</summary>
+<sets>
+M4
+M4FLAGS
+M4COM
+</sets>
+<uses>
+M4COMSTR
+</uses>
+</tool>
+
+<builder name="M4">
+<summary>
+Builds an output file from an M4 input file.
+This uses a default &cv-link-M4FLAGS; value of
+<option>-E</option>,
+which considers all warnings to be fatal
+and stops on the first warning
+when using the GNU version of m4.
+Example:
+
+<example>
+env.M4(target = 'foo.c', source = 'foo.c.m4')
+</example>
+</summary>
+</builder>
+
+<cvar name="M4">
+<summary>
+The M4 macro preprocessor.
+</summary>
+</cvar>
+
+<cvar name="M4COM">
+<summary>
+The command line used to pass files through the M4 macro preprocessor.
+</summary>
+</cvar>
+
+<cvar name="M4COMSTR">
+<summary>
+The string displayed when
+a file is passed through the M4 macro preprocessor.
+If this is not set, then &cv-link-M4COM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="M4FLAGS">
+<summary>
+General options passed to the M4 macro preprocessor.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/masm.py b/src/engine/SCons/Tool/masm.py
new file mode 100644
index 0000000..01db60a
--- /dev/null
+++ b/src/engine/SCons/Tool/masm.py
@@ -0,0 +1,77 @@
+"""SCons.Tool.masm
+
+Tool-specific initialization for the Microsoft Assembler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/masm.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+ASSuffixes = ['.s', '.asm', '.ASM']
+ASPPSuffixes = ['.spp', '.SPP', '.sx']
+if SCons.Util.case_sensitive_suffixes('.s', '.S'):
+ ASPPSuffixes.extend(['.S'])
+else:
+ ASSuffixes.extend(['.S'])
+
+def generate(env):
+ """Add Builders and construction variables for masm to an Environment."""
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in ASSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.ASAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ASAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ for suffix in ASPPSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.ASPPAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ASPPAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+ shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ env['AS'] = 'ml'
+ env['ASFLAGS'] = SCons.Util.CLVar('/nologo')
+ env['ASPPFLAGS'] = '$ASFLAGS'
+ env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES'
+ env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
+
+def exists(env):
+ return env.Detect('ml')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/masm.xml b/src/engine/SCons/Tool/masm.xml
new file mode 100644
index 0000000..3cc0ff2
--- /dev/null
+++ b/src/engine/SCons/Tool/masm.xml
@@ -0,0 +1,26 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="masm">
+<summary>
+Sets construction variables for the Microsoft assembler.
+</summary>
+<sets>
+AS
+ASFLAGS
+ASPPFLAGS
+ASCOM
+ASPPCOM
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+</sets>
+<uses>
+ASCOMSTR
+ASPPCOMSTR
+CPPFLAGS
+_CPPDEFFLAGS
+_CPPINCFLAGS
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py
new file mode 100644
index 0000000..9a20677
--- /dev/null
+++ b/src/engine/SCons/Tool/midl.py
@@ -0,0 +1,90 @@
+"""SCons.Tool.midl
+
+Tool-specific initialization for midl (Microsoft IDL compiler).
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/midl.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Scanner.IDL
+import SCons.Util
+
+from MSCommon import msvc_exists
+
+def midl_emitter(target, source, env):
+ """Produces a list of outputs from the MIDL compiler"""
+ base, ext = SCons.Util.splitext(str(target[0]))
+ tlb = target[0]
+ incl = base + '.h'
+ interface = base + '_i.c'
+ t = [tlb, incl, interface]
+
+ midlcom = env['MIDLCOM']
+
+ if string.find(midlcom, '/proxy') != -1:
+ proxy = base + '_p.c'
+ t.append(proxy)
+ if string.find(midlcom, '/dlldata') != -1:
+ dlldata = base + '_data.c'
+ t.append(dlldata)
+
+ return (t,source)
+
+idl_scanner = SCons.Scanner.IDL.IDLScan()
+
+midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR')
+
+midl_builder = SCons.Builder.Builder(action = midl_action,
+ src_suffix = '.idl',
+ suffix='.tlb',
+ emitter = midl_emitter,
+ source_scanner = idl_scanner)
+
+def generate(env):
+ """Add Builders and construction variables for midl to an Environment."""
+
+ env['MIDL'] = 'MIDL.EXE'
+ env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo')
+ env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL'
+ env['BUILDERS']['TypeLibrary'] = midl_builder
+
+def exists(env):
+ return msvc_exists()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/midl.xml b/src/engine/SCons/Tool/midl.xml
new file mode 100644
index 0000000..640207d
--- /dev/null
+++ b/src/engine/SCons/Tool/midl.xml
@@ -0,0 +1,68 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="midl">
+<summary>
+Sets construction variables for the Microsoft IDL compiler.
+</summary>
+<sets>
+MIDL
+MIDLFLAGS
+MIDLCOM
+</sets>
+<uses>
+MIDLCOMSTR
+</uses>
+</tool>
+
+<builder name="TypeLibrary">
+<summary>
+Builds a Windows type library (<filename>.tlb</filename>)
+file from an input IDL file (<filename>.idl</filename>).
+In addition, it will build the associated inteface stub and
+proxy source files,
+naming them according to the base name of the <filename>.idl</filename> file.
+For example,
+
+<example>
+env.TypeLibrary(source="foo.idl")
+</example>
+
+Will create <filename>foo.tlb</filename>,
+<filename>foo.h</filename>,
+<filename>foo_i.c</filename>,
+<filename>foo_p.c</filename>
+and
+<filename>foo_data.c</filename>
+files.
+</summary>
+</builder>
+
+<cvar name="MIDL">
+<summary>
+The Microsoft IDL compiler.
+</summary>
+</cvar>
+
+<cvar name="MIDLCOM">
+<summary>
+The command line used to pass files to the Microsoft IDL compiler.
+</summary>
+</cvar>
+
+<cvar name="MIDLCOMSTR">
+<summary>
+The string displayed when
+the Microsoft IDL copmiler is called.
+If this is not set, then &cv-link-MIDLCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="MIDLFLAGS">
+<summary>
+General options passed to the Microsoft IDL compiler.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py
new file mode 100644
index 0000000..61980e3
--- /dev/null
+++ b/src/engine/SCons/Tool/mingw.py
@@ -0,0 +1,159 @@
+"""SCons.Tool.gcc
+
+Tool-specific initialization for MinGW (http://www.mingw.org/)
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/mingw.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+# This is what we search for to find mingw:
+key_program = 'mingw32-gcc'
+
+def find(env):
+ # First search in the SCons path and then the OS path:
+ return env.WhereIs(key_program) or SCons.Util.WhereIs(key_program)
+
+def shlib_generator(target, source, env, for_signature):
+ cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS'])
+
+ dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ if dll: cmd.extend(['-o', dll])
+
+ cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS'])
+
+ implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
+ if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature))
+
+ def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX')
+ insert_def = env.subst("$WINDOWS_INSERT_DEF")
+ if not insert_def in ['', '0', 0] and def_target: \
+ cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature))
+
+ return [cmd]
+
+def shlib_emitter(target, source, env):
+ dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ no_import_lib = env.get('no_import_lib', 0)
+
+ if not dll:
+ raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")
+
+ if not no_import_lib and \
+ not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'):
+
+ # Append an import library to the list of targets.
+ target.append(env.ReplaceIxes(dll,
+ 'SHLIBPREFIX', 'SHLIBSUFFIX',
+ 'LIBPREFIX', 'LIBSUFFIX'))
+
+ # Append a def file target if there isn't already a def file target
+ # or a def file source. There is no option to disable def file
+ # target emitting, because I can't figure out why someone would ever
+ # want to turn it off.
+ def_source = env.FindIxes(source, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX')
+ def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX')
+ if not def_source and not def_target:
+ target.append(env.ReplaceIxes(dll,
+ 'SHLIBPREFIX', 'SHLIBSUFFIX',
+ 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX'))
+
+ return (target, source)
+
+
+shlib_action = SCons.Action.Action(shlib_generator, generator=1)
+
+res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
+
+res_builder = SCons.Builder.Builder(action=res_action, suffix='.o',
+ source_scanner=SCons.Tool.SourceFileScanner)
+SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan)
+
+def generate(env):
+ mingw = find(env)
+ if mingw:
+ dir = os.path.dirname(mingw)
+ env.PrependENVPath('PATH', dir )
+
+
+ # Most of mingw is the same as gcc and friends...
+ gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'm4']
+ for tool in gnu_tools:
+ SCons.Tool.Tool(tool)(env)
+
+ #... but a few things differ:
+ env['CC'] = 'gcc'
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+ env['CXX'] = 'g++'
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
+ env['SHLINKCOM'] = shlib_action
+ env['LDMODULECOM'] = shlib_action
+ env.Append(SHLIBEMITTER = [shlib_emitter])
+ env['AS'] = 'as'
+
+ env['WIN32DEFPREFIX'] = ''
+ env['WIN32DEFSUFFIX'] = '.def'
+ env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}'
+ env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}'
+
+ env['SHOBJSUFFIX'] = '.o'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
+
+ env['RC'] = 'windres'
+ env['RCFLAGS'] = SCons.Util.CLVar('')
+ env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+ env['RCINCPREFIX'] = '--include-dir '
+ env['RCINCSUFFIX'] = ''
+ env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET'
+ env['BUILDERS']['RES'] = res_builder
+
+ # Some setting from the platform also have to be overridden:
+ env['OBJSUFFIX'] = '.o'
+ env['LIBPREFIX'] = 'lib'
+ env['LIBSUFFIX'] = '.a'
+
+def exists(env):
+ return find(env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mingw.xml b/src/engine/SCons/Tool/mingw.xml
new file mode 100644
index 0000000..5be291c
--- /dev/null
+++ b/src/engine/SCons/Tool/mingw.xml
@@ -0,0 +1,38 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="mingw">
+<summary>
+Sets construction variables for MinGW (Minimal Gnu on Windows).
+</summary>
+<sets>
+CC
+SHCCFLAGS
+CXX
+SHCXXFLAGS
+SHLINKFLAGS
+SHLINKCOM
+LDMODULECOM
+AS
+WINDOWSDEFPREFIX
+WINDOWSDEFSUFFIX
+SHOBJSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+RC
+RCFLAGS
+RCINCFLAGS
+RCINCPREFIX
+RCINCSUFFIX
+RCCOM
+OBJSUFFIX
+LIBPREFIX
+LIBSUFFIX
+</sets>
+<uses>
+SHLINKCOMSTR
+RCCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/mslib.py b/src/engine/SCons/Tool/mslib.py
new file mode 100644
index 0000000..6945efd
--- /dev/null
+++ b/src/engine/SCons/Tool/mslib.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.mslib
+
+Tool-specific initialization for lib (MicroSoft library archiver).
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/mslib.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Tool.msvs
+import SCons.Tool.msvc
+import SCons.Util
+
+from MSCommon import msvc_exists, msvc_setup_env_once
+
+def generate(env):
+ """Add Builders and construction variables for lib to an Environment."""
+ SCons.Tool.createStaticLibBuilder(env)
+
+ # Set-up ms tools paths
+ msvc_setup_env_once(env)
+
+ env['AR'] = 'lib'
+ env['ARFLAGS'] = SCons.Util.CLVar('/nologo')
+ env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}"
+ env['LIBPREFIX'] = ''
+ env['LIBSUFFIX'] = '.lib'
+
+def exists(env):
+ return msvc_exists()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mslib.xml b/src/engine/SCons/Tool/mslib.xml
new file mode 100644
index 0000000..0bd4ccb
--- /dev/null
+++ b/src/engine/SCons/Tool/mslib.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="mslib">
+<summary>
+Sets construction variables for the Microsoft
+<application>mslib</application>
+library archiver.
+</summary>
+<sets>
+AR
+ARFLAGS
+ARCOM
+LIBPREFIX
+LIBSUFFIX
+</sets>
+<uses>
+ARCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py
new file mode 100644
index 0000000..4a7aa6c
--- /dev/null
+++ b/src/engine/SCons/Tool/mslink.py
@@ -0,0 +1,266 @@
+"""SCons.Tool.mslink
+
+Tool-specific initialization for the Microsoft linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/mslink.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Errors
+import SCons.Platform.win32
+import SCons.Tool
+import SCons.Tool.msvc
+import SCons.Tool.msvs
+import SCons.Util
+
+from MSCommon import msvc_setup_env_once, msvc_exists
+
+def pdbGenerator(env, target, source, for_signature):
+ try:
+ return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG']
+ except (AttributeError, IndexError):
+ return None
+
+def _dllTargets(target, source, env, for_signature, paramtp):
+ listCmd = []
+ dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp)
+ if dll: listCmd.append("/out:%s"%dll.get_string(for_signature))
+
+ implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
+ if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature))
+
+ return listCmd
+
+def _dllSources(target, source, env, for_signature, paramtp):
+ listCmd = []
+
+ deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")
+ for src in source:
+ # Check explicitly for a non-None deffile so that the __cmp__
+ # method of the base SCons.Util.Proxy class used for some Node
+ # proxies doesn't try to use a non-existent __dict__ attribute.
+ if deffile and src == deffile:
+ # Treat this source as a .def file.
+ listCmd.append("/def:%s" % src.get_string(for_signature))
+ else:
+ # Just treat it as a generic source file.
+ listCmd.append(src)
+ return listCmd
+
+def windowsShlinkTargets(target, source, env, for_signature):
+ return _dllTargets(target, source, env, for_signature, 'SHLIB')
+
+def windowsShlinkSources(target, source, env, for_signature):
+ return _dllSources(target, source, env, for_signature, 'SHLIB')
+
+def _windowsLdmodTargets(target, source, env, for_signature):
+ """Get targets for loadable modules."""
+ return _dllTargets(target, source, env, for_signature, 'LDMODULE')
+
+def _windowsLdmodSources(target, source, env, for_signature):
+ """Get sources for loadable modules."""
+ return _dllSources(target, source, env, for_signature, 'LDMODULE')
+
+def _dllEmitter(target, source, env, paramtp):
+ """Common implementation of dll emitter."""
+ SCons.Tool.msvc.validate_vars(env)
+
+ extratargets = []
+ extrasources = []
+
+ dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp)
+ no_import_lib = env.get('no_import_lib', 0)
+
+ if not dll:
+ raise SCons.Errors.UserError, 'A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp)
+
+ insert_def = env.subst("$WINDOWS_INSERT_DEF")
+ if not insert_def in ['', '0', 0] and \
+ not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"):
+
+ # append a def file to the list of sources
+ extrasources.append(
+ env.ReplaceIxes(dll,
+ '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
+ "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"))
+
+ version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
+ if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0):
+ # MSVC 8 automatically generates .manifest files that must be installed
+ extratargets.append(
+ env.ReplaceIxes(dll,
+ '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
+ "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX"))
+
+ if env.has_key('PDB') and env['PDB']:
+ pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
+ extratargets.append(pdb)
+ target[0].attributes.pdb = pdb
+
+ if not no_import_lib and \
+ not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"):
+ # Append an import library to the list of targets.
+ extratargets.append(
+ env.ReplaceIxes(dll,
+ '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
+ "LIBPREFIX", "LIBSUFFIX"))
+ # and .exp file is created if there are exports from a DLL
+ extratargets.append(
+ env.ReplaceIxes(dll,
+ '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
+ "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX"))
+
+ return (target+extratargets, source+extrasources)
+
+def windowsLibEmitter(target, source, env):
+ return _dllEmitter(target, source, env, 'SHLIB')
+
+def ldmodEmitter(target, source, env):
+ """Emitter for loadable modules.
+
+ Loadable modules are identical to shared libraries on Windows, but building
+ them is subject to different parameters (LDMODULE*).
+ """
+ return _dllEmitter(target, source, env, 'LDMODULE')
+
+def prog_emitter(target, source, env):
+ SCons.Tool.msvc.validate_vars(env)
+
+ extratargets = []
+
+ exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX")
+ if not exe:
+ raise SCons.Errors.UserError, "An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")
+
+ version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
+ if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0):
+ # MSVC 8 automatically generates .manifest files that have to be installed
+ extratargets.append(
+ env.ReplaceIxes(exe,
+ "PROGPREFIX", "PROGSUFFIX",
+ "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX"))
+
+ if env.has_key('PDB') and env['PDB']:
+ pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
+ extratargets.append(pdb)
+ target[0].attributes.pdb = pdb
+
+ return (target+extratargets,source)
+
+def RegServerFunc(target, source, env):
+ if env.has_key('register') and env['register']:
+ ret = regServerAction([target[0]], [source[0]], env)
+ if ret:
+ raise SCons.Errors.UserError, "Unable to register %s" % target[0]
+ else:
+ print "Registered %s sucessfully" % target[0]
+ return ret
+ return 0
+
+regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR")
+regServerCheck = SCons.Action.Action(RegServerFunc, None)
+shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}')
+compositeShLinkAction = shlibLinkAction + regServerCheck
+ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}')
+compositeLdmodAction = ldmodLinkAction + regServerCheck
+
+def generate(env):
+ """Add Builders and construction variables for ar to an Environment."""
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
+
+ env['SHLINK'] = '$LINK'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll')
+ env['_SHLINK_TARGETS'] = windowsShlinkTargets
+ env['_SHLINK_SOURCES'] = windowsShlinkSources
+ env['SHLINKCOM'] = compositeShLinkAction
+ env.Append(SHLIBEMITTER = [windowsLibEmitter])
+ env['LINK'] = 'link'
+ env['LINKFLAGS'] = SCons.Util.CLVar('/nologo')
+ env['_PDB'] = pdbGenerator
+ env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}'
+ env.Append(PROGEMITTER = [prog_emitter])
+ env['LIBDIRPREFIX']='/LIBPATH:'
+ env['LIBDIRSUFFIX']=''
+ env['LIBLINKPREFIX']=''
+ env['LIBLINKSUFFIX']='$LIBSUFFIX'
+
+ env['WIN32DEFPREFIX'] = ''
+ env['WIN32DEFSUFFIX'] = '.def'
+ env['WIN32_INSERT_DEF'] = 0
+ env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}'
+ env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}'
+ env['WINDOWS_INSERT_DEF'] = '${WIN32_INSERT_DEF}'
+
+ env['WIN32EXPPREFIX'] = ''
+ env['WIN32EXPSUFFIX'] = '.exp'
+ env['WINDOWSEXPPREFIX'] = '${WIN32EXPPREFIX}'
+ env['WINDOWSEXPSUFFIX'] = '${WIN32EXPSUFFIX}'
+
+ env['WINDOWSSHLIBMANIFESTPREFIX'] = ''
+ env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest'
+ env['WINDOWSPROGMANIFESTPREFIX'] = ''
+ env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest'
+
+ env['REGSVRACTION'] = regServerCheck
+ env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32')
+ env['REGSVRFLAGS'] = '/s '
+ env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}'
+
+ # Set-up ms tools paths
+ msvc_setup_env_once(env)
+
+
+ # Loadable modules are on Windows the same as shared libraries, but they
+ # are subject to different build parameters (LDMODULE* variables).
+ # Therefore LDMODULE* variables correspond as much as possible to
+ # SHLINK*/SHLIB* ones.
+ SCons.Tool.createLoadableModuleBuilder(env)
+ env['LDMODULE'] = '$SHLINK'
+ env['LDMODULEPREFIX'] = '$SHLIBPREFIX'
+ env['LDMODULESUFFIX'] = '$SHLIBSUFFIX'
+ env['LDMODULEFLAGS'] = '$SHLINKFLAGS'
+ env['_LDMODULE_TARGETS'] = _windowsLdmodTargets
+ env['_LDMODULE_SOURCES'] = _windowsLdmodSources
+ env['LDMODULEEMITTER'] = [ldmodEmitter]
+ env['LDMODULECOM'] = compositeLdmodAction
+
+def exists(env):
+ return msvc_exists()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mslink.xml b/src/engine/SCons/Tool/mslink.xml
new file mode 100644
index 0000000..05b0222
--- /dev/null
+++ b/src/engine/SCons/Tool/mslink.xml
@@ -0,0 +1,237 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="mslink">
+<summary>
+Sets construction variables for the Microsoft linker.
+</summary>
+<sets>
+SHLINK
+SHLINKFLAGS
+SHLINKCOM
+LINK
+LINKFLAGS
+LINKCOM
+LIBDIRPREFIX
+LIBDIRSUFFIX
+LIBLINKPREFIX
+LIBLINKSUFFIX
+WIN32DEFPREFIX
+WIN32DEFSUFFIX
+WINDOWSDEFPREFIX
+WINDOWSDEFSUFFIX
+WINDOWS_INSERT_DEF
+WIN32EXPPREFIX
+WIN32EXPSUFFIX
+WINDOWSEXPPREFIX
+WINDOWSEXPSUFFIX
+WINDOWSSHLIBMANIFESTPREFIX
+WINDOWSSHLIBMANIFESTSUFFIX
+WINDOWSPROGMANIFESTPREFIX
+WINDOWSPROGMANIFESTSUFFIX
+<!--REGSVRACTION-->
+REGSVR
+REGSVRFLAGS
+REGSVRCOM
+LDMODULE
+LDMODULEPREFIX
+LDMODULESUFFIX
+LDMODULEFLAGS
+LDMODULECOM
+</sets>
+<uses>
+SHLINKCOMSTR
+LINKCOMSTR
+REGSVRCOMSTR
+MSVS_IGNORE_IDE_PATHS
+LDMODULECOMSTR
+</uses>
+</tool>
+
+<cvar name="no_import_lib">
+<summary>
+When set to non-zero,
+suppresses creation of a corresponding Windows static import lib by the
+<literal>SharedLibrary</literal>
+builder when used with
+MinGW, Microsoft Visual Studio or Metrowerks.
+This also suppresses creation
+of an export (.exp) file
+when using Microsoft Visual Studio.
+</summary>
+</cvar>
+
+<cvar name="PDB">
+<summary>
+The Microsoft Visual C++ PDB file that will store debugging information for
+object files, shared libraries, and programs. This variable is ignored by
+tools other than Microsoft Visual C++.
+When this variable is
+defined SCons will add options to the compiler and linker command line to
+cause them to generate external debugging information, and will also set up the
+dependencies for the PDB file.
+Example:
+
+<example>
+env['PDB'] = 'hello.pdb'
+</example>
+
+The Visual C++ compiler switch that SCons uses by default
+to generate PDB information is <option>/Z7</option>.
+This works correctly with parallel (<option>-j</option>) builds
+because it embeds the debug information in the intermediate object files,
+as opposed to sharing a single PDB file between multiple object files.
+This is also the only way to get debug information
+embedded into a static library.
+Using the <option>/Zi</option> instead may yield improved
+link-time performance,
+although parallel builds will no longer work.
+You can generate PDB files with the <option>/Zi</option>
+switch by overriding the default &cv-link-CCPDBFLAGS; variable;
+see the entry for that variable for specific examples.
+</summary>
+</cvar>
+
+<cvar name="REGSVR">
+<summary>
+The program used on Windows systems
+to register a newly-built DLL library
+whenever the &b-SharedLibrary; builder
+is passed a keyword argument of <literal>register=1</literal>.
+</summary>
+</cvar>
+
+<cvar name="REGSVRCOM">
+<summary>
+The command line used on Windows systems
+to register a newly-built DLL library
+whenever the &b-SharedLibrary; builder
+is passed a keyword argument of <literal>register=1</literal>.
+</summary>
+</cvar>
+
+<cvar name="REGSVRCOMSTR">
+<summary>
+The string displayed when registering a newly-built DLL file.
+If this is not set, then &cv-link-REGSVRCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="REGSVRFLAGS">
+<summary>
+Flags passed to the DLL registration program
+on Windows systems when a newly-built DLL library is registered.
+By default,
+this includes the <option>/s</option>
+that prevents dialog boxes from popping up
+and requiring user attention.
+</summary>
+</cvar>
+
+<cvar name="WIN32_INSERT_DEF">
+<summary>
+A deprecated synonym for &cv-link-WINDOWS_INSERT_DEF;.
+</summary>
+</cvar>
+
+<cvar name="WIN32DEFPREFIX">
+<summary>
+A deprecated synonym for &cv-link-WINDOWSDEFPREFIX;.
+</summary>
+</cvar>
+
+<cvar name="WIN32DEFSUFFIX">
+<summary>
+A deprecated synonym for &cv-link-WINDOWSDEFSUFFIX;.
+</summary>
+</cvar>
+
+<cvar name="WIN32EXPPREFIX">
+<summary>
+A deprecated synonym for &cv-link-WINDOWSEXPSUFFIX;.
+</summary>
+</cvar>
+
+<cvar name="WIN32EXPSUFFIX">
+<summary>
+A deprecated synonym for &cv-link-WINDOWSEXPSUFFIX;.
+</summary>
+</cvar>
+
+<cvar name="WINDOWS_INSERT_DEF">
+<summary>
+When this is set to true,
+a library build of a Windows shared library
+(<filename>.dll</filename>file)
+will also build a corresponding <filename>.def</filename> file
+at the same time,
+if a <filename>.def</filename> file
+is not already listed as a build target.
+The default is 0 (do not build a <filename>.def</filename> file).
+</summary>
+</cvar>
+
+<cvar name="WINDOWS_INSERT_MANIFEST">
+<summary>
+When this is set to true,
+&scons;
+will be aware of the
+<filename>.manifest</filename>
+files generated by Microsoft Visua C/C++ 8.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSDEFPREFIX">
+<summary>
+The prefix used for Windows <filename>.def</filename>file names.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSDEFSUFFIX">
+<summary>
+The suffix used for Windows <filename>.def</filename> file names.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSEXPPREFIX">
+<summary>
+The prefix used for Windows <filename>.exp</filename> file names.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSEXPSUFFIX">
+<summary>
+The suffix used for Windows <filename>.exp</filename> file names.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSPROGMANIFESTPREFIX">
+<summary>
+The prefix used for executable program <filename>.manifest</filename> files
+generated by Microsoft Visual C/C++.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSPROGMANIFESTSUFFIX">
+<summary>
+The suffix used for executable program <filename>.manifest</filename> files
+generated by Microsoft Visual C/C++.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSSHLIBMANIFESTPREFIX">
+<summary>
+The prefix used for shared library <filename>.manifest</filename> files
+generated by Microsoft Visual C/C++.
+</summary>
+</cvar>
+
+<cvar name="WINDOWSSHLIBMANIFESTSUFFIX">
+<summary>
+The suffix used for shared library <filename>.manifest</filename> files
+generated by Microsoft Visual C/C++.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/mssdk.py b/src/engine/SCons/Tool/mssdk.py
new file mode 100644
index 0000000..9f7cc6f
--- /dev/null
+++ b/src/engine/SCons/Tool/mssdk.py
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/mssdk.py 4577 2009/12/27 19:44:43 scons"
+
+"""engine.SCons.Tool.mssdk
+
+Tool-specific initialization for Microsoft SDKs, both Platform
+SDKs and Windows SDKs.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+from MSCommon import mssdk_exists, \
+ mssdk_setup_env
+
+def generate(env):
+ """Add construction variables for an MS SDK to an Environment."""
+ mssdk_setup_env(env)
+
+def exists(env):
+ return mssdk_exists()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mssdk.xml b/src/engine/SCons/Tool/mssdk.xml
new file mode 100644
index 0000000..9c2e6d0
--- /dev/null
+++ b/src/engine/SCons/Tool/mssdk.xml
@@ -0,0 +1,50 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="mssdk">
+<summary>
+Sets variables for Microsoft Platform SDK and/or Windows SDK.
+Note that unlike most other Tool modules,
+mssdk does not set construction variables,
+but sets the <emphasis>environment variables</emphasis>
+in the environment &SCons; uses to execute
+the Microsoft toolchain:
+<literal>%INCLUDE%</literal>,
+<literal>%LIB%</literal>,
+<literal>%LIBPATH%</literal> and
+<literal>%PATH%</literal>.
+</summary>
+<sets>
+</sets>
+<uses>
+MSSDK_DIR
+MSSDK_VERSION
+MSVS_VERSION
+</uses>
+</tool>
+
+<cvar name="MSSDK_DIR">
+<summary>
+The directory containing the Microsoft SDK
+(either Platform SDK or Windows SDK)
+to be used for compilation.
+</summary>
+</cvar>
+
+<cvar name="MSSDK_VERSION">
+<summary>
+The version string of the Microsoft SDK
+(either Platform SDK or Windows SDK)
+to be used for compilation.
+Supported versions include
+<literal>6.1</literal>,
+<literal>6.0A</literal>,
+<literal>6.0</literal>,
+<literal>2003R2</literal>
+and
+<literal>2003R1</literal>.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py
new file mode 100644
index 0000000..0d3ec66
--- /dev/null
+++ b/src/engine/SCons/Tool/msvc.py
@@ -0,0 +1,257 @@
+"""engine.SCons.Tool.msvc
+
+Tool-specific initialization for Microsoft Visual C/C++.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/msvc.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import re
+import string
+import sys
+
+import SCons.Action
+import SCons.Builder
+import SCons.Errors
+import SCons.Platform.win32
+import SCons.Tool
+import SCons.Tool.msvs
+import SCons.Util
+import SCons.Warnings
+import SCons.Scanner.RC
+
+from MSCommon import msvc_exists, msvc_setup_env_once
+
+CSuffixes = ['.c', '.C']
+CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
+
+def validate_vars(env):
+ """Validate the PCH and PCHSTOP construction variables."""
+ if env.has_key('PCH') and env['PCH']:
+ if not env.has_key('PCHSTOP'):
+ raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
+ if not SCons.Util.is_String(env['PCHSTOP']):
+ raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
+
+def pch_emitter(target, source, env):
+ """Adds the object file target."""
+
+ validate_vars(env)
+
+ pch = None
+ obj = None
+
+ for t in target:
+ if SCons.Util.splitext(str(t))[1] == '.pch':
+ pch = t
+ if SCons.Util.splitext(str(t))[1] == '.obj':
+ obj = t
+
+ if not obj:
+ obj = SCons.Util.splitext(str(pch))[0]+'.obj'
+
+ target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
+
+ return (target, source)
+
+def object_emitter(target, source, env, parent_emitter):
+ """Sets up the PCH dependencies for an object file."""
+
+ validate_vars(env)
+
+ parent_emitter(target, source, env)
+
+ if env.has_key('PCH') and env['PCH']:
+ env.Depends(target, env['PCH'])
+
+ return (target, source)
+
+def static_object_emitter(target, source, env):
+ return object_emitter(target, source, env,
+ SCons.Defaults.StaticObjectEmitter)
+
+def shared_object_emitter(target, source, env):
+ return object_emitter(target, source, env,
+ SCons.Defaults.SharedObjectEmitter)
+
+pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
+pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
+ emitter=pch_emitter,
+ source_scanner=SCons.Tool.SourceFileScanner)
+
+
+# Logic to build .rc files into .res files (resource files)
+res_scanner = SCons.Scanner.RC.RCScan()
+res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
+res_builder = SCons.Builder.Builder(action=res_action,
+ src_suffix='.rc',
+ suffix='.res',
+ src_builder=[],
+ source_scanner=res_scanner)
+
+def msvc_batch_key(action, env, target, source):
+ """
+ Returns a key to identify unique batches of sources for compilation.
+
+ If batching is enabled (via the $MSVC_BATCH setting), then all
+ target+source pairs that use the same action, defined by the same
+ environment, and have the same target and source directories, will
+ be batched.
+
+ Returning None specifies that the specified target+source should not
+ be batched with other compilations.
+ """
+ b = env.subst('$MSVC_BATCH')
+ if b in (None, '', '0'):
+ # We're not using batching; return no key.
+ return None
+ t = target[0]
+ s = source[0]
+ if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]:
+ # The base names are different, so this *must* be compiled
+ # separately; return no key.
+ return None
+ return (id(action), id(env), t.dir, s.dir)
+
+def msvc_output_flag(target, source, env, for_signature):
+ """
+ Returns the correct /Fo flag for batching.
+
+ If batching is disabled or there's only one source file, then we
+ return an /Fo string that specifies the target explicitly. Otherwise,
+ we return an /Fo string that just specifies the first target's
+ directory (where the Visual C/C++ compiler will put the .obj files).
+ """
+ b = env.subst('$MSVC_BATCH')
+ if b in (None, '', '0') or len(source) == 1:
+ return '/Fo$TARGET'
+ else:
+ # The Visual C/C++ compiler requires a \ at the end of the /Fo
+ # option to indicate an output directory. We use os.sep here so
+ # that the test(s) for this can be run on non-Windows systems
+ # without having a hard-coded backslash mess up command-line
+ # argument parsing.
+ return '/Fo${TARGET.dir}' + os.sep
+
+CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR",
+ batch_key=msvc_batch_key,
+ targets='$CHANGED_TARGETS')
+ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR",
+ batch_key=msvc_batch_key,
+ targets='$CHANGED_TARGETS')
+CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR",
+ batch_key=msvc_batch_key,
+ targets='$CHANGED_TARGETS')
+ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR",
+ batch_key=msvc_batch_key,
+ targets='$CHANGED_TARGETS')
+
+def generate(env):
+ """Add Builders and construction variables for MSVC++ to an Environment."""
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ # TODO(batch): shouldn't reach in to cmdgen this way; necessary
+ # for now to bypass the checks in Builder.DictCmdGenerator.__call__()
+ # and allow .cc and .cpp to be compiled in the same command line.
+ static_obj.cmdgen.source_ext_match = False
+ shared_obj.cmdgen.source_ext_match = False
+
+ for suffix in CSuffixes:
+ static_obj.add_action(suffix, CAction)
+ shared_obj.add_action(suffix, ShCAction)
+ static_obj.add_emitter(suffix, static_object_emitter)
+ shared_obj.add_emitter(suffix, shared_object_emitter)
+
+ for suffix in CXXSuffixes:
+ static_obj.add_action(suffix, CXXAction)
+ shared_obj.add_action(suffix, ShCXXAction)
+ static_obj.add_emitter(suffix, static_object_emitter)
+ shared_obj.add_emitter(suffix, shared_object_emitter)
+
+ env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
+ env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
+ env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag
+ env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS'
+ env['CC'] = 'cl'
+ env['CCFLAGS'] = SCons.Util.CLVar('/nologo')
+ env['CFLAGS'] = SCons.Util.CLVar('')
+ env['CCCOM'] = '$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM'
+ env['SHCC'] = '$CC'
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+ env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
+ env['SHCCCOM'] = '$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM'
+ env['CXX'] = '$CC'
+ env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)')
+ env['CXXCOM'] = '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM'
+ env['SHCXX'] = '$CXX'
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
+ env['SHCXXCOM'] = '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM'
+ env['CPPDEFPREFIX'] = '/D'
+ env['CPPDEFSUFFIX'] = ''
+ env['INCPREFIX'] = '/I'
+ env['INCSUFFIX'] = ''
+# env.Append(OBJEMITTER = [static_object_emitter])
+# env.Append(SHOBJEMITTER = [shared_object_emitter])
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
+
+ env['RC'] = 'rc'
+ env['RCFLAGS'] = SCons.Util.CLVar('')
+ env['RCSUFFIXES']=['.rc','.rc2']
+ env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
+ env['BUILDERS']['RES'] = res_builder
+ env['OBJPREFIX'] = ''
+ env['OBJSUFFIX'] = '.obj'
+ env['SHOBJPREFIX'] = '$OBJPREFIX'
+ env['SHOBJSUFFIX'] = '$OBJSUFFIX'
+
+ # Set-up ms tools paths
+ msvc_setup_env_once(env)
+
+ env['CFILESUFFIX'] = '.c'
+ env['CXXFILESUFFIX'] = '.cc'
+
+ env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
+ env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
+ env['BUILDERS']['PCH'] = pch_builder
+
+ if not env.has_key('ENV'):
+ env['ENV'] = {}
+ if not env['ENV'].has_key('SystemRoot'): # required for dlls in the winsxs folders
+ env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()
+
+def exists(env):
+ return msvc_exists()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml
new file mode 100644
index 0000000..c744903
--- /dev/null
+++ b/src/engine/SCons/Tool/msvc.xml
@@ -0,0 +1,316 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="msvc">
+<summary>
+Sets construction variables for the Microsoft Visual C/C++ compiler.
+</summary>
+<sets>
+CCPDBFLAGS
+CCPCHFLAGS
+<!--CCCOMFLAGS-->
+CC
+CCFLAGS
+CFLAGS
+CCCOM
+SHCC
+SHCCFLAGS
+SHCFLAGS
+SHCCCOM
+CXX
+CXXFLAGS
+CXXCOM
+SHCXX
+SHCXXFLAGS
+SHCXXCOM
+CPPDEFPREFIX
+CPPDEFSUFFIX
+INCPREFIX
+INCSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+RC
+RCFLAGS
+RCCOM
+BUILDERS
+OBJPREFIX
+OBJSUFFIX
+SHOBJPREFIX
+SHOBJSUFFIX
+CFILESUFFIX
+CXXFILESUFFIX
+PCHPDBFLAGS
+PCHCOM
+</sets>
+<uses>
+CCCOMSTR
+SHCCCOMSTR
+CXXCOMSTR
+SHCXXCOMSTR
+PCH
+PCHSTOP
+PDB
+</uses>
+</tool>
+
+<builder name="PCH">
+<summary>
+Builds a Microsoft Visual C++ precompiled header.
+Calling this builder method
+returns a list of two targets: the PCH as the first element, and the object
+file as the second element. Normally the object file is ignored.
+This builder method is only
+provided when Microsoft Visual C++ is being used as the compiler.
+The PCH builder method is generally used in
+conjuction with the PCH construction variable to force object files to use
+the precompiled header:
+
+<example>
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+</example>
+</summary>
+</builder>
+
+<builder name="RES">
+<summary>
+Builds a Microsoft Visual C++ resource file.
+This builder method is only provided
+when Microsoft Visual C++ or MinGW is being used as the compiler. The
+<filename>.res</filename>
+(or
+<filename>.o</filename>
+for MinGW) suffix is added to the target name if no other suffix is given.
+The source
+file is scanned for implicit dependencies as though it were a C file.
+Example:
+
+<example>
+env.RES('resource.rc')
+</example>
+</summary>
+</builder>
+
+<cvar name="CCPCHFLAGS">
+<summary>
+Options added to the compiler command line
+to support building with precompiled headers.
+The default value expands expands to the appropriate
+Microsoft Visual C++ command-line options
+when the &cv-link-PCH; construction variable is set.
+</summary>
+</cvar>
+
+<cvar name="CCPDBFLAGS">
+<summary>
+Options added to the compiler command line
+to support storing debugging information in a
+Microsoft Visual C++ PDB file.
+The default value expands expands to appropriate
+Microsoft Visual C++ command-line options
+when the &cv-link-PDB; construction variable is set.
+
+The Visual C++ compiler option that SCons uses by default
+to generate PDB information is <option>/Z7</option>.
+This works correctly with parallel (<option>-j</option>) builds
+because it embeds the debug information in the intermediate object files,
+as opposed to sharing a single PDB file between multiple object files.
+This is also the only way to get debug information
+embedded into a static library.
+Using the <option>/Zi</option> instead may yield improved
+link-time performance,
+although parallel builds will no longer work.
+
+You can generate PDB files with the <option>/Zi</option>
+switch by overriding the default &cv-link-CCPDBFLAGS; variable as follows:
+
+<example>
+env['CCPDBFLAGS'] = ['${(PDB and "/Zi /Fd%s" % File(PDB)) or ""}']
+</example>
+
+An alternative would be to use the <option>/Zi</option>
+to put the debugging information in a separate <filename>.pdb</filename>
+file for each object file by overriding
+the &cv-link-CCPDBFLAGS; variable as follows:
+
+<example>
+env['CCPDBFLAGS'] = '/Zi /Fd${TARGET}.pdb'
+</example>
+</summary>
+</cvar>
+
+<cvar name="MSVC_BATCH">
+<summary>
+When set to any true value,
+specifies that &SCons; should batch
+compilation of object files
+when calling the Microsoft Visual C/C++ compiler.
+All compilations of source files from the same source directory
+that generate target files in a same output directory
+and were configured in &SCons; using the same construction environment
+will be built in a single call to the compiler.
+Only source files that have changed since their
+object files were built will be passed to each compiler invocation
+(via the &cv-link-CHANGED_SOURCES; construction variable).
+Any compilations where the object (target) file base name
+(minus the <filename>.obj</filename>)
+does not match the source file base name
+will be compiled separately.
+</summary>
+</cvar>
+
+<cvar name="PCH">
+<summary>
+The Microsoft Visual C++ precompiled header that will be used when compiling
+object files. This variable is ignored by tools other than Microsoft Visual C++.
+When this variable is
+defined SCons will add options to the compiler command line to
+cause it to use the precompiled header, and will also set up the
+dependencies for the PCH file.
+Example:
+
+<example>
+env['PCH'] = 'StdAfx.pch'
+</example>
+</summary>
+</cvar>
+
+<cvar name="PCHCOM">
+<summary>
+The command line used by the
+&b-PCH;
+builder to generated a precompiled header.
+</summary>
+</cvar>
+
+<cvar name="PCHCOMSTR">
+<summary>
+The string displayed when generating a precompiled header.
+If this is not set, then &cv-link-PCHCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="PCHPDBFLAGS">
+<summary>
+A construction variable that, when expanded,
+adds the <literal>/yD</literal> flag to the command line
+only if the &cv-PDB; construction variable is set.
+</summary>
+</cvar>
+
+<cvar name="PCHSTOP">
+<summary>
+This variable specifies how much of a source file is precompiled. This
+variable is ignored by tools other than Microsoft Visual C++, or when
+the PCH variable is not being used. When this variable is define it
+must be a string that is the name of the header that
+is included at the end of the precompiled portion of the source files, or
+the empty string if the "#pragma hrdstop" construct is being used:
+
+<example>
+env['PCHSTOP'] = 'StdAfx.h'
+</example>
+</summary>
+</cvar>
+
+<cvar name="RC">
+<summary>
+The resource compiler used to build
+a Microsoft Visual C++ resource file.
+</summary>
+</cvar>
+
+<cvar name="RCCOM">
+<summary>
+The command line used to build
+a Microsoft Visual C++ resource file.
+</summary>
+</cvar>
+
+<cvar name="RCCOMSTR">
+<summary>
+The string displayed when invoking the resource compiler
+to build a Microsoft Visual C++ resource file.
+If this is not set, then &cv-link-RCCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="RCFLAGS">
+<summary>
+The flags passed to the resource compiler by the RES builder.
+</summary>
+</cvar>
+
+<cvar name="RCINCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the command-line options
+for specifying directories to be searched
+by the resource compiler.
+The value of &cv-RCINCFLAGS; is created
+by appending &cv-RCINCPREFIX; and &cv-RCINCSUFFIX;
+to the beginning and end
+of each directory in &cv-CPPPATH;.
+</summary>
+</cvar>
+
+<cvar name="RCINCPREFIX">
+<summary>
+The prefix (flag) used to specify an include directory
+on the resource compiler command line.
+This will be appended to the beginning of each directory
+in the &cv-CPPPATH; construction variable
+when the &cv-RCINCFLAGS; variable is expanded.
+</summary>
+</cvar>
+
+<cvar name="RCINCSUFFIX">
+<summary>
+The suffix used to specify an include directory
+on the resource compiler command line.
+This will be appended to the end of each directory
+in the &cv-CPPPATH; construction variable
+when the &cv-RCINCFLAGS; variable is expanded.
+</summary>
+</cvar>
+
+<cvar name="MSVC_VERSION">
+<summary>
+Sets the preferred version of Microsoft Visual C/C++ to use.
+
+If &cv-MSVC_VERSION; is not set,
+&SCons; will (by default) select the latest version
+of Visual C/C++ installed on your system.
+If the specified version isn't installed,
+tool initialization will fail.
+</summary>
+</cvar>
+
+<cvar name="HOST_ARCH">
+<summary>
+Sets the host architecture for Visual Studio compiler. If not set,
+default to the detected host architecture: note that this may depend
+on the python you are using.
+
+Valid values are the same as for &cv-TARGET_ARCH;.
+
+This is currently only used on Windows, but in the future it will be
+used on other OSes as well.
+</summary>
+</cvar>
+<cvar name="TARGET_ARCH">
+<summary>
+Sets the target architecture for Visual Studio compiler (i.e. the arch
+of the binaries generated by the compiler). If not set, default to
+&cv-HOST_ARCH;.
+This is currently only used on Windows, but in the future it will be
+used on other OSes as well.
+
+Valid values for Windows are 'x86', 'i386' (for 32 bits);
+'amd64', 'emt64', 'x86_64' (64 bits);
+and 'ia64' (Itanium).
+For example, if you want to compile 64-bit binaries, you would set
+TARGET_ARCH='x86_64' in your SCons environment.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py
new file mode 100644
index 0000000..a490b9c
--- /dev/null
+++ b/src/engine/SCons/Tool/msvs.py
@@ -0,0 +1,1439 @@
+"""SCons.Tool.msvs
+
+Tool-specific initialization for Microsoft Visual Studio project files.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/msvs.py 4577 2009/12/27 19:44:43 scons"
+
+import base64
+import hashlib
+import ntpath
+import os
+import pickle
+import re
+import string
+import sys
+
+import SCons.Builder
+import SCons.Node.FS
+import SCons.Platform.win32
+import SCons.Script.SConscript
+import SCons.Util
+import SCons.Warnings
+
+from MSCommon import msvc_exists, msvc_setup_env_once
+from SCons.Defaults import processDefines
+
+##############################################################################
+# Below here are the classes and functions for generation of
+# DSP/DSW/SLN/VCPROJ files.
+##############################################################################
+
+def _hexdigest(s):
+ """Return a string as a string of hex characters.
+ """
+ # NOTE: This routine is a method in the Python 2.0 interface
+ # of the native md5 module, but we want SCons to operate all
+ # the way back to at least Python 1.5.2, which doesn't have it.
+ h = string.hexdigits
+ r = ''
+ for c in s:
+ i = ord(c)
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+ return r
+
+def xmlify(s):
+ s = string.replace(s, "&", "&amp;") # do this first
+ s = string.replace(s, "'", "&apos;")
+ s = string.replace(s, '"', "&quot;")
+ return s
+
+external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}'
+
+def _generateGUID(slnfile, name):
+ """This generates a dummy GUID for the sln file to use. It is
+ based on the MD5 signatures of the sln filename plus the name of
+ the project. It basically just needs to be unique, and not
+ change with each invocation."""
+ m = hashlib.md5()
+ # Normalize the slnfile path to a Windows path (\ separators) so
+ # the generated file has a consistent GUID even if we generate
+ # it on a non-Windows platform.
+ m.update(ntpath.normpath(str(slnfile)) + str(name))
+ # TODO(1.5)
+ #solution = m.hexdigest().upper()
+ solution = string.upper(_hexdigest(m.digest()))
+ # convert most of the signature to GUID form (discard the rest)
+ solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}"
+ return solution
+
+version_re = re.compile(r'(\d+\.\d+)(.*)')
+
+def msvs_parse_version(s):
+ """
+ Split a Visual Studio version, which may in fact be something like
+ '7.0Exp', into is version number (returned as a float) and trailing
+ "suite" portion.
+ """
+ num, suite = version_re.match(s).groups()
+ return float(num), suite
+
+# This is how we re-invoke SCons from inside MSVS Project files.
+# The problem is that we might have been invoked as either scons.bat
+# or scons.py. If we were invoked directly as scons.py, then we could
+# use sys.argv[0] to find the SCons "executable," but that doesn't work
+# if we were invoked as scons.bat, which uses "python -c" to execute
+# things and ends up with "-c" as sys.argv[0]. Consequently, we have
+# the MSVS Project file invoke SCons the same way that scons.bat does,
+# which works regardless of how we were invoked.
+def getExecScriptMain(env, xml=None):
+ scons_home = env.get('SCONS_HOME')
+ if not scons_home and os.environ.has_key('SCONS_LIB_DIR'):
+ scons_home = os.environ['SCONS_LIB_DIR']
+ if scons_home:
+ exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home
+ else:
+ version = SCons.__version__
+ exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals()
+ if xml:
+ exec_script_main = xmlify(exec_script_main)
+ return exec_script_main
+
+# The string for the Python executable we tell the Project file to use
+# is either sys.executable or, if an external PYTHON_ROOT environment
+# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to
+# pluck the actual executable name from sys.executable).
+try:
+ python_root = os.environ['PYTHON_ROOT']
+except KeyError:
+ python_executable = sys.executable
+else:
+ python_executable = os.path.join('$$(PYTHON_ROOT)',
+ os.path.split(sys.executable)[1])
+
+class Config:
+ pass
+
+def splitFully(path):
+ dir, base = os.path.split(path)
+ if dir and dir != '' and dir != path:
+ return splitFully(dir)+[base]
+ if base == '':
+ return []
+ return [base]
+
+def makeHierarchy(sources):
+ '''Break a list of files into a hierarchy; for each value, if it is a string,
+ then it is a file. If it is a dictionary, it is a folder. The string is
+ the original path of the file.'''
+
+ hierarchy = {}
+ for file in sources:
+ path = splitFully(file)
+ if len(path):
+ dict = hierarchy
+ for part in path[:-1]:
+ if not dict.has_key(part):
+ dict[part] = {}
+ dict = dict[part]
+ dict[path[-1]] = file
+ #else:
+ # print 'Warning: failed to decompose path for '+str(file)
+ return hierarchy
+
+class _DSPGenerator:
+ """ Base class for DSP generators """
+
+ srcargs = [
+ 'srcs',
+ 'incs',
+ 'localincs',
+ 'resources',
+ 'misc']
+
+ def __init__(self, dspfile, source, env):
+ self.dspfile = str(dspfile)
+ try:
+ get_abspath = dspfile.get_abspath
+ except AttributeError:
+ self.dspabs = os.path.abspath(dspfile)
+ else:
+ self.dspabs = get_abspath()
+
+ if not env.has_key('variant'):
+ raise SCons.Errors.InternalError, \
+ "You must specify a 'variant' argument (i.e. 'Debug' or " +\
+ "'Release') to create an MSVSProject."
+ elif SCons.Util.is_String(env['variant']):
+ variants = [env['variant']]
+ elif SCons.Util.is_List(env['variant']):
+ variants = env['variant']
+
+ if not env.has_key('buildtarget') or env['buildtarget'] == None:
+ buildtarget = ['']
+ elif SCons.Util.is_String(env['buildtarget']):
+ buildtarget = [env['buildtarget']]
+ elif SCons.Util.is_List(env['buildtarget']):
+ if len(env['buildtarget']) != len(variants):
+ raise SCons.Errors.InternalError, \
+ "Sizes of 'buildtarget' and 'variant' lists must be the same."
+ buildtarget = []
+ for bt in env['buildtarget']:
+ if SCons.Util.is_String(bt):
+ buildtarget.append(bt)
+ else:
+ buildtarget.append(bt.get_abspath())
+ else:
+ buildtarget = [env['buildtarget'].get_abspath()]
+ if len(buildtarget) == 1:
+ bt = buildtarget[0]
+ buildtarget = []
+ for _ in variants:
+ buildtarget.append(bt)
+
+ if not env.has_key('outdir') or env['outdir'] == None:
+ outdir = ['']
+ elif SCons.Util.is_String(env['outdir']):
+ outdir = [env['outdir']]
+ elif SCons.Util.is_List(env['outdir']):
+ if len(env['outdir']) != len(variants):
+ raise SCons.Errors.InternalError, \
+ "Sizes of 'outdir' and 'variant' lists must be the same."
+ outdir = []
+ for s in env['outdir']:
+ if SCons.Util.is_String(s):
+ outdir.append(s)
+ else:
+ outdir.append(s.get_abspath())
+ else:
+ outdir = [env['outdir'].get_abspath()]
+ if len(outdir) == 1:
+ s = outdir[0]
+ outdir = []
+ for v in variants:
+ outdir.append(s)
+
+ if not env.has_key('runfile') or env['runfile'] == None:
+ runfile = buildtarget[-1:]
+ elif SCons.Util.is_String(env['runfile']):
+ runfile = [env['runfile']]
+ elif SCons.Util.is_List(env['runfile']):
+ if len(env['runfile']) != len(variants):
+ raise SCons.Errors.InternalError, \
+ "Sizes of 'runfile' and 'variant' lists must be the same."
+ runfile = []
+ for s in env['runfile']:
+ if SCons.Util.is_String(s):
+ runfile.append(s)
+ else:
+ runfile.append(s.get_abspath())
+ else:
+ runfile = [env['runfile'].get_abspath()]
+ if len(runfile) == 1:
+ s = runfile[0]
+ runfile = []
+ for v in variants:
+ runfile.append(s)
+
+ self.sconscript = env['MSVSSCONSCRIPT']
+
+ cmdargs = env.get('cmdargs', '')
+
+ self.env = env
+
+ if self.env.has_key('name'):
+ self.name = self.env['name']
+ else:
+ self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
+ self.name = self.env.subst(self.name)
+
+ sourcenames = [
+ 'Source Files',
+ 'Header Files',
+ 'Local Headers',
+ 'Resource Files',
+ 'Other Files']
+
+ self.sources = {}
+ for n in sourcenames:
+ self.sources[n] = []
+
+ self.configs = {}
+
+ self.nokeep = 0
+ if env.has_key('nokeep') and env['variant'] != 0:
+ self.nokeep = 1
+
+ if self.nokeep == 0 and os.path.exists(self.dspabs):
+ self.Parse()
+
+ for t in zip(sourcenames,self.srcargs):
+ if self.env.has_key(t[1]):
+ if SCons.Util.is_List(self.env[t[1]]):
+ for i in self.env[t[1]]:
+ if not i in self.sources[t[0]]:
+ self.sources[t[0]].append(i)
+ else:
+ if not self.env[t[1]] in self.sources[t[0]]:
+ self.sources[t[0]].append(self.env[t[1]])
+
+ for n in sourcenames:
+ # TODO(1.5):
+ #self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower()))
+ self.sources[n].sort(lambda a, b: cmp(string.lower(a), string.lower(b)))
+
+ def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile):
+ config = Config()
+ config.buildtarget = buildtarget
+ config.outdir = outdir
+ config.cmdargs = cmdargs
+ config.runfile = runfile
+
+ match = re.match('(.*)\|(.*)', variant)
+ if match:
+ config.variant = match.group(1)
+ config.platform = match.group(2)
+ else:
+ config.variant = variant
+ config.platform = 'Win32'
+
+ self.configs[variant] = config
+ print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'"
+
+ for i in range(len(variants)):
+ AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs)
+
+ self.platforms = []
+ for key in self.configs.keys():
+ platform = self.configs[key].platform
+ if not platform in self.platforms:
+ self.platforms.append(platform)
+
+ def Build(self):
+ pass
+
+V6DSPHeader = """\
+# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) External Target" 0x0106
+
+CFG=%(name)s - Win32 %(confkey)s
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "%(name)s.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+"""
+
+class _GenerateV6DSP(_DSPGenerator):
+ """Generates a Project file for MSVS 6.0"""
+
+ def PrintHeader(self):
+ # pick a default config
+ confkeys = self.configs.keys()
+ confkeys.sort()
+
+ name = self.name
+ confkey = confkeys[0]
+
+ self.file.write(V6DSPHeader % locals())
+
+ for kind in confkeys:
+ self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind))
+
+ self.file.write('!MESSAGE \n\n')
+
+ def PrintProject(self):
+ name = self.name
+ self.file.write('# Begin Project\n'
+ '# PROP AllowPerConfigDependencies 0\n'
+ '# PROP Scc_ProjName ""\n'
+ '# PROP Scc_LocalPath ""\n\n')
+
+ first = 1
+ confkeys = self.configs.keys()
+ confkeys.sort()
+ for kind in confkeys:
+ outdir = self.configs[kind].outdir
+ buildtarget = self.configs[kind].buildtarget
+ if first == 1:
+ self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
+ first = 0
+ else:
+ self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
+
+ env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
+ if not env_has_buildtarget:
+ self.env['MSVSBUILDTARGET'] = buildtarget
+
+ # have to write this twice, once with the BASE settings, and once without
+ for base in ("BASE ",""):
+ self.file.write('# PROP %sUse_MFC 0\n'
+ '# PROP %sUse_Debug_Libraries ' % (base, base))
+ # TODO(1.5):
+ #if kind.lower().find('debug') < 0:
+ if string.find(string.lower(kind), 'debug') < 0:
+ self.file.write('0\n')
+ else:
+ self.file.write('1\n')
+ self.file.write('# PROP %sOutput_Dir "%s"\n'
+ '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir))
+ cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1)
+ self.file.write('# PROP %sCmd_Line "%s"\n'
+ '# PROP %sRebuild_Opt "-c && %s"\n'
+ '# PROP %sTarget_File "%s"\n'
+ '# PROP %sBsc_Name ""\n'
+ '# PROP %sTarget_Dir ""\n'\
+ %(base,cmd,base,cmd,base,buildtarget,base,base))
+
+ if not env_has_buildtarget:
+ del self.env['MSVSBUILDTARGET']
+
+ self.file.write('\n!ENDIF\n\n'
+ '# Begin Target\n\n')
+ for kind in confkeys:
+ self.file.write('# Name "%s - Win32 %s"\n' % (name,kind))
+ self.file.write('\n')
+ first = 0
+ for kind in confkeys:
+ if first == 0:
+ self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
+ first = 1
+ else:
+ self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
+ self.file.write('!ENDIF \n\n')
+ self.PrintSourceFiles()
+ self.file.write('# End Target\n'
+ '# End Project\n')
+
+ if self.nokeep == 0:
+ # now we pickle some data and add it to the file -- MSDEV will ignore it.
+ pdata = pickle.dumps(self.configs,1)
+ pdata = base64.encodestring(pdata)
+ self.file.write(pdata + '\n')
+ pdata = pickle.dumps(self.sources,1)
+ pdata = base64.encodestring(pdata)
+ self.file.write(pdata + '\n')
+
+ def PrintSourceFiles(self):
+ categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat',
+ 'Header Files': 'h|hpp|hxx|hm|inl',
+ 'Local Headers': 'h|hpp|hxx|hm|inl',
+ 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe',
+ 'Other Files': ''}
+
+ cats = categories.keys()
+ # TODO(1.5):
+ #cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
+ cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b)))
+ for kind in cats:
+ if not self.sources[kind]:
+ continue # skip empty groups
+
+ self.file.write('# Begin Group "' + kind + '"\n\n')
+ # TODO(1.5)
+ #typelist = categories[kind].replace('|', ';')
+ typelist = string.replace(categories[kind], '|', ';')
+ self.file.write('# PROP Default_Filter "' + typelist + '"\n')
+
+ for file in self.sources[kind]:
+ file = os.path.normpath(file)
+ self.file.write('# Begin Source File\n\n'
+ 'SOURCE="' + file + '"\n'
+ '# End Source File\n')
+ self.file.write('# End Group\n')
+
+ # add the SConscript file outside of the groups
+ self.file.write('# Begin Source File\n\n'
+ 'SOURCE="' + str(self.sconscript) + '"\n'
+ '# End Source File\n')
+
+ def Parse(self):
+ try:
+ dspfile = open(self.dspabs,'r')
+ except IOError:
+ return # doesn't exist yet, so can't add anything to configs.
+
+ line = dspfile.readline()
+ while line:
+ # TODO(1.5):
+ #if line.find("# End Project") > -1:
+ if string.find(line, "# End Project") > -1:
+ break
+ line = dspfile.readline()
+
+ line = dspfile.readline()
+ datas = line
+ while line and line != '\n':
+ line = dspfile.readline()
+ datas = datas + line
+
+ # OK, we've found our little pickled cache of data.
+ try:
+ datas = base64.decodestring(datas)
+ data = pickle.loads(datas)
+ except KeyboardInterrupt:
+ raise
+ except:
+ return # unable to unpickle any data for some reason
+
+ self.configs.update(data)
+
+ data = None
+ line = dspfile.readline()
+ datas = line
+ while line and line != '\n':
+ line = dspfile.readline()
+ datas = datas + line
+
+ # OK, we've found our little pickled cache of data.
+ # it has a "# " in front of it, so we strip that.
+ try:
+ datas = base64.decodestring(datas)
+ data = pickle.loads(datas)
+ except KeyboardInterrupt:
+ raise
+ except:
+ return # unable to unpickle any data for some reason
+
+ self.sources.update(data)
+
+ def Build(self):
+ try:
+ self.file = open(self.dspabs,'w')
+ except IOError, detail:
+ raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail)
+ else:
+ self.PrintHeader()
+ self.PrintProject()
+ self.file.close()
+
+V7DSPHeader = """\
+<?xml version="1.0" encoding = "%(encoding)s"?>
+<VisualStudioProject
+\tProjectType="Visual C++"
+\tVersion="%(versionstr)s"
+\tName="%(name)s"
+%(scc_attrs)s
+\tKeyword="MakeFileProj">
+"""
+
+V7DSPConfiguration = """\
+\t\t<Configuration
+\t\t\tName="%(variant)s|%(platform)s"
+\t\t\tOutputDirectory="%(outdir)s"
+\t\t\tIntermediateDirectory="%(outdir)s"
+\t\t\tConfigurationType="0"
+\t\t\tUseOfMFC="0"
+\t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
+\t\t\t<Tool
+\t\t\t\tName="VCNMakeTool"
+\t\t\t\tBuildCommandLine="%(buildcmd)s"
+\t\t\t\tCleanCommandLine="%(cleancmd)s"
+\t\t\t\tRebuildCommandLine="%(rebuildcmd)s"
+\t\t\t\tOutput="%(runfile)s"/>
+\t\t</Configuration>
+"""
+
+V8DSPHeader = """\
+<?xml version="1.0" encoding="%(encoding)s"?>
+<VisualStudioProject
+\tProjectType="Visual C++"
+\tVersion="%(versionstr)s"
+\tName="%(name)s"
+%(scc_attrs)s
+\tRootNamespace="%(name)s"
+\tKeyword="MakeFileProj">
+"""
+
+V8DSPConfiguration = """\
+\t\t<Configuration
+\t\t\tName="%(variant)s|%(platform)s"
+\t\t\tConfigurationType="0"
+\t\t\tUseOfMFC="0"
+\t\t\tATLMinimizesCRunTimeLibraryUsage="false"
+\t\t\t>
+\t\t\t<Tool
+\t\t\t\tName="VCNMakeTool"
+\t\t\t\tBuildCommandLine="%(buildcmd)s"
+\t\t\t\tReBuildCommandLine="%(rebuildcmd)s"
+\t\t\t\tCleanCommandLine="%(cleancmd)s"
+\t\t\t\tOutput="%(runfile)s"
+\t\t\t\tPreprocessorDefinitions="%(preprocdefs)s"
+\t\t\t\tIncludeSearchPath="%(includepath)s"
+\t\t\t\tForcedIncludes=""
+\t\t\t\tAssemblySearchPath=""
+\t\t\t\tForcedUsingAssemblies=""
+\t\t\t\tCompileAsManaged=""
+\t\t\t/>
+\t\t</Configuration>
+"""
+class _GenerateV7DSP(_DSPGenerator):
+ """Generates a Project file for MSVS .NET"""
+
+ def __init__(self, dspfile, source, env):
+ _DSPGenerator.__init__(self, dspfile, source, env)
+ self.version = env['MSVS_VERSION']
+ self.version_num, self.suite = msvs_parse_version(self.version)
+ if self.version_num >= 8.0:
+ self.versionstr = '8.00'
+ self.dspheader = V8DSPHeader
+ self.dspconfiguration = V8DSPConfiguration
+ else:
+ if self.version_num >= 7.1:
+ self.versionstr = '7.10'
+ else:
+ self.versionstr = '7.00'
+ self.dspheader = V7DSPHeader
+ self.dspconfiguration = V7DSPConfiguration
+ self.file = None
+
+ def PrintHeader(self):
+ env = self.env
+ versionstr = self.versionstr
+ name = self.name
+ encoding = self.env.subst('$MSVSENCODING')
+ scc_provider = env.get('MSVS_SCC_PROVIDER', '')
+ scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
+ scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
+ scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
+ project_guid = env.get('MSVS_PROJECT_GUID', '')
+ if self.version_num >= 8.0 and not project_guid:
+ project_guid = _generateGUID(self.dspfile, '')
+ if scc_provider != '':
+ scc_attrs = ('\tProjectGUID="%s"\n'
+ '\tSccProjectName="%s"\n'
+ '\tSccAuxPath="%s"\n'
+ '\tSccLocalPath="%s"\n'
+ '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider))
+ else:
+ scc_attrs = ('\tProjectGUID="%s"\n'
+ '\tSccProjectName="%s"\n'
+ '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path))
+
+ self.file.write(self.dspheader % locals())
+
+ self.file.write('\t<Platforms>\n')
+ for platform in self.platforms:
+ self.file.write(
+ '\t\t<Platform\n'
+ '\t\t\tName="%s"/>\n' % platform)
+ self.file.write('\t</Platforms>\n')
+
+ if self.version_num >= 8.0:
+ self.file.write('\t<ToolFiles>\n'
+ '\t</ToolFiles>\n')
+
+ def PrintProject(self):
+ self.file.write('\t<Configurations>\n')
+
+ confkeys = self.configs.keys()
+ confkeys.sort()
+ for kind in confkeys:
+ variant = self.configs[kind].variant
+ platform = self.configs[kind].platform
+ outdir = self.configs[kind].outdir
+ buildtarget = self.configs[kind].buildtarget
+ runfile = self.configs[kind].runfile
+ cmdargs = self.configs[kind].cmdargs
+
+ env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
+ if not env_has_buildtarget:
+ self.env['MSVSBUILDTARGET'] = buildtarget
+
+ starting = 'echo Starting SCons && '
+ if cmdargs:
+ cmdargs = ' ' + cmdargs
+ else:
+ cmdargs = ''
+ buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs)
+ rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs)
+ cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs)
+
+ # TODO(1.5)
+ #preprocdefs = xmlify(';'.join(self.env.get('CPPDEFINES', [])))
+ #includepath = xmlify(';'.join(self.env.get('CPPPATH', [])))
+ preprocdefs = xmlify(string.join(processDefines(self.env.get('CPPDEFINES', [])), ';'))
+ includepath = xmlify(string.join(self.env.get('CPPPATH', []), ';'))
+
+ if not env_has_buildtarget:
+ del self.env['MSVSBUILDTARGET']
+
+ self.file.write(self.dspconfiguration % locals())
+
+ self.file.write('\t</Configurations>\n')
+
+ if self.version_num >= 7.1:
+ self.file.write('\t<References>\n'
+ '\t</References>\n')
+
+ self.PrintSourceFiles()
+
+ self.file.write('</VisualStudioProject>\n')
+
+ if self.nokeep == 0:
+ # now we pickle some data and add it to the file -- MSDEV will ignore it.
+ pdata = pickle.dumps(self.configs,1)
+ pdata = base64.encodestring(pdata)
+ self.file.write('<!-- SCons Data:\n' + pdata + '\n')
+ pdata = pickle.dumps(self.sources,1)
+ pdata = base64.encodestring(pdata)
+ self.file.write(pdata + '-->\n')
+
+ def printSources(self, hierarchy, commonprefix):
+ sorteditems = hierarchy.items()
+ # TODO(1.5):
+ #sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower()))
+ sorteditems.sort(lambda a, b: cmp(string.lower(a[0]), string.lower(b[0])))
+
+ # First folders, then files
+ for key, value in sorteditems:
+ if SCons.Util.is_Dict(value):
+ self.file.write('\t\t\t<Filter\n'
+ '\t\t\t\tName="%s"\n'
+ '\t\t\t\tFilter="">\n' % (key))
+ self.printSources(value, commonprefix)
+ self.file.write('\t\t\t</Filter>\n')
+
+ for key, value in sorteditems:
+ if SCons.Util.is_String(value):
+ file = value
+ if commonprefix:
+ file = os.path.join(commonprefix, value)
+ file = os.path.normpath(file)
+ self.file.write('\t\t\t<File\n'
+ '\t\t\t\tRelativePath="%s">\n'
+ '\t\t\t</File>\n' % (file))
+
+ def PrintSourceFiles(self):
+ categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
+ 'Header Files': 'h;hpp;hxx;hm;inl',
+ 'Local Headers': 'h;hpp;hxx;hm;inl',
+ 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe',
+ 'Other Files': ''}
+
+ self.file.write('\t<Files>\n')
+
+ cats = categories.keys()
+ # TODO(1.5)
+ #cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
+ cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b)))
+ cats = filter(lambda k, s=self: s.sources[k], cats)
+ for kind in cats:
+ if len(cats) > 1:
+ self.file.write('\t\t<Filter\n'
+ '\t\t\tName="%s"\n'
+ '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
+
+ sources = self.sources[kind]
+
+ # First remove any common prefix
+ commonprefix = None
+ if len(sources) > 1:
+ s = map(os.path.normpath, sources)
+ # take the dirname because the prefix may include parts
+ # of the filenames (e.g. if you have 'dir\abcd' and
+ # 'dir\acde' then the cp will be 'dir\a' )
+ cp = os.path.dirname( os.path.commonprefix(s) )
+ if cp and s[0][len(cp)] == os.sep:
+ # +1 because the filename starts after the separator
+ sources = map(lambda s, l=len(cp)+1: s[l:], sources)
+ commonprefix = cp
+ elif len(sources) == 1:
+ commonprefix = os.path.dirname( sources[0] )
+ sources[0] = os.path.basename( sources[0] )
+
+ hierarchy = makeHierarchy(sources)
+ self.printSources(hierarchy, commonprefix=commonprefix)
+
+ if len(cats)>1:
+ self.file.write('\t\t</Filter>\n')
+
+ # add the SConscript file outside of the groups
+ self.file.write('\t\t<File\n'
+ '\t\t\tRelativePath="%s">\n'
+ '\t\t</File>\n' % str(self.sconscript))
+
+ self.file.write('\t</Files>\n'
+ '\t<Globals>\n'
+ '\t</Globals>\n')
+
+ def Parse(self):
+ try:
+ dspfile = open(self.dspabs,'r')
+ except IOError:
+ return # doesn't exist yet, so can't add anything to configs.
+
+ line = dspfile.readline()
+ while line:
+ # TODO(1.5)
+ #if line.find('<!-- SCons Data:') > -1:
+ if string.find(line, '<!-- SCons Data:') > -1:
+ break
+ line = dspfile.readline()
+
+ line = dspfile.readline()
+ datas = line
+ while line and line != '\n':
+ line = dspfile.readline()
+ datas = datas + line
+
+ # OK, we've found our little pickled cache of data.
+ try:
+ datas = base64.decodestring(datas)
+ data = pickle.loads(datas)
+ except KeyboardInterrupt:
+ raise
+ except:
+ return # unable to unpickle any data for some reason
+
+ self.configs.update(data)
+
+ data = None
+ line = dspfile.readline()
+ datas = line
+ while line and line != '\n':
+ line = dspfile.readline()
+ datas = datas + line
+
+ # OK, we've found our little pickled cache of data.
+ try:
+ datas = base64.decodestring(datas)
+ data = pickle.loads(datas)
+ except KeyboardInterrupt:
+ raise
+ except:
+ return # unable to unpickle any data for some reason
+
+ self.sources.update(data)
+
+ def Build(self):
+ try:
+ self.file = open(self.dspabs,'w')
+ except IOError, detail:
+ raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail)
+ else:
+ self.PrintHeader()
+ self.PrintProject()
+ self.file.close()
+
+class _DSWGenerator:
+ """ Base class for DSW generators """
+ def __init__(self, dswfile, source, env):
+ self.dswfile = os.path.normpath(str(dswfile))
+ self.env = env
+
+ if not env.has_key('projects'):
+ raise SCons.Errors.UserError, \
+ "You must specify a 'projects' argument to create an MSVSSolution."
+ projects = env['projects']
+ if not SCons.Util.is_List(projects):
+ raise SCons.Errors.InternalError, \
+ "The 'projects' argument must be a list of nodes."
+ projects = SCons.Util.flatten(projects)
+ if len(projects) < 1:
+ raise SCons.Errors.UserError, \
+ "You must specify at least one project to create an MSVSSolution."
+ self.dspfiles = map(str, projects)
+
+ if self.env.has_key('name'):
+ self.name = self.env['name']
+ else:
+ self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0])
+ self.name = self.env.subst(self.name)
+
+ def Build(self):
+ pass
+
+class _GenerateV7DSW(_DSWGenerator):
+ """Generates a Solution file for MSVS .NET"""
+ def __init__(self, dswfile, source, env):
+ _DSWGenerator.__init__(self, dswfile, source, env)
+
+ self.file = None
+ self.version = self.env['MSVS_VERSION']
+ self.version_num, self.suite = msvs_parse_version(self.version)
+ self.versionstr = '7.00'
+ if self.version_num >= 8.0:
+ self.versionstr = '9.00'
+ elif self.version_num >= 7.1:
+ self.versionstr = '8.00'
+ if self.version_num >= 8.0:
+ self.versionstr = '9.00'
+
+ if env.has_key('slnguid') and env['slnguid']:
+ self.slnguid = env['slnguid']
+ else:
+ self.slnguid = _generateGUID(dswfile, self.name)
+
+ self.configs = {}
+
+ self.nokeep = 0
+ if env.has_key('nokeep') and env['variant'] != 0:
+ self.nokeep = 1
+
+ if self.nokeep == 0 and os.path.exists(self.dswfile):
+ self.Parse()
+
+ def AddConfig(self, variant, dswfile=dswfile):
+ config = Config()
+
+ match = re.match('(.*)\|(.*)', variant)
+ if match:
+ config.variant = match.group(1)
+ config.platform = match.group(2)
+ else:
+ config.variant = variant
+ config.platform = 'Win32'
+
+ self.configs[variant] = config
+ print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'"
+
+ if not env.has_key('variant'):
+ raise SCons.Errors.InternalError, \
+ "You must specify a 'variant' argument (i.e. 'Debug' or " +\
+ "'Release') to create an MSVS Solution File."
+ elif SCons.Util.is_String(env['variant']):
+ AddConfig(self, env['variant'])
+ elif SCons.Util.is_List(env['variant']):
+ for variant in env['variant']:
+ AddConfig(self, variant)
+
+ self.platforms = []
+ for key in self.configs.keys():
+ platform = self.configs[key].platform
+ if not platform in self.platforms:
+ self.platforms.append(platform)
+
+ def Parse(self):
+ try:
+ dswfile = open(self.dswfile,'r')
+ except IOError:
+ return # doesn't exist yet, so can't add anything to configs.
+
+ line = dswfile.readline()
+ while line:
+ if line[:9] == "EndGlobal":
+ break
+ line = dswfile.readline()
+
+ line = dswfile.readline()
+ datas = line
+ while line:
+ line = dswfile.readline()
+ datas = datas + line
+
+ # OK, we've found our little pickled cache of data.
+ try:
+ datas = base64.decodestring(datas)
+ data = pickle.loads(datas)
+ except KeyboardInterrupt:
+ raise
+ except:
+ return # unable to unpickle any data for some reason
+
+ self.configs.update(data)
+
+ def PrintSolution(self):
+ """Writes a solution file"""
+ self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr )
+ if self.version_num >= 8.0:
+ self.file.write('# Visual Studio 2005\n')
+ for p in self.dspfiles:
+ name = os.path.basename(p)
+ base, suffix = SCons.Util.splitext(name)
+ if suffix == '.vcproj':
+ name = base
+ guid = _generateGUID(p, '')
+ self.file.write('Project("%s") = "%s", "%s", "%s"\n'
+ % ( external_makefile_guid, name, p, guid ) )
+ if self.version_num >= 7.1 and self.version_num < 8.0:
+ self.file.write('\tProjectSection(ProjectDependencies) = postProject\n'
+ '\tEndProjectSection\n')
+ self.file.write('EndProject\n')
+
+ self.file.write('Global\n')
+
+ env = self.env
+ if env.has_key('MSVS_SCC_PROVIDER'):
+ dspfile_base = os.path.basename(self.dspfile)
+ slnguid = self.slnguid
+ scc_provider = env.get('MSVS_SCC_PROVIDER', '')
+ scc_provider = string.replace(scc_provider, ' ', r'\u0020')
+ scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
+ # scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
+ scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
+ scc_project_base_path = env.get('MSVS_SCC_PROJECT_BASE_PATH', '')
+ # project_guid = env.get('MSVS_PROJECT_GUID', '')
+
+ self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n'
+ '\t\tSccNumberOfProjects = 2\n'
+ '\t\tSccProjectUniqueName0 = %(dspfile_base)s\n'
+ '\t\tSccLocalPath0 = %(scc_local_path)s\n'
+ '\t\tCanCheckoutShared = true\n'
+ '\t\tSccProjectFilePathRelativizedFromConnection0 = %(scc_project_base_path)s\n'
+ '\t\tSccProjectName1 = %(scc_project_name)s\n'
+ '\t\tSccLocalPath1 = %(scc_local_path)s\n'
+ '\t\tSccProvider1 = %(scc_provider)s\n'
+ '\t\tCanCheckoutShared = true\n'
+ '\t\tSccProjectFilePathRelativizedFromConnection1 = %(scc_project_base_path)s\n'
+ '\t\tSolutionUniqueID = %(slnguid)s\n'
+ '\tEndGlobalSection\n' % locals())
+
+ if self.version_num >= 8.0:
+ self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
+ else:
+ self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n')
+
+ confkeys = self.configs.keys()
+ confkeys.sort()
+ cnt = 0
+ for name in confkeys:
+ variant = self.configs[name].variant
+ platform = self.configs[name].platform
+ if self.version_num >= 8.0:
+ self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform))
+ else:
+ self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant))
+ cnt = cnt + 1
+ self.file.write('\tEndGlobalSection\n')
+ if self.version_num < 7.1:
+ self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n'
+ '\tEndGlobalSection\n')
+ if self.version_num >= 8.0:
+ self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
+ else:
+ self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
+
+ for name in confkeys:
+ variant = self.configs[name].variant
+ platform = self.configs[name].platform
+ if self.version_num >= 8.0:
+ for p in self.dspfiles:
+ guid = _generateGUID(p, '')
+ self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n'
+ '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform))
+ else:
+ for p in self.dspfiles:
+ guid = _generateGUID(p, '')
+ self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
+ '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform))
+
+ self.file.write('\tEndGlobalSection\n')
+
+ if self.version_num >= 8.0:
+ self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n'
+ '\t\tHideSolutionNode = FALSE\n'
+ '\tEndGlobalSection\n')
+ else:
+ self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
+ '\tEndGlobalSection\n'
+ '\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
+ '\tEndGlobalSection\n')
+ self.file.write('EndGlobal\n')
+ if self.nokeep == 0:
+ pdata = pickle.dumps(self.configs,1)
+ pdata = base64.encodestring(pdata)
+ self.file.write(pdata + '\n')
+
+ def Build(self):
+ try:
+ self.file = open(self.dswfile,'w')
+ except IOError, detail:
+ raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
+ else:
+ self.PrintSolution()
+ self.file.close()
+
+V6DSWHeader = """\
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "%(name)s"="%(dspfile)s" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+"""
+
+class _GenerateV6DSW(_DSWGenerator):
+ """Generates a Workspace file for MSVS 6.0"""
+
+ def PrintWorkspace(self):
+ """ writes a DSW file """
+ name = self.name
+ dspfile = self.dspfiles[0]
+ self.file.write(V6DSWHeader % locals())
+
+ def Build(self):
+ try:
+ self.file = open(self.dswfile,'w')
+ except IOError, detail:
+ raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
+ else:
+ self.PrintWorkspace()
+ self.file.close()
+
+
+def GenerateDSP(dspfile, source, env):
+ """Generates a Project file based on the version of MSVS that is being used"""
+
+ version_num = 6.0
+ if env.has_key('MSVS_VERSION'):
+ version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
+ if version_num >= 7.0:
+ g = _GenerateV7DSP(dspfile, source, env)
+ g.Build()
+ else:
+ g = _GenerateV6DSP(dspfile, source, env)
+ g.Build()
+
+def GenerateDSW(dswfile, source, env):
+ """Generates a Solution/Workspace file based on the version of MSVS that is being used"""
+
+ version_num = 6.0
+ if env.has_key('MSVS_VERSION'):
+ version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
+ if version_num >= 7.0:
+ g = _GenerateV7DSW(dswfile, source, env)
+ g.Build()
+ else:
+ g = _GenerateV6DSW(dswfile, source, env)
+ g.Build()
+
+
+##############################################################################
+# Above here are the classes and functions for generation of
+# DSP/DSW/SLN/VCPROJ files.
+##############################################################################
+
+def GetMSVSProjectSuffix(target, source, env, for_signature):
+ return env['MSVS']['PROJECTSUFFIX']
+
+def GetMSVSSolutionSuffix(target, source, env, for_signature):
+ return env['MSVS']['SOLUTIONSUFFIX']
+
+def GenerateProject(target, source, env):
+ # generate the dsp file, according to the version of MSVS.
+ builddspfile = target[0]
+ dspfile = builddspfile.srcnode()
+
+ # this detects whether or not we're using a VariantDir
+ if not dspfile is builddspfile:
+ try:
+ bdsp = open(str(builddspfile), "w+")
+ except IOError, detail:
+ print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
+ raise
+
+ bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
+
+ GenerateDSP(dspfile, source, env)
+
+ if env.get('auto_build_solution', 1):
+ builddswfile = target[1]
+ dswfile = builddswfile.srcnode()
+
+ if not dswfile is builddswfile:
+
+ try:
+ bdsw = open(str(builddswfile), "w+")
+ except IOError, detail:
+ print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
+ raise
+
+ bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
+
+ GenerateDSW(dswfile, source, env)
+
+def GenerateSolution(target, source, env):
+ GenerateDSW(target[0], source, env)
+
+def projectEmitter(target, source, env):
+ """Sets up the DSP dependencies."""
+
+ # todo: Not sure what sets source to what user has passed as target,
+ # but this is what happens. When that is fixed, we also won't have
+ # to make the user always append env['MSVSPROJECTSUFFIX'] to target.
+ if source[0] == target[0]:
+ source = []
+
+ # make sure the suffix is correct for the version of MSVS we're running.
+ (base, suff) = SCons.Util.splitext(str(target[0]))
+ suff = env.subst('$MSVSPROJECTSUFFIX')
+ target[0] = base + suff
+
+ if not source:
+ source = 'prj_inputs:'
+ source = source + env.subst('$MSVSSCONSCOM', 1)
+ source = source + env.subst('$MSVSENCODING', 1)
+
+ if env.has_key('buildtarget') and env['buildtarget'] != None:
+ if SCons.Util.is_String(env['buildtarget']):
+ source = source + ' "%s"' % env['buildtarget']
+ elif SCons.Util.is_List(env['buildtarget']):
+ for bt in env['buildtarget']:
+ if SCons.Util.is_String(bt):
+ source = source + ' "%s"' % bt
+ else:
+ try: source = source + ' "%s"' % bt.get_abspath()
+ except AttributeError: raise SCons.Errors.InternalError, \
+ "buildtarget can be a string, a node, a list of strings or nodes, or None"
+ else:
+ try: source = source + ' "%s"' % env['buildtarget'].get_abspath()
+ except AttributeError: raise SCons.Errors.InternalError, \
+ "buildtarget can be a string, a node, a list of strings or nodes, or None"
+
+ if env.has_key('outdir') and env['outdir'] != None:
+ if SCons.Util.is_String(env['outdir']):
+ source = source + ' "%s"' % env['outdir']
+ elif SCons.Util.is_List(env['outdir']):
+ for s in env['outdir']:
+ if SCons.Util.is_String(s):
+ source = source + ' "%s"' % s
+ else:
+ try: source = source + ' "%s"' % s.get_abspath()
+ except AttributeError: raise SCons.Errors.InternalError, \
+ "outdir can be a string, a node, a list of strings or nodes, or None"
+ else:
+ try: source = source + ' "%s"' % env['outdir'].get_abspath()
+ except AttributeError: raise SCons.Errors.InternalError, \
+ "outdir can be a string, a node, a list of strings or nodes, or None"
+
+ if env.has_key('name'):
+ if SCons.Util.is_String(env['name']):
+ source = source + ' "%s"' % env['name']
+ else:
+ raise SCons.Errors.InternalError, "name must be a string"
+
+ if env.has_key('variant'):
+ if SCons.Util.is_String(env['variant']):
+ source = source + ' "%s"' % env['variant']
+ elif SCons.Util.is_List(env['variant']):
+ for variant in env['variant']:
+ if SCons.Util.is_String(variant):
+ source = source + ' "%s"' % variant
+ else:
+ raise SCons.Errors.InternalError, "name must be a string or a list of strings"
+ else:
+ raise SCons.Errors.InternalError, "variant must be a string or a list of strings"
+ else:
+ raise SCons.Errors.InternalError, "variant must be specified"
+
+ for s in _DSPGenerator.srcargs:
+ if env.has_key(s):
+ if SCons.Util.is_String(env[s]):
+ source = source + ' "%s' % env[s]
+ elif SCons.Util.is_List(env[s]):
+ for t in env[s]:
+ if SCons.Util.is_String(t):
+ source = source + ' "%s"' % t
+ else:
+ raise SCons.Errors.InternalError, s + " must be a string or a list of strings"
+ else:
+ raise SCons.Errors.InternalError, s + " must be a string or a list of strings"
+
+ source = source + ' "%s"' % str(target[0])
+ source = [SCons.Node.Python.Value(source)]
+
+ targetlist = [target[0]]
+ sourcelist = source
+
+ if env.get('auto_build_solution', 1):
+ env['projects'] = targetlist
+ t, s = solutionEmitter(target, target, env)
+ targetlist = targetlist + t
+
+ return (targetlist, sourcelist)
+
+def solutionEmitter(target, source, env):
+ """Sets up the DSW dependencies."""
+
+ # todo: Not sure what sets source to what user has passed as target,
+ # but this is what happens. When that is fixed, we also won't have
+ # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target.
+ if source[0] == target[0]:
+ source = []
+
+ # make sure the suffix is correct for the version of MSVS we're running.
+ (base, suff) = SCons.Util.splitext(str(target[0]))
+ suff = env.subst('$MSVSSOLUTIONSUFFIX')
+ target[0] = base + suff
+
+ if not source:
+ source = 'sln_inputs:'
+
+ if env.has_key('name'):
+ if SCons.Util.is_String(env['name']):
+ source = source + ' "%s"' % env['name']
+ else:
+ raise SCons.Errors.InternalError, "name must be a string"
+
+ if env.has_key('variant'):
+ if SCons.Util.is_String(env['variant']):
+ source = source + ' "%s"' % env['variant']
+ elif SCons.Util.is_List(env['variant']):
+ for variant in env['variant']:
+ if SCons.Util.is_String(variant):
+ source = source + ' "%s"' % variant
+ else:
+ raise SCons.Errors.InternalError, "name must be a string or a list of strings"
+ else:
+ raise SCons.Errors.InternalError, "variant must be a string or a list of strings"
+ else:
+ raise SCons.Errors.InternalError, "variant must be specified"
+
+ if env.has_key('slnguid'):
+ if SCons.Util.is_String(env['slnguid']):
+ source = source + ' "%s"' % env['slnguid']
+ else:
+ raise SCons.Errors.InternalError, "slnguid must be a string"
+
+ if env.has_key('projects'):
+ if SCons.Util.is_String(env['projects']):
+ source = source + ' "%s"' % env['projects']
+ elif SCons.Util.is_List(env['projects']):
+ for t in env['projects']:
+ if SCons.Util.is_String(t):
+ source = source + ' "%s"' % t
+
+ source = source + ' "%s"' % str(target[0])
+ source = [SCons.Node.Python.Value(source)]
+
+ return ([target[0]], source)
+
+projectAction = SCons.Action.Action(GenerateProject, None)
+
+solutionAction = SCons.Action.Action(GenerateSolution, None)
+
+projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
+ suffix = '$MSVSPROJECTSUFFIX',
+ emitter = projectEmitter)
+
+solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM',
+ suffix = '$MSVSSOLUTIONSUFFIX',
+ emitter = solutionEmitter)
+
+default_MSVS_SConscript = None
+
+def generate(env):
+ """Add Builders and construction variables for Microsoft Visual
+ Studio project files to an Environment."""
+ try:
+ env['BUILDERS']['MSVSProject']
+ except KeyError:
+ env['BUILDERS']['MSVSProject'] = projectBuilder
+
+ try:
+ env['BUILDERS']['MSVSSolution']
+ except KeyError:
+ env['BUILDERS']['MSVSSolution'] = solutionBuilder
+
+ env['MSVSPROJECTCOM'] = projectAction
+ env['MSVSSOLUTIONCOM'] = solutionAction
+
+ if SCons.Script.call_stack:
+ # XXX Need to find a way to abstract this; the build engine
+ # shouldn't depend on anything in SCons.Script.
+ env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript
+ else:
+ global default_MSVS_SConscript
+ if default_MSVS_SConscript is None:
+ default_MSVS_SConscript = env.File('SConstruct')
+ env['MSVSSCONSCRIPT'] = default_MSVS_SConscript
+
+ env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env))
+ env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}'
+ env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS'
+ env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
+ env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
+ env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"'
+ env['MSVSENCODING'] = 'Windows-1252'
+
+ # Set-up ms tools paths for default version
+ msvc_setup_env_once(env)
+
+ if env.has_key('MSVS_VERSION'):
+ version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
+ else:
+ (version_num, suite) = (7.0, None) # guess at a default
+ if not env.has_key('MSVS'):
+ env['MSVS'] = {}
+ if (version_num < 7.0):
+ env['MSVS']['PROJECTSUFFIX'] = '.dsp'
+ env['MSVS']['SOLUTIONSUFFIX'] = '.dsw'
+ else:
+ env['MSVS']['PROJECTSUFFIX'] = '.vcproj'
+ env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
+
+ env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix
+ env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix
+ env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}'
+ env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}'
+ env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
+
+def exists(env):
+ return msvc_exists()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml
new file mode 100644
index 0000000..a04ee05
--- /dev/null
+++ b/src/engine/SCons/Tool/msvs.xml
@@ -0,0 +1,640 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="msvs">
+<summary>
+Sets construction variables for Microsoft Visual Studio.
+</summary>
+<sets>
+MSVSPROJECTCOM
+MSVSSOLUTIONCOM
+MSVSSCONSCRIPT
+MSVSSCONS
+MSVSSCONSFLAGS
+MSVSSCONSCOM
+MSVSBUILDCOM
+MSVSREBUILDCOM
+MSVSCLEANCOM
+MSVSENCODING
+</sets>
+<uses>
+</uses>
+</tool>
+
+<builder name ="MSVSProject">
+<summary>
+Builds a Microsoft Visual Studio project file,
+and by default builds a solution file as well.
+
+This builds a Visual Studio project file, based on the version of
+Visual Studio that is configured (either the latest installed version,
+or the version specified by
+&cv-link-MSVS_VERSION;
+in the Environment constructor).
+For Visual Studio 6, it will generate a
+<filename>.dsp</filename>
+file.
+For Visual Studio 7 (.NET) and later versions, it will generate a
+<filename>.vcproj</filename>
+file.
+
+By default,
+this also generates a solution file
+for the specified project,
+a
+<filename>.dsw</filename>
+file for Visual Studio 6
+or a
+<filename>.sln</filename>
+file for Visual Studio 7 (.NET).
+This behavior may be disabled by specifying
+<literal>auto_build_solution=0</literal>
+when you call
+&b-MSVSProject;,
+in which case you presumably want to
+build the solution file(s)
+by calling the
+&b-MSVSSolution;
+Builder (see below).
+
+The &b-MSVSProject; builder
+takes several lists of filenames
+to be placed into the project file.
+These are currently limited to
+<literal>srcs</literal>,
+<literal>incs</literal>,
+<literal>localincs</literal>,
+<literal>resources</literal>,
+and
+<literal>misc</literal>.
+These are pretty self-explanatory, but it should be noted that these
+lists are added to the &cv-link-SOURCES; construction variable as strings,
+NOT as SCons File Nodes. This is because they represent file
+names to be added to the project file, not the source files used to
+build the project file.
+
+The above filename lists are all optional,
+although at least one must be specified
+for the resulting project file to be non-empty.
+
+In addition to the above lists of values,
+the following values may be specified:
+
+<literal>target</literal>:
+The name of the target
+<filename>.dsp</filename>
+or
+<filename>.vcproj</filename>
+file.
+The correct
+suffix for the version of Visual Studio must be used,
+but the
+&cv-link-MSVSPROJECTSUFFIX;
+construction variable
+will be defined to the correct value (see example below).
+
+<literal>variant</literal>:
+The name of this particular variant.
+For Visual Studio 7 projects,
+this can also be a list of variant names.
+These are typically things like "Debug" or "Release", but really
+can be anything you want.
+For Visual Studio 7 projects,
+they may also specify a target platform
+separated from the variant name by a
+<literal>|</literal>
+(vertical pipe)
+character:
+<literal>Debug|Xbox</literal>.
+The default target platform is Win32.
+Multiple calls to
+&b-MSVSProject;
+with different variants are allowed;
+all variants will be added to the project file with their appropriate
+build targets and sources.
+
+<literal>buildtarget</literal>:
+An optional string, node, or list of strings or nodes
+(one per build variant), to tell the Visual Studio debugger
+what output target to use in what build variant.
+The number of
+<literal>buildtarget</literal>
+entries must match the number of
+<literal>variant</literal>
+entries.
+
+<literal>runfile</literal>:
+The name of the file that Visual Studio 7 and later
+will run and debug.
+This appears as the value of the
+<literal>Output</literal>
+field in the resutling Visual Studio project file.
+If this is not specified,
+the default is the same as the specified
+<literal>buildtarget</literal>
+value.
+
+Note that because &SCons; always executes its build commands
+from the directory in which the &SConstruct; file is located,
+if you generate a project file in a different directory
+than the &SConstruct; directory,
+users will not be able to double-click
+on the file name in compilation error messages
+displayed in the Visual Studio console output window.
+This can be remedied by adding the
+Visual C/C++
+.B /FC
+compiler option to the &cv-link-CCFLAGS; variable
+so that the compiler will print
+the full path name of any
+files that cause compilation errors.
+
+Example usage:
+
+<example>
+barsrcs = ['bar.cpp'],
+barincs = ['bar.h'],
+barlocalincs = ['StdAfx.h']
+barresources = ['bar.rc','resource.h']
+barmisc = ['bar_readme.txt']
+
+dll = env.SharedLibrary(target = 'bar.dll',
+ source = barsrcs)
+
+env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'],
+ srcs = barsrcs,
+ incs = barincs,
+ localincs = barlocalincs,
+ resources = barresources,
+ misc = barmisc,
+ buildtarget = dll,
+ variant = 'Release')
+</example>
+</summary>
+</builder>
+
+<builder name ="MSVSSolution">
+<summary>
+Builds a Microsoft Visual Studio solution file.
+
+This builds a Visual Studio solution file,
+based on the version of Visual Studio that is configured
+(either the latest installed version,
+or the version specified by
+&cv-link-MSVS_VERSION;
+in the construction environment).
+For Visual Studio 6, it will generate a
+<filename>.dsw</filename>
+file.
+For Visual Studio 7 (.NET), it will
+generate a
+<filename>.sln</filename>
+file.
+
+The following values must be specified:
+
+<literal>target</literal>:
+The name of the target .dsw or .sln file. The correct
+suffix for the version of Visual Studio must be used, but the value
+&cv-link-MSVSSOLUTIONSUFFIX;
+will be defined to the correct value (see example below).
+
+<literal>variant</literal>:
+The name of this particular variant, or a list of variant
+names (the latter is only supported for MSVS 7 solutions). These are
+typically things like "Debug" or "Release", but really can be anything
+you want. For MSVS 7 they may also specify target platform, like this
+"Debug|Xbox". Default platform is Win32.
+
+<literal>projects</literal>:
+A list of project file names, or Project nodes returned by calls to the
+&b-MSVSProject;
+Builder,
+to be placed into the solution file.
+It should be noted that these file names are NOT added to the $SOURCES
+environment variable in form of files, but rather as strings. This
+is because they represent file names to be added to the solution file,
+not the source files used to build the solution file.
+
+(NOTE: Currently only one project is supported per solution.)
+
+Example Usage:
+
+<example>
+env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'],
+ projects = ['bar' + env['MSVSPROJECTSUFFIX']],
+ variant = 'Release')
+</example>
+</summary>
+</builder>
+
+<cvar name="MSVS">
+<summary>
+When the Microsoft Visual Studio tools are initialized, they set up
+this dictionary with the following keys:
+
+<envar>VERSION</envar>:
+the version of MSVS being used (can be set via
+&cv-link-MSVS_VERSION;)
+
+<envar>VERSIONS</envar>:
+the available versions of MSVS installed
+
+<envar>VCINSTALLDIR</envar>:
+installed directory of Visual C++
+
+<envar>VSINSTALLDIR</envar>:
+installed directory of Visual Studio
+
+<envar>FRAMEWORKDIR</envar>:
+installed directory of the .NET framework
+
+<envar>FRAMEWORKVERSIONS</envar>:
+list of installed versions of the .NET framework, sorted latest to oldest.
+
+<envar>FRAMEWORKVERSION</envar>:
+latest installed version of the .NET framework
+
+<envar>FRAMEWORKSDKDIR</envar>:
+installed location of the .NET SDK.
+
+<envar>PLATFORMSDKDIR</envar>:
+installed location of the Platform SDK.
+
+<envar>PLATFORMSDK_MODULES</envar>:
+dictionary of installed Platform SDK modules,
+where the dictionary keys are keywords for the various modules, and
+the values are 2-tuples where the first is the release date, and the
+second is the version number.
+
+If a value isn't set, it wasn't available in the registry.
+</summary>
+</cvar>
+
+<cvar name="MSVS_ARCH">
+<summary>
+Sets the architecture for which the generated project(s) should build.
+
+The default value is <literal>x86</literal>.
+<literal>amd64</literal> is also supported
+by &SCons; for some Visual Studio versions.
+Trying to set &cv-MSVS_ARCH; to an architecture that's not
+supported for a given Visual Studio version
+will generate an error.
+</summary>
+</cvar>
+
+<cvar name="MSVS_IGNORE_IDE_PATHS">
+<summary>
+Tells the MS Visual Studio tools to use minimal INCLUDE, LIB, and PATH settings,
+instead of the settings from the IDE.
+
+For Visual Studio, SCons will (by default) automatically determine
+where MSVS is installed, and use the LIB, INCLUDE, and PATH variables
+set by the IDE. You can override this behavior by setting these
+variables after Environment initialization, or by setting
+<envar>MSVS_IGNORE_IDE_PATHS = 1</envar>
+in the Environment initialization.
+Specifying this will not leave these unset, but will set them to a
+minimal set of paths needed to run the tools successfully.
+
+For VS6, the mininimal set is:
+<example>
+ INCLUDE:'&lt;VSDir&gt;\VC98\ATL\include;&lt;VSDir&gt;\VC98\MFC\include;&lt;VSDir&gt;\VC98\include'
+ LIB:'&lt;VSDir&gt;\VC98\MFC\lib;&lt;VSDir&gt;\VC98\lib'
+ PATH:'&lt;VSDir&gt;\Common\MSDev98\bin;&lt;VSDir&gt;\VC98\bin'
+</example>
+For VS7, it is:
+<example>
+ INCLUDE:'&lt;VSDir&gt;\Vc7\atlmfc\include;&lt;VSDir&gt;\Vc7\include'
+ LIB:'&lt;VSDir&gt;\Vc7\atlmfc\lib;&lt;VSDir&gt;\Vc7\lib'
+ PATH:'&lt;VSDir&gt;\Common7\Tools\bin;&lt;VSDir&gt;\Common7\Tools;&lt;VSDir&gt;\Vc7\bin'
+</example>
+
+Where '&lt;VSDir&gt;' is the installed location of Visual Studio.
+</summary>
+</cvar>
+
+<cvar name="MSVS_PROJECT_BASE_PATH">
+<summary>
+The string
+placed in a generated Microsoft Visual Studio solution file
+as the value of the
+<literal>SccProjectFilePathRelativizedFromConnection0</literal>
+and
+<literal>SccProjectFilePathRelativizedFromConnection1</literal>
+attributes of the
+<literal>GlobalSection(SourceCodeControl)</literal>
+section.
+There is no default value.
+</summary>
+</cvar>
+
+<cvar name="MSVS_PROJECT_GUID">
+<summary>
+The string
+placed in a generated Microsoft Visual Studio project file
+as the value of the
+<literal>ProjectGUID</literal>
+attribute.
+The string is also placed in the
+<literal>SolutionUniqueID</literal>
+attribute of the
+<literal>GlobalSection(SourceCodeControl)</literal>
+section of the Microsoft Visual Studio solution file.
+There is no default value.
+</summary>
+</cvar>
+
+<cvar name="MSVS_SCC_AUX_PATH">
+<summary>
+The path name
+placed in a generated Microsoft Visual Studio project file
+as the value of the
+<literal>SccAuxPath</literal>
+attribute
+if the
+<envar>MSVS_SCC_PROVIDER</envar>
+construction variable is also set.
+There is no default value.
+</summary>
+</cvar>
+
+<cvar name="MSVS_SCC_LOCAL_PATH">
+<summary>
+The path name
+placed in a generated Microsoft Visual Studio project file
+as the value of the
+<literal>SccLocalPath</literal>
+attribute
+if the
+<envar>MSVS_SCC_PROVIDER</envar>
+construction variable is also set.
+The path name is also placed in the
+<literal>SccLocalPath0</literal>
+and
+<literal>SccLocalPath1</literal>
+attributes of the
+<literal>GlobalSection(SourceCodeControl)</literal>
+section of the Microsoft Visual Studio solution file.
+There is no default value.
+</summary>
+</cvar>
+
+<cvar name="MSVS_SCC_PROJECT_NAME">
+<summary>
+The project name
+placed in a generated Microsoft Visual Studio project file
+as the value of the
+<literal>SccProjectName</literal>
+attribute.
+There is no default value.
+</summary>
+</cvar>
+
+<cvar name="MSVS_SCC_PROVIDER">
+<summary>
+The string
+placed in a generated Microsoft Visual Studio project file
+as the value of the
+<literal>SccProvider</literal>
+attribute.
+The string is also placed in the
+<literal>SccProvider1</literal>
+attribute of the
+<literal>GlobalSection(SourceCodeControl)</literal>
+section of the Microsoft Visual Studio solution file.
+There is no default value.
+</summary>
+</cvar>
+
+<cvar name="MSVS_USE_MFC_DIRS">
+<summary>
+Tells the MS Visual Studio tool(s) to use
+the MFC directories in its default paths
+for compiling and linking.
+The &cv-MSVS_USE_MFC_DIRS; variable has no effect if the
+<envar>INCLUDE</envar>
+or
+<envar>LIB</envar>
+environment variables are set explictly.
+
+Under Visual Studio version 6,
+setting
+&cv-MSVS_USE_MFC_DIRS;
+to a non-zero value
+adds the
+<filename>ATL\include</filename>
+and
+<filename>MFC\include</filename>
+directories to
+the default
+<envar>INCLUDE</envar>
+external environment variable,
+and adds the
+<filename>MFC\lib</filename>
+directory to
+the default
+<envar>LIB</envar>
+external environment variable.
+
+Under Visual Studio version 7,
+setting
+&cv-MSVS_USE_MFC_DIRS;
+to a non-zero value
+adds the
+<filename>atlmfc\include</filename>
+directory to the default
+<envar>INCLUDE</envar>
+external environment variable,
+and adds the
+<filename>atlmfc\lib</filename>
+directory to the default
+<envar>LIB</envar>
+external environment variable.
+
+Under Visual Studio version 8,
+setting
+&cv-MSVS_USE_MFC_DIRS;
+to a non-zero value will,
+by default,
+add the
+<filename>atlmfc\include</filename>
+directory to the default
+<envar>INCLUDE</envar>
+external environment variable,
+and the
+<filename>atlmfc\lib</filename>
+directory to the default
+<envar>LIB</envar>
+external environment variable.
+If, however, the
+<envar>['MSVS']['PLATFORMSDKDIR']</envar>
+variable is set,
+then the
+<filename>mfc</filename>
+and the
+<filename>atl</filename>
+subdirectories of the
+<envar>PLATFORMSDKDIR</envar>
+are added to the default value of the
+<envar>INCLUDE</envar>
+external environment variable,
+and the default value of the
+<envar>LIB</envar>
+external environment variable is left untouched.
+</summary>
+</cvar>
+
+<cvar name="MSVS_VERSION">
+<summary>
+Sets the preferred version of Microsoft Visual Studio to use.
+
+If &cv-MSVS_VERSION; is not set,
+&SCons; will (by default) select the latest version
+of Visual Studio installed on your system.
+So, if you have version 6 and version 7 (MSVS .NET) installed,
+it will prefer version 7.
+You can override this by
+specifying the
+<envar>MSVS_VERSION</envar>
+variable in the Environment initialization, setting it to the
+appropriate version ('6.0' or '7.0', for example).
+If the specified version isn't installed,
+tool initialization will fail.
+
+This is obsolete: use &cv-MSVC_VERSION; instead. If &cv-MSVS_VERSION; is set and
+&cv-MSVC_VERSION; is not, &cv-MSVC_VERSION; will be set automatically to &cv-MSVS_VERSION;.
+If both are set to different values, scons will raise an error.
+</summary>
+</cvar>
+
+<cvar name="MSVSBUILDCOM">
+<summary>
+The build command line placed in
+a generated Microsoft Visual Studio project file.
+The default is to have Visual Studio invoke SCons with any specified
+build targets.
+</summary>
+</cvar>
+
+<cvar name="MSVSCLEANCOM">
+<summary>
+The clean command line placed in
+a generated Microsoft Visual Studio project file.
+The default is to have Visual Studio invoke SCons with the -c option
+to remove any specified targets.
+</summary>
+</cvar>
+
+<cvar name="MSVSENCODING">
+<summary>
+The encoding string placed in
+a generated Microsoft Visual Studio project file.
+The default is encoding
+<literal>Windows-1252</literal>.
+</summary>
+</cvar>
+
+<cvar name="MSVSPROJECTCOM">
+<summary>
+The action used to generate Microsoft Visual Studio project files.
+</summary>
+</cvar>
+
+<cvar name="MSVSPROJECTSUFFIX">
+<summary>
+The suffix used for Microsoft Visual Studio project (DSP) files.
+The default value is
+<filename>.vcproj</filename>
+when using Visual Studio version 7.x (.NET)
+or later version,
+and
+<filename>.dsp</filename>
+when using earlier versions of Visual Studio.
+</summary>
+</cvar>
+
+<cvar name="MSVSREBUILDCOM">
+<summary>
+The rebuild command line placed in
+a generated Microsoft Visual Studio project file.
+The default is to have Visual Studio invoke SCons with any specified
+rebuild targets.
+</summary>
+</cvar>
+
+<cvar name="MSVSSCONS">
+<summary>
+The SCons used in generated Microsoft Visual Studio project files.
+The default is the version of SCons being
+used to generate the project file.
+</summary>
+</cvar>
+
+<cvar name="MSVSSCONSFLAGS">
+<summary>
+The SCons flags used in generated Microsoft Visual Studio
+project files.
+</summary>
+</cvar>
+
+<cvar name="MSVSSCONSCOM">
+<summary>
+The default SCons command used in generated Microsoft Visual Studio
+project files.
+</summary>
+</cvar>
+
+<cvar name="MSVSSCONSCRIPT">
+<summary>
+The sconscript file
+(that is,
+&SConstruct;
+or
+&SConscript;
+file)
+that will be invoked by Visual Studio
+project files
+(through the
+&cv-link-MSVSSCONSCOM;
+variable).
+The default is the same sconscript file
+that contains the call to
+&b-MSVSProject;
+to build the project file.
+</summary>
+</cvar>
+
+<cvar name="MSVSSOLUTIONCOM">
+<summary>
+The action used to generate Microsoft Visual Studio solution files.
+</summary>
+</cvar>
+
+<cvar name="MSVSSOLUTIONSUFFIX">
+<summary>
+The suffix used for Microsoft Visual Studio solution (DSW) files.
+The default value is
+<filename>.sln</filename>
+when using Visual Studio version 7.x (.NET),
+and
+<filename>.dsw</filename>
+when using earlier versions of Visual Studio.
+</summary>
+</cvar>
+
+<cvar name="SCONS_HOME">
+<summary>
+The (optional) path to the SCons library directory,
+initialized from the external environment.
+If set, this is used to construct a shorter and more
+efficient search path in the
+&cv-link-MSVSSCONS;
+command line executed
+from Microsoft Visual Studio project files.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py
new file mode 100644
index 0000000..08066fb
--- /dev/null
+++ b/src/engine/SCons/Tool/msvsTests.py
@@ -0,0 +1,757 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/msvsTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import string
+import sys
+import TestCmd
+import unittest
+import copy
+
+from SCons.Tool.msvs import *
+import SCons.Util
+import SCons.Warnings
+
+from SCons.Tool.MSCommon.common import debug
+
+from SCons.Tool.MSCommon import get_default_version, \
+ query_versions
+
+regdata_6a = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks]
+"sp3"=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup]
+"VsCommonDir"="C:\Program Files\Microsoft Visual Studio\Common"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Developer Network Library - Visual Studio 6.0a]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio\MSDN98\98VSa\1033"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio\VC98"
+''','\n')
+
+regdata_6b = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0]
+"InstallDir"="C:\VS6\Common\IDE\IDE98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks]
+"sp5"=""
+"latest"=dword:00000005
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup]
+"VsCommonDir"="C:\VS6\Common"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic]
+"ProductDir"="C:\VS6\VB98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++]
+"ProductDir"="C:\VS6\VC98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio]
+"ProductDir"="C:\VS6"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft VSEE Client]
+"ProductDir"="C:\VS6\Common\Tools"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Visual Studio 98]
+''','\n')
+
+regdata_7 = string.split(r'''
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0]
+"InstallDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"Source Directories"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\crt\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\atl\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\CrystalReports]
+@="#15007"
+"Package"="{F05E92C6-8346-11D3-B4AD-00A0C9B04E7B}"
+"ProductDetails"="#15009"
+"LogoID"="0"
+"PID"="#15008"
+"UseInterface"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual Basic.NET]
+@=""
+"DefaultProductAttribute"="VB"
+"Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}"
+"UseInterface"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual C#]
+@=""
+"Package"="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}"
+"UseInterface"=dword:00000001
+"DefaultProductAttribute"="C#"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\VisualC++]
+"UseInterface"=dword:00000001
+"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}"
+"DefaultProductAttribute"="VC"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup]
+"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"dw_dir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\MSDN]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Msdn\1033\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Servicing\SKU]
+"Visual Studio .NET Professional - English"="{D0610409-7D65-11D5-A54F-0090278A1BB8}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VB]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vb7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC#]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\VC#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Visual Studio .NET Professional - English]
+"InstallSuccess"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS]
+"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe"
+"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe"
+"MSMDir"="C:\Program Files\Common Files\Merge Modules\"
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\"
+"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\"
+"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\"
+"VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET\Setup\VSUpdate\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\BuildNumber]
+"1033"="7.0.9466"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\Pro]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32]
+@="{A54AAE91-30C2-11D3-87BF-A04A4CC10000}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]
+"Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\perl\bin;C:\cygwin\bin;c:\cygwin\usr\bin;C:\bin;C:\program files\perforce;C:\cygwin\usr\local\bin\i686-pc-cygwin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem"
+"Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib"
+"Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include"
+"Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src"
+"Reference Dirs"=""
+''','\n')
+
+regdata_7_1 = string.split(r'''
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1]
+@=""
+"Source Directories"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\crt\src\;C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\atl\"
+"ThisVersionSolutionCLSID"="{246C57AE-40DD-4d6b-9E8D-B0F5757BB2A8}"
+"ThisVersionDTECLSID"="{8CD2DD97-4EC1-4bc4-9359-89A3EEDD57A6}"
+"InstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\"
+"CLR Version"="v1.1.4322"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Smart Device Extensions]
+"UseInterface"=dword:00000001
+"VS7InstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\"
+"VBDeviceInstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VB7\"
+"CSharpDeviceInstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Visual Basic.NET]
+"UseInterface"=dword:00000001
+"Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}"
+"DefaultProductAttribute"="VB"
+@=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Visual C#]
+"DefaultProductAttribute"="C#"
+"UseInterface"=dword:00000001
+"Package"="{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}"
+@=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Visual JSharp]
+@=""
+"Package"="{E6FDF8B0-F3D1-11D4-8576-0002A516ECE8}"
+"UseInterface"=dword:00000001
+"DefaultProductAttribute"="Visual JSharp"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\VisualC++]
+"UseInterface"=dword:00000001
+"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}"
+"DefaultProductAttribute"="VC"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup]
+"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\"
+"dw_dir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\CSDPROJ]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\JSHPROJ]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VJ#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Servicing]
+"CurrentSULevel"=dword:00000000
+"CurrentSPLevel"=dword:00000000
+"Server Path"=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Servicing\Package]
+"FxSDK"=""
+"VB"=""
+"VC"=""
+"VCS"=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Servicing\SKU]
+"Visual Studio .NET Professional 2003 - English"="{20610409-CA18-41A6-9E21-A93AE82EE7C5}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VB]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VBDPROJ]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VC]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VC#]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Visual Studio .NET Professional 2003 - English]
+"InstallSuccess"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VS]
+"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\"
+"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.exe"
+"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.exe"
+"MSMDir"="C:\Program Files\Common Files\Merge Modules\"
+"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\"
+"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\"
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\"
+"VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Setup\VSUpdate\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VS\BuildNumber]
+"1033"="7.1.3088"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VS\Pro]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO\Win32]
+@="{759354D0-6B42-4705-AFFB-56E34D2BC3D4}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]
+"Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\Perl\bin\;c:\bin;c:\cygwin\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\Common Files\Avid;C:\Program Files\backburner 2\;C:\Program Files\cvsnt;C:\Program Files\Subversion\bin;C:\Program Files\Common Files\Adobe\AGL;C:\Program Files\HTMLDoc"
+"Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib"
+"Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include"
+"Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src"
+"Reference Dirs"="$(FrameWorkDir)$(FrameWorkVersion)"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO\Win32\ToolDefaultExtensionLists]
+"VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c"
+"VCLinkerTool"="*.obj;*.res;*.lib;*.rsc"
+"VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc"
+"VCMIDLTool"="*.idl;*.odl"
+"VCCustomBuildTool"="*.bat"
+"VCResourceCompilerTool"="*.rc"
+"VCPreBuildEventTool"="*.bat"
+"VCPreLinkEventTool"="*.bat"
+"VCPostBuildEventTool"="*.bat"
+"VCBscMakeTool"="*.sbr"
+"VCNMakeTool"=""
+"VCWebServiceProxyGeneratorTool"="*.discomap"
+"VCWebDeploymentTool"=""
+"VCALinkTool"="*.resources"
+"VCManagedResourceCompilerTool"="*.resx"
+"VCXMLDataGeneratorTool"="*.xsd"
+"VCManagedWrapperGeneratorTool"=""
+"VCAuxiliaryManagedWrapperGeneratorTool"=""
+"VCPrimaryInteropTool"=""
+''','\n')
+
+regdata_8exp = string.split(r'''
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0]
+"CLR Version"="v2.0.50727"
+"ApplicationID"="VCExpress"
+"SecurityAppID"="{741726F6-1EAE-4680-86A6-6085E8872CF8}"
+"InstallDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\"
+"EnablePreloadCLR"=dword:00000001
+"RestoreAppPath"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\InstalledProducts]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\InstalledProducts\Microsoft Visual C++]
+"UseInterface"=dword:00000001
+"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}"
+"DefaultProductAttribute"="VC"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\Setup]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\Setup\VC]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio 8\VC\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\Setup\VS]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio 8\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC\VC_OBJECTS_PLATFORM_INFO]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32]
+@="{72f11281-2429-11d7-8bf6-00b0d03daa06}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\ToolDefaultExtensionLists]
+"VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c"
+"VCLinkerTool"="*.obj;*.res;*.lib;*.rsc;*.licenses"
+"VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc"
+"VCMIDLTool"="*.idl;*.odl"
+"VCCustomBuildTool"="*.bat"
+"VCResourceCompilerTool"="*.rc"
+"VCPreBuildEventTool"="*.bat"
+"VCPreLinkEventTool"="*.bat"
+"VCPostBuildEventTool"="*.bat"
+"VCBscMakeTool"="*.sbr"
+"VCFxCopTool"="*.dll;*.exe"
+"VCNMakeTool"=""
+"VCWebServiceProxyGeneratorTool"="*.discomap"
+"VCWebDeploymentTool"=""
+"VCALinkTool"="*.resources"
+"VCManagedResourceCompilerTool"="*.resx"
+"VCXMLDataGeneratorTool"="*.xsd"
+"VCManifestTool"="*.manifest"
+"VCXDCMakeTool"="*.xdc"
+''','\n')
+
+regdata_80 = string.split(r'''
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0]
+"CLR Version"="v2.0.50727"
+"ApplicationID"="VisualStudio"
+"ThisVersionDTECLSID"="{BA018599-1DB3-44f9-83B4-461454C84BF8}"
+"ThisVersionSolutionCLSID"="{1B2EEDD6-C203-4d04-BD59-78906E3E8AAB}"
+"SecurityAppID"="{DF99D4F5-9F04-4CEF-9D39-095821B49C77}"
+"InstallDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\"
+"EnablePreloadCLR"=dword:00000001
+"RestoreAppPath"=dword:00000001
+"Source Directories"="C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\;C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\atl\;C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\InstalledProducts]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\InstalledProducts\Microsoft Visual C++]
+"UseInterface"=dword:00000001
+"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}"
+"DefaultProductAttribute"="VC"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup]
+"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\EF]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio 8\EnterpriseFrameworks\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\Microsoft Visual Studio 2005 Professional Edition - ENU]
+"SrcPath"="d:\vs\"
+"InstallSuccess"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VC]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio 8\VC\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VS]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio 8\"
+"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe"
+"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe"
+"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\"
+"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\"
+"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VS\BuildNumber]
+"1033"="8.0.50727.42"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VS\Pro]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio 8\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC\VC_OBJECTS_PLATFORM_INFO]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32]
+@="{72f11281-2429-11d7-8bf6-00b0d03daa06}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\ToolDefaultExtensionLists]
+"VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c"
+"VCLinkerTool"="*.obj;*.res;*.lib;*.rsc;*.licenses"
+"VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc"
+"VCMIDLTool"="*.idl;*.odl"
+"VCCustomBuildTool"="*.bat"
+"VCResourceCompilerTool"="*.rc"
+"VCPreBuildEventTool"="*.bat"
+"VCPreLinkEventTool"="*.bat"
+"VCPostBuildEventTool"="*.bat"
+"VCBscMakeTool"="*.sbr"
+"VCFxCopTool"="*.dll;*.exe"
+"VCNMakeTool"=""
+"VCWebServiceProxyGeneratorTool"="*.discomap"
+"VCWebDeploymentTool"=""
+"VCALinkTool"="*.resources"
+"VCManagedResourceCompilerTool"="*.resx"
+"VCXMLDataGeneratorTool"="*.xsd"
+"VCManifestTool"="*.manifest"
+"VCXDCMakeTool"="*.xdc"
+''','\n')
+
+regdata_cv = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion]
+"ProgramFilesDir"="C:\Program Files"
+"CommonFilesDir"="C:\Program Files\Common Files"
+"MediaPath"="C:\WINDOWS\Media"
+''','\n')
+
+
+regdata_none = []
+
+class DummyEnv:
+ def __init__(self, dict=None):
+ if dict:
+ self.dict = dict
+ else:
+ self.dict = {}
+
+ def Dictionary(self, key = None):
+ if not key:
+ return self.dict
+ return self.dict[key]
+
+ def __setitem__(self,key,value):
+ self.dict[key] = value
+
+ def __getitem__(self,key):
+ return self.dict[key]
+
+ def has_key(self,name):
+ return self.dict.has_key(name)
+
+class RegKey:
+ """key class for storing an 'open' registry key"""
+ def __init__(self,key):
+ self.key = key
+
+# Warning: this is NOT case-insensitive, unlike the Windows registry.
+# So e.g. HKLM\Software is NOT the same key as HKLM\SOFTWARE.
+class RegNode:
+ """node in the dummy registry"""
+ def __init__(self,name):
+ self.valdict = {}
+ self.keydict = {}
+ self.keyarray = []
+ self.valarray = []
+ self.name = name
+
+ def value(self,val):
+ if self.valdict.has_key(val):
+ return (self.valdict[val],1)
+ else:
+ raise SCons.Util.RegError
+
+ def addValue(self,name,val):
+ self.valdict[name] = val
+ self.valarray.append(name)
+
+ def valindex(self,index):
+ rv = None
+ try:
+ rv = (self.valarray[index],self.valdict[self.valarray[index]],1)
+ except KeyError:
+ raise SCons.Util.RegError
+ return rv
+
+ def key(self,key,sep = '\\'):
+ if key.find(sep) != -1:
+ keyname, subkeys = key.split(sep,1)
+ else:
+ keyname = key
+ subkeys = ""
+ try:
+ # recurse, and return the lowest level key node
+ if subkeys:
+ return self.keydict[keyname].key(subkeys)
+ else:
+ return self.keydict[keyname]
+ except KeyError:
+ raise SCons.Util.RegError
+
+ def addKey(self,name,sep = '\\'):
+ if string.find(name, sep) != -1:
+ keyname, subkeys = string.split(name, sep, 1)
+ else:
+ keyname = name
+ subkeys = ""
+
+ if not self.keydict.has_key(keyname):
+ self.keydict[keyname] = RegNode(keyname)
+ self.keyarray.append(keyname)
+
+ # recurse, and return the lowest level key node
+ if subkeys:
+ return self.keydict[keyname].addKey(subkeys)
+ else:
+ return self.keydict[keyname]
+
+ def keyindex(self,index):
+ return self.keydict[self.keyarray[index]]
+
+ def __str__(self):
+ return self._doStr()
+
+ def _doStr(self, indent = ''):
+ rv = ""
+ for value in self.valarray:
+ rv = rv + '%s"%s" = "%s"\n' % (indent, value, self.valdict[value])
+ for key in self.keyarray:
+ rv = rv + "%s%s: {\n"%(indent, key)
+ rv = rv + self.keydict[key]._doStr(indent + ' ')
+ rv = rv + indent + '}\n'
+ return rv
+
+class DummyRegistry:
+ """registry class for storing fake registry attributes"""
+ def __init__(self,data):
+ """parse input data into the fake registry"""
+ self.root = RegNode('REGISTRY')
+ self.root.addKey('HKEY_LOCAL_MACHINE')
+ self.root.addKey('HKEY_CURRENT_USER')
+ self.root.addKey('HKEY_USERS')
+ self.root.addKey('HKEY_CLASSES_ROOT')
+
+ self.parse(data)
+
+ def parse(self, data):
+ parent = self.root
+ keymatch = re.compile('^\[(.*)\]$')
+ valmatch = re.compile('^(?:"(.*)"|[@])="(.*)"$')
+ for line in data:
+ m1 = keymatch.match(line)
+ if m1:
+ # add a key, set it to current parent
+ parent = self.root.addKey(m1.group(1))
+ else:
+ m2 = valmatch.match(line)
+ if m2:
+ parent.addValue(m2.group(1),m2.group(2))
+
+ def OpenKeyEx(self,root,key):
+ if root == SCons.Util.HKEY_CLASSES_ROOT:
+ mykey = 'HKEY_CLASSES_ROOT\\' + key
+ if root == SCons.Util.HKEY_USERS:
+ mykey = 'HKEY_USERS\\' + key
+ if root == SCons.Util.HKEY_CURRENT_USER:
+ mykey = 'HKEY_CURRENT_USER\\' + key
+ if root == SCons.Util.HKEY_LOCAL_MACHINE:
+ mykey = 'HKEY_LOCAL_MACHINE\\' + key
+ debug("Open Key:%s"%mykey)
+ return self.root.key(mykey)
+
+def DummyOpenKeyEx(root, key):
+ return registry.OpenKeyEx(root,key)
+
+def DummyEnumKey(key, index):
+ rv = None
+ try:
+ rv = key.keyarray[index]
+ except IndexError:
+ raise SCons.Util.RegError
+# print "Enum Key",key.name,"[",index,"] =>",rv
+ return rv
+
+def DummyEnumValue(key, index):
+ rv = key.valindex(index)
+# print "Enum Value",key.name,"[",index,"] =>",rv
+ return rv
+
+def DummyQueryValue(key, value):
+ rv = key.value(value)
+# print "Query Value",key.name+"\\"+value,"=>",rv
+ return rv
+
+def DummyExists(path):
+ return 1
+
+class msvsTestCase(unittest.TestCase):
+ """This test case is run several times with different defaults.
+ See its subclasses below."""
+ def setUp(self):
+ debug("THIS TYPE :%s"%self)
+ global registry
+ registry = self.registry
+ from SCons.Tool.MSCommon.vs import reset_installed_visual_studios
+ reset_installed_visual_studios()
+
+ def test_get_default_version(self):
+ """Test retrieval of the default visual studio version"""
+
+ debug("Testing for default version %s"%self.default_version)
+ env = DummyEnv()
+ v1 = get_default_version(env)
+ assert env['MSVS_VERSION'] == self.default_version, \
+ ("env['MSVS_VERSION'] != self.default_version",self.default_version, env['MSVS_VERSION'])
+ assert env['MSVS']['VERSION'] == self.default_version, \
+ ("env['MSVS']['VERSION'] != self.default_version",self.default_version, env['MSVS']['VERSION'])
+ assert v1 == self.default_version, (self.default_version, v1)
+
+ env = DummyEnv({'MSVS_VERSION':'7.0'})
+ v2 = get_default_version(env)
+ assert env['MSVS_VERSION'] == '7.0', env['MSVS_VERSION']
+ assert env['MSVS']['VERSION'] == '7.0', env['MSVS']['VERSION']
+ assert v2 == '7.0', v2
+
+ env = DummyEnv()
+ v3 = get_default_version(env)
+ if v3 == '7.1':
+ override = '7.0'
+ else:
+ override = '7.1'
+ env['MSVS_VERSION'] = override
+ v3 = get_default_version(env)
+ assert env['MSVS_VERSION'] == override, env['MSVS_VERSION']
+ assert env['MSVS']['VERSION'] == override, env['MSVS']['VERSION']
+ assert v3 == override, v3
+
+ def _TODO_test_merge_default_version(self):
+ """Test the merge_default_version() function"""
+ pass
+
+ def test_query_versions(self):
+ """Test retrieval of the list of visual studio versions"""
+ v1 = query_versions()
+ assert not v1 or str(v1[0]) == self.highest_version, \
+ (v1, self.highest_version)
+ assert len(v1) == self.number_of_versions, v1
+
+class msvs6aTestCase(msvsTestCase):
+ """Test MSVS 6 Registry"""
+ registry = DummyRegistry(regdata_6a + regdata_cv)
+ default_version = '6.0'
+ highest_version = '6.0'
+ number_of_versions = 1
+ install_locs = {
+ '6.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio\\VC98', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio\\VC98\\Bin'},
+ '7.0' : {},
+ '7.1' : {},
+ '8.0' : {},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['6.0']
+
+class msvs6bTestCase(msvsTestCase):
+ """Test Other MSVS 6 Registry"""
+ registry = DummyRegistry(regdata_6b + regdata_cv)
+ default_version = '6.0'
+ highest_version = '6.0'
+ number_of_versions = 1
+ install_locs = {
+ '6.0' : {'VSINSTALLDIR': 'C:\\VS6\\VC98', 'VCINSTALLDIR': 'C:\\VS6\\VC98\\Bin'},
+ '7.0' : {},
+ '7.1' : {},
+ '8.0' : {},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['6.0']
+
+class msvs6and7TestCase(msvsTestCase):
+ """Test MSVS 6 & 7 Registry"""
+ registry = DummyRegistry(regdata_6b + regdata_7 + regdata_cv)
+ default_version = '7.0'
+ highest_version = '7.0'
+ number_of_versions = 2
+ install_locs = {
+ '6.0' : {'VSINSTALLDIR': 'C:\\VS6\\VC98',
+ 'VCINSTALLDIR': 'C:\\VS6\\VC98\\Bin'},
+ '7.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7',
+ 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7\\Tools'},
+ '7.1' : {},
+ '8.0' : {},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['7.0']
+
+class msvs7TestCase(msvsTestCase):
+ """Test MSVS 7 Registry"""
+ registry = DummyRegistry(regdata_7 + regdata_cv)
+ default_version = '7.0'
+ highest_version = '7.0'
+ number_of_versions = 1
+ install_locs = {
+ '6.0' : {},
+ '7.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7',
+ 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7\\Tools'},
+ '7.1' : {},
+ '8.0' : {},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['7.0']
+
+class msvs71TestCase(msvsTestCase):
+ """Test MSVS 7.1 Registry"""
+ registry = DummyRegistry(regdata_7_1 + regdata_cv)
+ default_version = '7.1'
+ highest_version = '7.1'
+ number_of_versions = 1
+ install_locs = {
+ '6.0' : {},
+ '7.0' : {},
+ '7.1' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET 2003\\Common7',
+ 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET 2003\\Common7\\Tools'},
+ '8.0' : {},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['7.1']
+
+class msvs8ExpTestCase(msvsTestCase): # XXX: only one still not working
+ """Test MSVS 8 Express Registry"""
+ registry = DummyRegistry(regdata_8exp + regdata_cv)
+ default_version = '8.0Exp'
+ highest_version = '8.0Exp'
+ number_of_versions = 1
+ install_locs = {
+ '6.0' : {},
+ '7.0' : {},
+ '7.1' : {},
+ '8.0' : {},
+ '8.0Exp' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8',
+ 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8\\VC'},
+ }
+ default_install_loc = install_locs['8.0Exp']
+
+class msvs80TestCase(msvsTestCase):
+ """Test MSVS 8 Registry"""
+ registry = DummyRegistry(regdata_80 + regdata_cv)
+ default_version = '8.0'
+ highest_version = '8.0'
+ number_of_versions = 1
+ install_locs = {
+ '6.0' : {},
+ '7.0' : {},
+ '7.1' : {},
+ '8.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8',
+ 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8\\VC'},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['8.0']
+
+class msvsEmptyTestCase(msvsTestCase):
+ """Test Empty Registry"""
+ registry = DummyRegistry(regdata_none)
+ default_version = '9.0'
+ highest_version = None
+ number_of_versions = 0
+ install_locs = {
+ '6.0' : {},
+ '7.0' : {},
+ '7.1' : {},
+ '8.0' : {},
+ '8.0Exp' : {},
+ }
+ default_install_loc = install_locs['8.0Exp']
+
+if __name__ == "__main__":
+
+ # only makes sense to test this on win32
+ if sys.platform != 'win32':
+ sys.stdout.write("NO RESULT for msvsTests.py: '%s' is not win32\n" % sys.platform)
+ sys.exit(0)
+
+ SCons.Util.RegOpenKeyEx = DummyOpenKeyEx
+ SCons.Util.RegEnumKey = DummyEnumKey
+ SCons.Util.RegEnumValue = DummyEnumValue
+ SCons.Util.RegQueryValueEx = DummyQueryValue
+
+ os.path.exists = DummyExists # make sure all files exist :-)
+ os.path.isfile = DummyExists # make sure all files are files :-)
+ os.path.isdir = DummyExists # make sure all dirs are dirs :-)
+
+ exit_val = 0
+
+ test_classes = [
+ msvs6aTestCase,
+ msvs6bTestCase,
+ msvs6and7TestCase,
+ msvs7TestCase,
+ msvs71TestCase,
+ msvs8ExpTestCase,
+ msvs80TestCase,
+ msvsEmptyTestCase,
+ ]
+
+ for test_class in test_classes:
+ print "TEST: ", test_class.__doc__
+ back_osenv = copy.deepcopy(os.environ)
+ try:
+ # XXX: overriding the os.environ is bad, but doing it
+ # correctly is too complicated for now. Those tests should
+ # be fixed
+ for k in ['VS71COMNTOOLS',
+ 'VS80COMNTOOLS',
+ 'VS90COMNTOOLS']:
+ if os.environ.has_key(k):
+ del os.environ[k]
+
+ suite = unittest.makeSuite(test_class, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ exit_val = 1
+ finally:
+ os.env = back_osenv
+
+ sys.exit(exit_val)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mwcc.py b/src/engine/SCons/Tool/mwcc.py
new file mode 100644
index 0000000..885f009
--- /dev/null
+++ b/src/engine/SCons/Tool/mwcc.py
@@ -0,0 +1,208 @@
+"""SCons.Tool.mwcc
+
+Tool-specific initialization for the Metrowerks CodeWarrior compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/mwcc.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+
+import SCons.Util
+
+def set_vars(env):
+ """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars
+
+ MWCW_VERSIONS is set to a list of objects representing installed versions
+
+ MWCW_VERSION is set to the version object that will be used for building.
+ MWCW_VERSION can be set to a string during Environment
+ construction to influence which version is chosen, otherwise
+ the latest one from MWCW_VERSIONS is used.
+
+ Returns true if at least one version is found, false otherwise
+ """
+ desired = env.get('MWCW_VERSION', '')
+
+ # return right away if the variables are already set
+ if isinstance(desired, MWVersion):
+ return 1
+ elif desired is None:
+ return 0
+
+ versions = find_versions()
+ version = None
+
+ if desired:
+ for v in versions:
+ if str(v) == desired:
+ version = v
+ elif versions:
+ version = versions[-1]
+
+ env['MWCW_VERSIONS'] = versions
+ env['MWCW_VERSION'] = version
+
+ if version is None:
+ return 0
+
+ env.PrependENVPath('PATH', version.clpath)
+ env.PrependENVPath('PATH', version.dllpath)
+ ENV = env['ENV']
+ ENV['CWFolder'] = version.path
+ ENV['LM_LICENSE_FILE'] = version.license
+ plus = lambda x: '+%s' % x
+ ENV['MWCIncludes'] = string.join(map(plus, version.includes), os.pathsep)
+ ENV['MWLibraries'] = string.join(map(plus, version.libs), os.pathsep)
+ return 1
+
+
+def find_versions():
+ """Return a list of MWVersion objects representing installed versions"""
+ versions = []
+
+ ### This function finds CodeWarrior by reading from the registry on
+ ### Windows. Some other method needs to be implemented for other
+ ### platforms, maybe something that calls env.WhereIs('mwcc')
+
+ if SCons.Util.can_read_reg:
+ try:
+ HLM = SCons.Util.HKEY_LOCAL_MACHINE
+ product = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions'
+ product_key = SCons.Util.RegOpenKeyEx(HLM, product)
+
+ i = 0
+ while 1:
+ name = product + '\\' + SCons.Util.RegEnumKey(product_key, i)
+ name_key = SCons.Util.RegOpenKeyEx(HLM, name)
+
+ try:
+ version = SCons.Util.RegQueryValueEx(name_key, 'VERSION')
+ path = SCons.Util.RegQueryValueEx(name_key, 'PATH')
+ mwv = MWVersion(version[0], path[0], 'Win32-X86')
+ versions.append(mwv)
+ except SCons.Util.RegError:
+ pass
+
+ i = i + 1
+
+ except SCons.Util.RegError:
+ pass
+
+ return versions
+
+
+class MWVersion:
+ def __init__(self, version, path, platform):
+ self.version = version
+ self.path = path
+ self.platform = platform
+ self.clpath = os.path.join(path, 'Other Metrowerks Tools',
+ 'Command Line Tools')
+ self.dllpath = os.path.join(path, 'Bin')
+
+ # The Metrowerks tools don't store any configuration data so they
+ # are totally dumb when it comes to locating standard headers,
+ # libraries, and other files, expecting all the information
+ # to be handed to them in environment variables. The members set
+ # below control what information scons injects into the environment
+
+ ### The paths below give a normal build environment in CodeWarrior for
+ ### Windows, other versions of CodeWarrior might need different paths.
+
+ msl = os.path.join(path, 'MSL')
+ support = os.path.join(path, '%s Support' % platform)
+
+ self.license = os.path.join(path, 'license.dat')
+ self.includes = [msl, support]
+ self.libs = [msl, support]
+
+ def __str__(self):
+ return self.version
+
+
+CSuffixes = ['.c', '.C']
+CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
+
+
+def generate(env):
+ """Add Builders and construction variables for the mwcc to an Environment."""
+ import SCons.Defaults
+ import SCons.Tool
+
+ set_vars(env)
+
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in CSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.CAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
+
+ for suffix in CXXSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.CXXAction)
+ shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
+
+ env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES'
+
+ env['CC'] = 'mwcc'
+ env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS'
+
+ env['CXX'] = 'mwcc'
+ env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
+
+ env['SHCC'] = '$CC'
+ env['SHCCFLAGS'] = '$CCFLAGS'
+ env['SHCFLAGS'] = '$CFLAGS'
+ env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS'
+
+ env['SHCXX'] = '$CXX'
+ env['SHCXXFLAGS'] = '$CXXFLAGS'
+ env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
+
+ env['CFILESUFFIX'] = '.c'
+ env['CXXFILESUFFIX'] = '.cpp'
+ env['CPPDEFPREFIX'] = '-D'
+ env['CPPDEFSUFFIX'] = ''
+ env['INCPREFIX'] = '-I'
+ env['INCSUFFIX'] = ''
+
+ #env['PCH'] = ?
+ #env['PCHSTOP'] = ?
+
+
+def exists(env):
+ return set_vars(env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mwcc.xml b/src/engine/SCons/Tool/mwcc.xml
new file mode 100644
index 0000000..e7df1f3
--- /dev/null
+++ b/src/engine/SCons/Tool/mwcc.xml
@@ -0,0 +1,53 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="mwcc">
+<summary>
+Sets construction variables for the Metrowerks CodeWarrior compiler.
+</summary>
+<sets>
+MWCW_VERSIONS
+MWCW_VERSION
+<!--CCCOMFLAGS-->
+CC
+CCCOM
+CXX
+CXXCOM
+SHCC
+SHCCFLAGS
+SHCFLAGS
+SHCCCOM
+SHCXX
+SHCXXFLAGS
+SHCXXCOM
+CFILESUFFIX
+CXXFILESUFFIX
+CPPDEFPREFIX
+CPPDEFSUFFIX
+INCPREFIX
+INCSUFFIX
+</sets>
+<uses>
+CCCOMSTR
+CXXCOMSTR
+SHCCCOMSTR
+SHCXXCOMSTR
+</uses>
+</tool>
+
+<cvar name="MWCW_VERSION">
+<summary>
+The version number of the MetroWerks CodeWarrior C compiler
+to be used.
+</summary>
+</cvar>
+
+<cvar name="MWCW_VERSIONS">
+<summary>
+A list of installed versions of the MetroWerks CodeWarrior C compiler
+on this system.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py
new file mode 100644
index 0000000..e0e1484
--- /dev/null
+++ b/src/engine/SCons/Tool/mwld.py
@@ -0,0 +1,107 @@
+"""SCons.Tool.mwld
+
+Tool-specific initialization for the Metrowerks CodeWarrior linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/mwld.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Tool
+
+
+def generate(env):
+ """Add Builders and construction variables for lib to an Environment."""
+ SCons.Tool.createStaticLibBuilder(env)
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
+
+ env['AR'] = 'mwld'
+ env['ARCOM'] = '$AR $ARFLAGS -library -o $TARGET $SOURCES'
+
+ env['LIBDIRPREFIX'] = '-L'
+ env['LIBDIRSUFFIX'] = ''
+ env['LIBLINKPREFIX'] = '-l'
+ env['LIBLINKSUFFIX'] = '.lib'
+
+ env['LINK'] = 'mwld'
+ env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+
+ env['SHLINK'] = '$LINK'
+ env['SHLINKFLAGS'] = '$LINKFLAGS'
+ env['SHLINKCOM'] = shlib_action
+ env['SHLIBEMITTER']= shlib_emitter
+
+
+def exists(env):
+ import SCons.Tool.mwcc
+ return SCons.Tool.mwcc.set_vars(env)
+
+
+def shlib_generator(target, source, env, for_signature):
+ cmd = ['$SHLINK', '$SHLINKFLAGS', '-shared']
+
+ no_import_lib = env.get('no_import_lib', 0)
+ if no_import_lib: cmd.extend('-noimplib')
+
+ dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ if dll: cmd.extend(['-o', dll])
+
+ implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
+ if implib: cmd.extend(['-implib', implib.get_string(for_signature)])
+
+ cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS'])
+
+ return [cmd]
+
+
+def shlib_emitter(target, source, env):
+ dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ no_import_lib = env.get('no_import_lib', 0)
+
+ if not dll:
+ raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")
+
+ if not no_import_lib and \
+ not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'):
+
+ # Append an import library to the list of targets.
+ target.append(env.ReplaceIxes(dll,
+ 'SHLIBPREFIX', 'SHLIBSUFFIX',
+ 'LIBPREFIX', 'LIBSUFFIX'))
+
+ return target, source
+
+
+shlib_action = SCons.Action.Action(shlib_generator, generator=1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/mwld.xml b/src/engine/SCons/Tool/mwld.xml
new file mode 100644
index 0000000..368d658
--- /dev/null
+++ b/src/engine/SCons/Tool/mwld.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="mwld">
+<summary>
+Sets construction variables for the Metrowerks CodeWarrior linker.
+</summary>
+<sets>
+AR
+ARCOM
+LIBDIRPREFIX
+LIBDIRSUFFIX
+LIBLINKPREFIX
+LIBLINKSUFFIX
+LINK
+LINKCOM
+SHLINK
+SHLINKFLAGS
+SHLINKCOM
+<!--SHLIBEMITTER-->
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/nasm.py b/src/engine/SCons/Tool/nasm.py
new file mode 100644
index 0000000..16ef193
--- /dev/null
+++ b/src/engine/SCons/Tool/nasm.py
@@ -0,0 +1,72 @@
+"""SCons.Tool.nasm
+
+Tool-specific initialization for nasm, the famous Netwide Assembler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/nasm.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+ASSuffixes = ['.s', '.asm', '.ASM']
+ASPPSuffixes = ['.spp', '.SPP', '.sx']
+if SCons.Util.case_sensitive_suffixes('.s', '.S'):
+ ASPPSuffixes.extend(['.S'])
+else:
+ ASSuffixes.extend(['.S'])
+
+def generate(env):
+ """Add Builders and construction variables for nasm to an Environment."""
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+ for suffix in ASSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.ASAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+
+ for suffix in ASPPSuffixes:
+ static_obj.add_action(suffix, SCons.Defaults.ASPPAction)
+ static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
+
+ env['AS'] = 'nasm'
+ env['ASFLAGS'] = SCons.Util.CLVar('')
+ env['ASPPFLAGS'] = '$ASFLAGS'
+ env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES'
+ env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES'
+
+def exists(env):
+ return env.Detect('nasm')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/nasm.xml b/src/engine/SCons/Tool/nasm.xml
new file mode 100644
index 0000000..725c186
--- /dev/null
+++ b/src/engine/SCons/Tool/nasm.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="nasm">
+<summary>
+Sets construction variables for the
+<application>nasm</application> Netwide Assembler.
+</summary>
+<sets>
+AS
+ASFLAGS
+ASPPFLAGS
+ASCOM
+ASPPCOM
+</sets>
+<uses>
+ASCOMSTR
+ASPPCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/packaging.xml b/src/engine/SCons/Tool/packaging.xml
new file mode 100644
index 0000000..0e36128
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging.xml
@@ -0,0 +1,73 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="packaging">
+<summary>
+A framework for building binary and source packages.
+</summary>
+</tool>
+
+<builder name="Package">
+<summary>
+Builds a Binary Package of the given source files.
+
+<example>
+env.Package(source = FindInstalledFiles())
+</example>
+</summary>
+</builder>
+
+<cvar name="JAR">
+<summary>
+The Java archive tool.
+</summary>
+</cvar>
+
+<cvar name="JARCHDIR">
+<summary>
+The directory to which the Java archive tool should change
+(using the
+<option>-C</option>
+option).
+</summary>
+</cvar>
+
+<cvar name="JARCOM">
+<summary>
+The command line used to call the Java archive tool.
+</summary>
+</cvar>
+
+<cvar name="JARCOMSTR">
+<summary>
+The string displayed when the Java archive tool
+is called
+If this is not set, then &cv-JARCOM; (the command line) is displayed.
+
+<example>
+env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="JARFLAGS">
+<summary>
+General options passed to the Java archive tool.
+By default this is set to
+<option>cf</option>
+to create the necessary
+<command>jar</command>
+file.
+</summary>
+</cvar>
+
+<cvar name="JARSUFFIX">
+<summary>
+The suffix for Java archives:
+<filename>.jar</filename>
+by default.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py
new file mode 100644
index 0000000..90947e4
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/__init__.py
@@ -0,0 +1,314 @@
+"""SCons.Tool.Packaging
+
+SCons Packaging Tool.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Environment
+from SCons.Variables import *
+from SCons.Errors import *
+from SCons.Util import is_List, make_path_relative
+from SCons.Warnings import warn, Warning
+
+import os, imp
+import SCons.Defaults
+
+__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ]
+
+#
+# Utility and Builder function
+#
+def Tag(env, target, source, *more_tags, **kw_tags):
+ """ Tag a file with the given arguments, just sets the accordingly named
+ attribute on the file object.
+
+ TODO: FIXME
+ """
+ if not target:
+ target=source
+ first_tag=None
+ else:
+ first_tag=source
+
+ if first_tag:
+ kw_tags[first_tag[0]] = ''
+
+ if len(kw_tags) == 0 and len(more_tags) == 0:
+ raise UserError, "No tags given."
+
+ # XXX: sanity checks
+ for x in more_tags:
+ kw_tags[x] = ''
+
+ if not SCons.Util.is_List(target):
+ target=[target]
+ else:
+ # hmm, sometimes the target list, is a list of a list
+ # make sure it is flattened prior to processing.
+ # TODO: perhaps some bug ?!?
+ target=env.Flatten(target)
+
+ for t in target:
+ for (k,v) in kw_tags.items():
+ # all file tags have to start with PACKAGING_, so we can later
+ # differentiate between "normal" object attributes and the
+ # packaging attributes. As the user should not be bothered with
+ # that, the prefix will be added here if missing.
+ #if not k.startswith('PACKAGING_'):
+ if k[:10] != 'PACKAGING_':
+ k='PACKAGING_'+k
+ setattr(t, k, v)
+
+def Package(env, target=None, source=None, **kw):
+ """ Entry point for the package tool.
+ """
+ # check if we need to find the source files ourself
+ if not source:
+ source = env.FindInstalledFiles()
+
+ if len(source)==0:
+ raise UserError, "No source for Package() given"
+
+ # decide which types of packages shall be built. Can be defined through
+ # four mechanisms: command line argument, keyword argument,
+ # environment argument and default selection( zip or tar.gz ) in that
+ # order.
+ try: kw['PACKAGETYPE']=env['PACKAGETYPE']
+ except KeyError: pass
+
+ if not kw.get('PACKAGETYPE'):
+ from SCons.Script import GetOption
+ kw['PACKAGETYPE'] = GetOption('package_type')
+
+ if kw['PACKAGETYPE'] == None:
+ if env['BUILDERS'].has_key('Tar'):
+ kw['PACKAGETYPE']='targz'
+ elif env['BUILDERS'].has_key('Zip'):
+ kw['PACKAGETYPE']='zip'
+ else:
+ raise UserError, "No type for Package() given"
+
+ PACKAGETYPE=kw['PACKAGETYPE']
+ if not is_List(PACKAGETYPE):
+ PACKAGETYPE=string.split(PACKAGETYPE, ',')
+
+ # load the needed packagers.
+ def load_packager(type):
+ try:
+ file,path,desc=imp.find_module(type, __path__)
+ return imp.load_module(type, file, path, desc)
+ except ImportError, e:
+ raise EnvironmentError("packager %s not available: %s"%(type,str(e)))
+
+ packagers=map(load_packager, PACKAGETYPE)
+
+ # set up targets and the PACKAGEROOT
+ try:
+ # fill up the target list with a default target name until the PACKAGETYPE
+ # list is of the same size as the target list.
+ if not target: target = []
+
+ size_diff = len(PACKAGETYPE)-len(target)
+ default_name = "%(NAME)s-%(VERSION)s"
+
+ if size_diff>0:
+ default_target = default_name%kw
+ target.extend( [default_target]*size_diff )
+
+ if not kw.has_key('PACKAGEROOT'):
+ kw['PACKAGEROOT'] = default_name%kw
+
+ except KeyError, e:
+ raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] )
+
+ # setup the source files
+ source=env.arg2nodes(source, env.fs.Entry)
+
+ # call the packager to setup the dependencies.
+ targets=[]
+ try:
+ for packager in packagers:
+ t=[target.pop(0)]
+ t=apply(packager.package, [env,t,source], kw)
+ targets.extend(t)
+
+ assert( len(target) == 0 )
+
+ except KeyError, e:
+ raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\
+ % (e.args[0],packager.__name__) )
+ except TypeError, e:
+ # this exception means that a needed argument for the packager is
+ # missing. As our packagers get their "tags" as named function
+ # arguments we need to find out which one is missing.
+ from inspect import getargspec
+ args,varargs,varkw,defaults=getargspec(packager.package)
+ if defaults!=None:
+ args=args[:-len(defaults)] # throw away arguments with default values
+ args.remove('env')
+ args.remove('target')
+ args.remove('source')
+ # now remove any args for which we have a value in kw.
+ #args=[x for x in args if not kw.has_key(x)]
+ args=filter(lambda x, kw=kw: not kw.has_key(x), args)
+
+ if len(args)==0:
+ raise # must be a different error, so reraise
+ elif len(args)==1:
+ raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\
+ % (args[0],packager.__name__) )
+ else:
+ raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packager"\
+ % (", ".join(args),packager.__name__) )
+
+ target=env.arg2nodes(target, env.fs.Entry)
+ targets.extend(env.Alias( 'package', targets ))
+ return targets
+
+#
+# SCons tool initialization functions
+#
+
+added = None
+
+def generate(env):
+ from SCons.Script import AddOption
+ global added
+ if not added:
+ added = 1
+ AddOption('--package-type',
+ dest='package_type',
+ default=None,
+ type="string",
+ action="store",
+ help='The type of package to create.')
+
+ try:
+ env['BUILDERS']['Package']
+ env['BUILDERS']['Tag']
+ except KeyError:
+ env['BUILDERS']['Package'] = Package
+ env['BUILDERS']['Tag'] = Tag
+
+def exists(env):
+ return 1
+
+# XXX
+def options(opts):
+ opts.AddVariables(
+ EnumVariable( 'PACKAGETYPE',
+ 'the type of package to create.',
+ None, allowed_values=map( str, __all__ ),
+ ignorecase=2
+ )
+ )
+
+#
+# Internal utility functions
+#
+
+def copy_attr(f1, f2):
+ """ copies the special packaging file attributes from f1 to f2.
+ """
+ #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\
+ # x.startswith('PACKAGING_')]
+ copyit = lambda x, f2=f2: not hasattr(f2, x) and x[:10] == 'PACKAGING_'
+ pattrs = filter(copyit, dir(f1))
+ for attr in pattrs:
+ setattr(f2, attr, getattr(f1, attr))
+def putintopackageroot(target, source, env, pkgroot, honor_install_location=1):
+ """ Uses the CopyAs builder to copy all source files to the directory given
+ in pkgroot.
+
+ If honor_install_location is set and the copied source file has an
+ PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is
+ used as the new name of the source file under pkgroot.
+
+ The source file will not be copied if it is already under the the pkgroot
+ directory.
+
+ All attributes of the source file will be copied to the new file.
+ """
+ # make sure the packageroot is a Dir object.
+ if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot)
+ if not SCons.Util.is_List(source): source=[source]
+
+ new_source = []
+ for file in source:
+ if SCons.Util.is_String(file): file = env.File(file)
+
+ if file.is_under(pkgroot):
+ new_source.append(file)
+ else:
+ if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\
+ honor_install_location:
+ new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION)
+ else:
+ new_name=make_path_relative(file.get_path())
+
+ new_file=pkgroot.File(new_name)
+ new_file=env.CopyAs(new_file, file)[0]
+ copy_attr(file, new_file)
+ new_source.append(new_file)
+
+ return (target, new_source)
+
+def stripinstallbuilder(target, source, env):
+ """ strips the install builder action from the source list and stores
+ the final installation location as the "PACKAGING_INSTALL_LOCATION" of
+ the source of the source file. This effectively removes the final installed
+ files from the source list while remembering the installation location.
+
+ It also warns about files which have no install builder attached.
+ """
+ def has_no_install_location(file):
+ return not (file.has_builder() and\
+ hasattr(file.builder, 'name') and\
+ (file.builder.name=="InstallBuilder" or\
+ file.builder.name=="InstallAsBuilder"))
+
+ if len(filter(has_no_install_location, source)):
+ warn(Warning, "there are files to package which have no\
+ InstallBuilder attached, this might lead to irreproducible packages")
+
+ n_source=[]
+ for s in source:
+ if has_no_install_location(s):
+ n_source.append(s)
+ else:
+ for ss in s.sources:
+ n_source.append(ss)
+ copy_attr(s, ss)
+ setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path())
+
+ return (target, n_source)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml
new file mode 100644
index 0000000..521c9f6
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/__init__.xml
@@ -0,0 +1,659 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="Packaging">
+<summary>
+Sets construction variables for the &b-Package; Builder.
+</summary>
+<sets>
+</sets>
+<uses>
+</uses>
+</tool>
+
+<builder name="Package">
+<summary>
+Builds software distribution packages.
+Packages consist of files to install and packaging information.
+The former may be specified with the &source; parameter and may be left out,
+in which case the &FindInstalledFiles; function will collect
+all files that have an &b-Install; or &b-InstallAs; Builder attached.
+If the &target; is not specified
+it will be deduced from additional information given to this Builder.
+
+The packaging information is specified
+with the help of construction variables documented below.
+This information is called a tag to stress that
+some of them can also be attached to files with the &Tag; function.
+The mandatory ones will complain if they were not specified.
+They vary depending on chosen target packager.
+
+The target packager may be selected with the "PACKAGETYPE" command line
+option or with the &cv-PACKAGETYPE; construction variable. Currently
+the following packagers available:
+
+ * msi - Microsoft Installer
+ * rpm - Redhat Package Manger
+ * ipkg - Itsy Package Management System
+ * tarbz2 - compressed tar
+ * targz - compressed tar
+ * zip - zip file
+ * src_tarbz2 - compressed tar source
+ * src_targz - compressed tar source
+ * src_zip - zip file source
+
+An updated list is always available under the "package_type" option when
+running "scons --help" on a project that has packaging activated.
+<example>
+env = Environment(tools=['default', 'packaging'])
+env.Install('/bin/', 'my_program')
+env.Package( NAME = 'foo',
+ VERSION = '1.2.3',
+ PACKAGEVERSION = 0,
+ PACKAGETYPE = 'rpm',
+ LICENSE = 'gpl',
+ SUMMARY = 'balalalalal',
+ DESCRIPTION = 'this should be really really long',
+ X_RPM_GROUP = 'Application/fu',
+ SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz'
+ )
+</example>
+</summary>
+</builder>
+
+<cvar name="ARCHITECTURE">
+<summary>
+Specifies the system architecture for which
+the package is being built.
+The default is the system architecture
+of the machine on which SCons is running.
+This is used to fill in the
+<literal>Architecture:</literal>
+field in an Ipkg
+<filename>control</filename> file,
+and as part of the name of a generated RPM file.
+</summary>
+</cvar>
+
+<cvar name="CHANGE_SPECFILE">
+<summary>
+A hook for modifying the file that controls the packaging build
+(the <filename>.spec</filename> for RPM,
+the <filename>control</filename> for Ipkg,
+the <filename>.wxs</filename> for MSI).
+If set, the function will be called
+after the SCons template for the file has been written.
+XXX
+</summary>
+</cvar>
+
+<cvar name="CHANGELOG">
+<summary>
+The name of a file containing the change log text
+to be included in the package.
+This is included as the
+<literal>%changelog</literal>
+section of the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="DESCRIPTION">
+<summary>
+A long description of the project being packaged.
+This is included in the relevant section
+of the file that controls the packaging build.
+</summary>
+</cvar>
+
+<cvar name="DESCRIPTION_lang">
+<summary>
+A language-specific long description for
+the specified <varname>lang</varname>.
+This is used to populate a
+<literal>%description -l</literal>
+section of an RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="LICENSE">
+<summary>
+The abbreviated name of the license under which
+this project is released (gpl, lpgl, bsd etc.).
+See http://www.opensource.org/licenses/alphabetical
+for a list of license names.
+</summary>
+</cvar>
+
+<cvar name="NAME">
+<summary>
+Specfies the name of the project to package.
+</summary>
+</cvar>
+
+<cvar name="PACKAGEROOT">
+<summary>
+Specifies the directory where all files in resulting archive will be
+placed if applicable. The default value is "$NAME-$VERSION".
+</summary>
+</cvar>
+
+<cvar name="PACKAGETYPE">
+<summary>
+Selects the package type to build. Currently these are available:
+
+ * msi - Microsoft Installer
+ * rpm - Redhat Package Manger
+ * ipkg - Itsy Package Management System
+ * tarbz2 - compressed tar
+ * targz - compressed tar
+ * zip - zip file
+ * src_tarbz2 - compressed tar source
+ * src_targz - compressed tar source
+ * src_zip - zip file source
+
+This may be overridden with the "package_type" command line option.
+</summary>
+</cvar>
+
+<cvar name="PACKAGEVERSION">
+<summary>
+The version of the package (not the underlying project).
+This is currently only used by the rpm packager
+and should reflect changes in the packaging,
+not the underlying project code itself.
+</summary>
+</cvar>
+
+<cvar name="SOURCE_URL">
+<summary>
+The URL
+(web address)
+of the location from which the project was retrieved.
+This is used to fill in the
+<literal>Source:</literal>
+field in the controlling information for Ipkg and RPM packages.
+</summary>
+</cvar>
+
+<cvar name="SUMMARY">
+<summary>
+A short summary of what the project is about.
+This is used to fill in the
+<literal>Summary:</literal>
+field in the controlling information for Ipkg and RPM packages,
+and as the
+<literal>Description:</literal>
+field in MSI packages.
+</summary>
+</cvar>
+
+<cvar name="VENDOR">
+<summary>
+The person or organization who supply the packaged software.
+This is used to fill in the
+<literal>Vendor:</literal>
+field in the controlling information for RPM packages,
+and the
+<literal>Manufacturer:</literal>
+field in the controlling information for MSI packages.
+</summary>
+</cvar>
+
+<cvar name="VERSION">
+<summary>
+The version of the project, specified as a string.
+</summary>
+</cvar>
+
+
+<cvar name="X_IPK_DEPENDS">
+<summary>
+This is used to fill in the
+<literal>Depends:</literal>
+field in the controlling information for Ipkg packages.
+</summary>
+</cvar>
+
+<cvar name="X_IPK_DESCRIPTION">
+<summary>
+This is used to fill in the
+<literal>Description:</literal>
+field in the controlling information for Ipkg packages.
+The default value is
+<literal>$SUMMARY\n$DESCRIPTION</literal>
+</summary>
+</cvar>
+
+<cvar name="X_IPK_MAINTAINER">
+<summary>
+This is used to fill in the
+<literal>Maintainer:</literal>
+field in the controlling information for Ipkg packages.
+</summary>
+</cvar>
+
+<cvar name="X_IPK_PRIORITY">
+<summary>
+This is used to fill in the
+<literal>Priority:</literal>
+field in the controlling information for Ipkg packages.
+</summary>
+</cvar>
+
+<cvar name="X_IPK_SECTION">
+<summary>
+This is used to fill in the
+<literal>Section:</literal>
+field in the controlling information for Ipkg packages.
+</summary>
+</cvar>
+
+
+
+<cvar name="X_MSI_LANGUAGE">
+<summary>
+This is used to fill in the
+<literal>Language:</literal>
+attribute in the controlling information for MSI packages.
+</summary>
+</cvar>
+
+<cvar name="X_MSI_LICENSE_TEXT">
+<summary>
+The text of the software license in RTF format.
+Carriage return characters will be
+replaced with the RTF equivalent \\par.
+</summary>
+</cvar>
+
+<cvar name="X_MSI_UPGRADE_CODE">
+<summary>
+TODO
+</summary>
+</cvar>
+
+
+<cvar name="X_RPM_AUTOREQPROV">
+<summary>
+This is used to fill in the
+<literal>AutoReqProv:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_BUILD">
+<summary>
+internal, but overridable
+</summary>
+</cvar>
+
+<cvar name="X_RPM_BUILDREQUIRES">
+<summary>
+This is used to fill in the
+<literal>BuildRequires:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_BUILDROOT">
+<summary>
+internal, but overridable
+</summary>
+</cvar>
+
+<cvar name="X_RPM_CLEAN">
+<summary>
+internal, but overridable
+</summary>
+</cvar>
+
+<cvar name="X_RPM_CONFLICTS">
+<summary>
+This is used to fill in the
+<literal>Conflicts:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_DEFATTR">
+<summary>
+This value is used as the default attributes
+for the files in the RPM package.
+The default value is
+<literal>(-,root,root)</literal>.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_DISTRIBUTION">
+<summary>
+This is used to fill in the
+<literal>Distribution:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_EPOCH">
+<summary>
+This is used to fill in the
+<literal>Epoch:</literal>
+field in the controlling information for RPM packages.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_EXCLUDEARCH">
+<summary>
+This is used to fill in the
+<literal>ExcludeArch:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_EXLUSIVEARCH">
+<summary>
+This is used to fill in the
+<literal>ExclusiveArch:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_GROUP">
+<summary>
+This is used to fill in the
+<literal>Group:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_GROUP_lang">
+<summary>
+This is used to fill in the
+<literal>Group(lang):</literal>
+field in the RPM
+<filename>.spec</filename> file.
+Note that
+<varname>lang</varname>
+is not literal
+and should be replaced by
+the appropriate language code.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_ICON">
+<summary>
+This is used to fill in the
+<literal>Icon:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_INSTALL">
+<summary>
+internal, but overridable
+</summary>
+</cvar>
+
+<cvar name="X_RPM_PACKAGER">
+<summary>
+This is used to fill in the
+<literal>Packager:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_PROVIDES">
+<summary>
+This is used to fill in the
+<literal>Provides:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_POSTINSTALL">
+<summary>
+This is used to fill in the
+<literal>%post:</literal>
+section in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_PREINSTALL">
+<summary>
+This is used to fill in the
+<literal>%pre:</literal>
+section in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_PREFIX">
+<summary>
+This is used to fill in the
+<literal>Prefix:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_PREP">
+<summary>
+internal, but overridable
+</summary>
+</cvar>
+
+<cvar name="X_RPM_POSTUNINSTALL">
+<summary>
+This is used to fill in the
+<literal>%postun:</literal>
+section in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_PREUNINSTALL">
+<summary>
+This is used to fill in the
+<literal>%preun:</literal>
+section in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_REQUIRES">
+<summary>
+This is used to fill in the
+<literal>Requires:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_SERIAL">
+<summary>
+This is used to fill in the
+<literal>Serial:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+<cvar name="X_RPM_URL">
+<summary>
+This is used to fill in the
+<literal>Url:</literal>
+field in the RPM
+<filename>.spec</filename> file.
+</summary>
+</cvar>
+
+
+
+<!--
+
+THE FOLLOWING AREN'T CONSTRUCTION VARIABLES,
+THEY'RE "TAGS" THAT CAN BE ATTACHED
+TO DIFFERENT FILES OR DIRECTORIES.
+NOT SURE YET WHAT TO DO ABOUT THESE.
+
+<cvar name="CONFIG">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="CONFIG_NOREPLACE">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="DOC">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="INSTALL_LOCATION">
+<summary>
+internal, but overridable, TODO
+</summary>
+</cvar>
+
+<cvar name="LANG_lang">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="UNIX_ATTR">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_IPK_POSTINST">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_IPK_POSTRM">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_IPK_PREINST">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_IPK_PRERM">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_MSI_FEATURE">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_MSI_FILEID">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_MSI_LONGNAME">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_MSI_SHORTNAME">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_MSI_VITAL">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_RPM_DIR">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_RPM_DOCDIR">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_RPM_GHOST">
+<summary>
+TODO
+</summary>
+</cvar>
+
+<cvar name="X_RPM_VERIFY">
+<summary>
+TODO
+</summary>
+</cvar>
+
+-->
+
+
+<!--
+<builder name="Tag">
+<summary>
+Leaves hints for the Package() Builder on how specific
+files or directories should be packaged.
+All tags are optional.
+
+<example>
+# makes sure the built library will be installed with 0644 file
+# access mode
+Tag( Library( 'lib.c' ), unix-attr="0644" )
+
+# marks file2.txt to be a documentation file
+Tag( 'file2.txt', doc )
+</example>
+</summary>
+</builder>
+
+<function name="FindSourceFiles">
+<summary>
+A convenience function which returns all leaves of the build tree.
+</summary>
+</function>
+
+<builder name="FindInstalledFiles">
+<summary>
+Returns all files "built" by the &b-Install; or &b-InstallAs; builders.
+</summary>
+</function>
+-->
diff --git a/src/engine/SCons/Tool/packaging/ipk.py b/src/engine/SCons/Tool/packaging/ipk.py
new file mode 100644
index 0000000..d566f48
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/ipk.py
@@ -0,0 +1,185 @@
+"""SCons.Tool.Packaging.ipk
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/ipk.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Builder
+import SCons.Node.FS
+import os
+
+from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION,
+ SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL,
+ X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw):
+ """ this function prepares the packageroot directory for packaging with the
+ ipkg builder.
+ """
+ SCons.Tool.Tool('ipkg').generate(env)
+
+ # setup the Ipkg builder
+ bld = env['BUILDERS']['Ipkg']
+ target, source = stripinstallbuilder(target, source, env)
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT)
+
+ # This should be overridable from the construction environment,
+ # which it is by using ARCHITECTURE=.
+ # Guessing based on what os.uname() returns at least allows it
+ # to work for both i386 and x86_64 Linux systems.
+ archmap = {
+ 'i686' : 'i386',
+ 'i586' : 'i386',
+ 'i486' : 'i386',
+ }
+
+ buildarchitecture = os.uname()[4]
+ buildarchitecture = archmap.get(buildarchitecture, buildarchitecture)
+
+ if kw.has_key('ARCHITECTURE'):
+ buildarchitecture = kw['ARCHITECTURE']
+
+ # setup the kw to contain the mandatory arguments to this fucntion.
+ # do this before calling any builder or setup function
+ loc=locals()
+ del loc['kw']
+ kw.update(loc)
+ del kw['source'], kw['target'], kw['env']
+
+ # generate the specfile
+ specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw)
+
+ # override the default target.
+ if str(target[0])=="%s-%s"%(NAME, VERSION):
+ target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ]
+
+ # now apply the Ipkg builder
+ return apply(bld, [env, target, specfile], kw)
+
+def gen_ipk_dir(proot, source, env, kw):
+ # make sure the packageroot is a Dir object.
+ if SCons.Util.is_String(proot): proot=env.Dir(proot)
+
+ # create the specfile builder
+ s_bld=SCons.Builder.Builder(
+ action = build_specfiles,
+ )
+
+ # create the specfile targets
+ spec_target=[]
+ control=proot.Dir('CONTROL')
+ spec_target.append(control.File('control'))
+ spec_target.append(control.File('conffiles'))
+ spec_target.append(control.File('postrm'))
+ spec_target.append(control.File('prerm'))
+ spec_target.append(control.File('postinst'))
+ spec_target.append(control.File('preinst'))
+
+ # apply the builder to the specfile targets
+ apply(s_bld, [env, spec_target, source], kw)
+
+ # the packageroot directory does now contain the specfiles.
+ return proot
+
+def build_specfiles(source, target, env):
+ """ filter the targets for the needed files and use the variables in env
+ to create the specfile.
+ """
+ #
+ # At first we care for the CONTROL/control file, which is the main file for ipk.
+ #
+ # For this we need to open multiple files in random order, so we store into
+ # a dict so they can be easily accessed.
+ #
+ #
+ opened_files={}
+ def open_file(needle, haystack):
+ try:
+ return opened_files[needle]
+ except KeyError:
+ file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0]
+ opened_files[needle]=open(file.abspath, 'w')
+ return opened_files[needle]
+
+ control_file=open_file('control', target)
+
+ if not env.has_key('X_IPK_DESCRIPTION'):
+ env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'],
+ env['DESCRIPTION'].replace('\n', '\n '))
+
+
+ content = """
+Package: $NAME
+Version: $VERSION
+Priority: $X_IPK_PRIORITY
+Section: $X_IPK_SECTION
+Source: $SOURCE_URL
+Architecture: $ARCHITECTURE
+Maintainer: $X_IPK_MAINTAINER
+Depends: $X_IPK_DEPENDS
+Description: $X_IPK_DESCRIPTION
+"""
+
+ control_file.write(env.subst(content))
+
+ #
+ # now handle the various other files, which purpose it is to set post-,
+ # pre-scripts and mark files as config files.
+ #
+ # We do so by filtering the source files for files which are marked with
+ # the "config" tag and afterwards we do the same for x_ipk_postrm,
+ # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags.
+ #
+ # The first one will write the name of the file into the file
+ # CONTROL/configfiles, the latter add the content of the x_ipk_* variable
+ # into the same named file.
+ #
+ for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]:
+ config=open_file('conffiles')
+ config.write(f.PACKAGING_INSTALL_LOCATION)
+ config.write('\n')
+
+ for str in 'POSTRM PRERM POSTINST PREINST'.split():
+ name="PACKAGING_X_IPK_%s"%str
+ for f in [x for x in source if name in dir(x)]:
+ file=open_file(name)
+ file.write(env[str])
+
+ #
+ # close all opened files
+ for f in opened_files.values():
+ f.close()
+
+ # call a user specified function
+ if env.has_key('CHANGE_SPECFILE'):
+ content += env['CHANGE_SPECFILE'](target)
+
+ return 0
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/msi.py b/src/engine/SCons/Tool/packaging/msi.py
new file mode 100644
index 0000000..3806df9
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/msi.py
@@ -0,0 +1,526 @@
+"""SCons.Tool.packaging.msi
+
+The msi packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/msi.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import SCons
+from SCons.Action import Action
+from SCons.Builder import Builder
+
+from xml.dom.minidom import *
+from xml.sax.saxutils import escape
+
+from SCons.Tool.packaging import stripinstallbuilder
+
+#
+# Utility functions
+#
+def convert_to_id(s, id_set):
+ """ Some parts of .wxs need an Id attribute (for example: The File and
+ Directory directives. The charset is limited to A-Z, a-z, digits,
+ underscores, periods. Each Id must begin with a letter or with a
+ underscore. Google for "CNDL0015" for information about this.
+
+ Requirements:
+ * the string created must only contain chars from the target charset.
+ * the string created must have a minimal editing distance from the
+ original string.
+ * the string created must be unique for the whole .wxs file.
+
+ Observation:
+ * There are 62 chars in the charset.
+
+ Idea:
+ * filter out forbidden characters. Check for a collision with the help
+ of the id_set. Add the number of the number of the collision at the
+ end of the created string. Furthermore care for a correct start of
+ the string.
+ """
+ charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.'
+ if s[0] in '0123456789.':
+ s += '_'+s
+ id = filter( lambda c : c in charset, s )
+
+ # did we already generate an id for this file?
+ try:
+ return id_set[id][s]
+ except KeyError:
+ # no we did not so initialize with the id
+ if not id_set.has_key(id): id_set[id] = { s : id }
+ # there is a collision, generate an id which is unique by appending
+ # the collision number
+ else: id_set[id][s] = id + str(len(id_set[id]))
+
+ return id_set[id][s]
+
+def is_dos_short_file_name(file):
+ """ examine if the given file is in the 8.3 form.
+ """
+ fname, ext = os.path.splitext(file)
+ proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot
+ proper_fname = file.isupper() and len(fname) <= 8
+
+ return proper_ext and proper_fname
+
+def gen_dos_short_file_name(file, filename_set):
+ """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982
+
+ These are no complete 8.3 dos short names. The ~ char is missing and
+ replaced with one character from the filename. WiX warns about such
+ filenames, since a collision might occur. Google for "CNDL1014" for
+ more information.
+ """
+ # guard this to not confuse the generation
+ if is_dos_short_file_name(file):
+ return file
+
+ fname, ext = os.path.splitext(file) # ext contains the dot
+
+ # first try if it suffices to convert to upper
+ file = file.upper()
+ if is_dos_short_file_name(file):
+ return file
+
+ # strip forbidden characters.
+ forbidden = '."/[]:;=, '
+ fname = filter( lambda c : c not in forbidden, fname )
+
+ # check if we already generated a filename with the same number:
+ # thisis1.txt, thisis2.txt etc.
+ duplicate, num = not None, 1
+ while duplicate:
+ shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\
+ str(num))
+ if len(ext) >= 2:
+ shortname = "%s%s" % (shortname, ext[:4].upper())
+
+ duplicate, num = shortname in filename_set, num+1
+
+ assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file)
+ filename_set.append(shortname)
+ return shortname
+
+def create_feature_dict(files):
+ """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a
+ hierarchy. This function collects the files into this hierarchy.
+ """
+ dict = {}
+
+ def add_to_dict( feature, file ):
+ if not SCons.Util.is_List( feature ):
+ feature = [ feature ]
+
+ for f in feature:
+ if not dict.has_key( f ):
+ dict[ f ] = [ file ]
+ else:
+ dict[ f ].append( file )
+
+ for file in files:
+ if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ):
+ add_to_dict(file.PACKAGING_X_MSI_FEATURE, file)
+ elif hasattr( file, 'PACKAGING_DOC' ):
+ add_to_dict( 'PACKAGING_DOC', file )
+ else:
+ add_to_dict( 'default', file )
+
+ return dict
+
+def generate_guids(root):
+ """ generates globally unique identifiers for parts of the xml which need
+ them.
+
+ Component tags have a special requirement. Their UUID is only allowed to
+ change if the list of their contained resources has changed. This allows
+ for clean removal and proper updates.
+
+ To handle this requirement, the uuid is generated with an md5 hashing the
+ whole subtree of a xml node.
+ """
+ from hashlib import md5
+
+ # specify which tags need a guid and in which attribute this should be stored.
+ needs_id = { 'Product' : 'Id',
+ 'Package' : 'Id',
+ 'Component' : 'Guid',
+ }
+
+ # find all XMl nodes matching the key, retrieve their attribute, hash their
+ # subtree, convert hash to string and add as a attribute to the xml node.
+ for (key,value) in needs_id.items():
+ node_list = root.getElementsByTagName(key)
+ attribute = value
+ for node in node_list:
+ hash = md5(node.toxml()).hexdigest()
+ hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] )
+ node.attributes[attribute] = hash_str
+
+
+
+def string_wxsfile(target, source, env):
+ return "building WiX file %s"%( target[0].path )
+
+def build_wxsfile(target, source, env):
+ """ compiles a .wxs file from the keywords given in env['msi_spec'] and
+ by analyzing the tree of source nodes and their tags.
+ """
+ file = open(target[0].abspath, 'w')
+
+ try:
+ # Create a document with the Wix root tag
+ doc = Document()
+ root = doc.createElement( 'Wix' )
+ root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi'
+ doc.appendChild( root )
+
+ filename_set = [] # this is to circumvent duplicates in the shortnames
+ id_set = {} # this is to circumvent duplicates in the ids
+
+ # Create the content
+ build_wxsfile_header_section(root, env)
+ build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set)
+ generate_guids(root)
+ build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set)
+ build_wxsfile_default_gui(root)
+ build_license_file(target[0].get_dir(), env)
+
+ # write the xml to a file
+ file.write( doc.toprettyxml() )
+
+ # call a user specified function
+ if env.has_key('CHANGE_SPECFILE'):
+ env['CHANGE_SPECFILE'](target, source)
+
+ except KeyError, e:
+ raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] )
+
+#
+# setup function
+#
+def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set):
+ """ Create the wix default target directory layout and return the innermost
+ directory.
+
+ We assume that the XML tree delivered in the root argument already contains
+ the Product tag.
+
+ Everything is put under the PFiles directory property defined by WiX.
+ After that a directory with the 'VENDOR' tag is placed and then a
+ directory with the name of the project and its VERSION. This leads to the
+ following TARGET Directory Layout:
+ C:\<PFiles>\<Vendor>\<Projectname-Version>\
+ Example: C:\Programme\Company\Product-1.2\
+ """
+ doc = Document()
+ d1 = doc.createElement( 'Directory' )
+ d1.attributes['Id'] = 'TARGETDIR'
+ d1.attributes['Name'] = 'SourceDir'
+
+ d2 = doc.createElement( 'Directory' )
+ d2.attributes['Id'] = 'ProgramFilesFolder'
+ d2.attributes['Name'] = 'PFiles'
+
+ d3 = doc.createElement( 'Directory' )
+ d3.attributes['Id'] = 'VENDOR_folder'
+ d3.attributes['Name'] = escape( gen_dos_short_file_name( VENDOR, filename_set ) )
+ d3.attributes['LongName'] = escape( VENDOR )
+
+ d4 = doc.createElement( 'Directory' )
+ project_folder = "%s-%s" % ( NAME, VERSION )
+ d4.attributes['Id'] = 'MY_DEFAULT_FOLDER'
+ d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) )
+ d4.attributes['LongName'] = escape( project_folder )
+
+ d1.childNodes.append( d2 )
+ d2.childNodes.append( d3 )
+ d3.childNodes.append( d4 )
+
+ root.getElementsByTagName('Product')[0].childNodes.append( d1 )
+
+ return d4
+
+#
+# mandatory and optional file tags
+#
+def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set):
+ """ builds the Component sections of the wxs file with their included files.
+
+ Files need to be specified in 8.3 format and in the long name format, long
+ filenames will be converted automatically.
+
+ Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag.
+ """
+ root = create_default_directory_layout( root, NAME, VERSION, VENDOR, filename_set )
+ components = create_feature_dict( files )
+ factory = Document()
+
+ def get_directory( node, dir ):
+ """ returns the node under the given node representing the directory.
+
+ Returns the component node if dir is None or empty.
+ """
+ if dir == '' or not dir:
+ return node
+
+ Directory = node
+ dir_parts = dir.split(os.path.sep)
+
+ # to make sure that our directory ids are unique, the parent folders are
+ # consecutively added to upper_dir
+ upper_dir = ''
+
+ # walk down the xml tree finding parts of the directory
+ dir_parts = filter( lambda d: d != '', dir_parts )
+ for d in dir_parts[:]:
+ already_created = filter( lambda c: c.nodeName == 'Directory' and c.attributes['LongName'].value == escape(d), Directory.childNodes )
+
+ if already_created != []:
+ Directory = already_created[0]
+ dir_parts.remove(d)
+ upper_dir += d
+ else:
+ break
+
+ for d in dir_parts:
+ nDirectory = factory.createElement( 'Directory' )
+ nDirectory.attributes['LongName'] = escape( d )
+ nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) )
+ upper_dir += d
+ nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set )
+
+ Directory.childNodes.append( nDirectory )
+ Directory = nDirectory
+
+ return Directory
+
+ for file in files:
+ drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION )
+ filename = os.path.basename( path )
+ dirname = os.path.dirname( path )
+
+ h = {
+ # tagname : default value
+ 'PACKAGING_X_MSI_VITAL' : 'yes',
+ 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set),
+ 'PACKAGING_X_MSI_LONGNAME' : filename,
+ 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set),
+ 'PACKAGING_X_MSI_SOURCE' : file.get_path(),
+ }
+
+ # fill in the default tags given above.
+ for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]:
+ setattr( file, k, v )
+
+ File = factory.createElement( 'File' )
+ File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME )
+ File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME )
+ File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE )
+ File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID )
+ File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL )
+
+ # create the <Component> Tag under which this file should appear
+ Component = factory.createElement('Component')
+ Component.attributes['DiskId'] = '1'
+ Component.attributes['Id'] = convert_to_id( filename, id_set )
+
+ # hang the component node under the root node and the file node
+ # under the component node.
+ Directory = get_directory( root, dirname )
+ Directory.childNodes.append( Component )
+ Component.childNodes.append( File )
+
+#
+# additional functions
+#
+def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set):
+ """ This function creates the <features> tag based on the supplied xml tree.
+
+ This is achieved by finding all <component>s and adding them to a default target.
+
+ It should be called after the tree has been built completly. We assume
+ that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree.
+
+ Furthermore a top-level with the name and VERSION of the software will be created.
+
+ An PACKAGING_X_MSI_FEATURE can either be a string, where the feature
+ DESCRIPTION will be the same as its title or a Tuple, where the first
+ part will be its title and the second its DESCRIPTION.
+ """
+ factory = Document()
+ Feature = factory.createElement('Feature')
+ Feature.attributes['Id'] = 'complete'
+ Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER'
+ Feature.attributes['Level'] = '1'
+ Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) )
+ Feature.attributes['Description'] = escape( SUMMARY )
+ Feature.attributes['Display'] = 'expand'
+
+ for (feature, files) in create_feature_dict(files).items():
+ SubFeature = factory.createElement('Feature')
+ SubFeature.attributes['Level'] = '1'
+
+ if SCons.Util.is_Tuple(feature):
+ SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set )
+ SubFeature.attributes['Title'] = escape(feature[0])
+ SubFeature.attributes['Description'] = escape(feature[1])
+ else:
+ SubFeature.attributes['Id'] = convert_to_id( feature, id_set )
+ if feature=='default':
+ SubFeature.attributes['Description'] = 'Main Part'
+ SubFeature.attributes['Title'] = 'Main Part'
+ elif feature=='PACKAGING_DOC':
+ SubFeature.attributes['Description'] = 'Documentation'
+ SubFeature.attributes['Title'] = 'Documentation'
+ else:
+ SubFeature.attributes['Description'] = escape(feature)
+ SubFeature.attributes['Title'] = escape(feature)
+
+ # build the componentrefs. As one of the design decision is that every
+ # file is also a component we walk the list of files and create a
+ # reference.
+ for f in files:
+ ComponentRef = factory.createElement('ComponentRef')
+ ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set )
+ SubFeature.childNodes.append(ComponentRef)
+
+ Feature.childNodes.append(SubFeature)
+
+ root.getElementsByTagName('Product')[0].childNodes.append(Feature)
+
+def build_wxsfile_default_gui(root):
+ """ this function adds a default GUI to the wxs file
+ """
+ factory = Document()
+ Product = root.getElementsByTagName('Product')[0]
+
+ UIRef = factory.createElement('UIRef')
+ UIRef.attributes['Id'] = 'WixUI_Mondo'
+ Product.childNodes.append(UIRef)
+
+ UIRef = factory.createElement('UIRef')
+ UIRef.attributes['Id'] = 'WixUI_ErrorProgressText'
+ Product.childNodes.append(UIRef)
+
+def build_license_file(directory, spec):
+ """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT"
+ in the given directory
+ """
+ name, text = '', ''
+
+ try:
+ name = spec['LICENSE']
+ text = spec['X_MSI_LICENSE_TEXT']
+ except KeyError:
+ pass # ignore this as X_MSI_LICENSE_TEXT is optional
+
+ if name!='' or text!='':
+ file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' )
+ file.write('{\\rtf')
+ if text!='':
+ file.write(text.replace('\n', '\\par '))
+ else:
+ file.write(name+'\\par\\par')
+ file.write('}')
+ file.close()
+
+#
+# mandatory and optional package tags
+#
+def build_wxsfile_header_section(root, spec):
+ """ Adds the xml file node which define the package meta-data.
+ """
+ # Create the needed DOM nodes and add them at the correct position in the tree.
+ factory = Document()
+ Product = factory.createElement( 'Product' )
+ Package = factory.createElement( 'Package' )
+
+ root.childNodes.append( Product )
+ Product.childNodes.append( Package )
+
+ # set "mandatory" default values
+ if not spec.has_key('X_MSI_LANGUAGE'):
+ spec['X_MSI_LANGUAGE'] = '1033' # select english
+
+ # mandatory sections, will throw a KeyError if the tag is not available
+ Product.attributes['Name'] = escape( spec['NAME'] )
+ Product.attributes['Version'] = escape( spec['VERSION'] )
+ Product.attributes['Manufacturer'] = escape( spec['VENDOR'] )
+ Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] )
+ Package.attributes['Description'] = escape( spec['SUMMARY'] )
+
+ # now the optional tags, for which we avoid the KeyErrror exception
+ if spec.has_key( 'DESCRIPTION' ):
+ Package.attributes['Comments'] = escape( spec['DESCRIPTION'] )
+
+ if spec.has_key( 'X_MSI_UPGRADE_CODE' ):
+ Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] )
+
+ # We hardcode the media tag as our current model cannot handle it.
+ Media = factory.createElement('Media')
+ Media.attributes['Id'] = '1'
+ Media.attributes['Cabinet'] = 'default.cab'
+ Media.attributes['EmbedCab'] = 'yes'
+ root.getElementsByTagName('Product')[0].childNodes.append(Media)
+
+# this builder is the entry-point for .wxs file compiler.
+wxs_builder = Builder(
+ action = Action( build_wxsfile, string_wxsfile ),
+ ensure_suffix = '.wxs' )
+
+def package(env, target, source, PACKAGEROOT, NAME, VERSION,
+ DESCRIPTION, SUMMARY, VENDOR, X_MSI_LANGUAGE, **kw):
+ # make sure that the Wix Builder is in the environment
+ SCons.Tool.Tool('wix').generate(env)
+
+ # get put the keywords for the specfile compiler. These are the arguments
+ # given to the package function and all optional ones stored in kw, minus
+ # the the source, target and env one.
+ loc = locals()
+ del loc['kw']
+ kw.update(loc)
+ del kw['source'], kw['target'], kw['env']
+
+ # strip the install builder from the source files
+ target, source = stripinstallbuilder(target, source, env)
+
+ # put the arguments into the env and call the specfile builder.
+ env['msi_spec'] = kw
+ specfile = apply( wxs_builder, [env, target, source], kw )
+
+ # now call the WiX Tool with the built specfile added as a source.
+ msifile = env.WiX(target, specfile)
+
+ # return the target and source tuple.
+ return (msifile, source+[specfile])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py
new file mode 100644
index 0000000..461b4b1
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/rpm.py
@@ -0,0 +1,367 @@
+"""SCons.Tool.Packaging.rpm
+
+The rpm packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/rpm.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import string
+
+import SCons.Builder
+
+from SCons.Environment import OverrideEnvironment
+from SCons.Tool.packaging import stripinstallbuilder, src_targz
+from SCons.Errors import UserError
+
+def package(env, target, source, PACKAGEROOT, NAME, VERSION,
+ PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE,
+ **kw):
+ # initialize the rpm tool
+ SCons.Tool.Tool('rpm').generate(env)
+
+ bld = env['BUILDERS']['Rpm']
+
+ # Generate a UserError whenever the target name has been set explicitly,
+ # since rpm does not allow for controlling it. This is detected by
+ # checking if the target has been set to the default by the Package()
+ # Environment function.
+ if str(target[0])!="%s-%s"%(NAME, VERSION):
+ raise UserError( "Setting target is not supported for rpm." )
+ else:
+ # This should be overridable from the construction environment,
+ # which it is by using ARCHITECTURE=.
+ # Guessing based on what os.uname() returns at least allows it
+ # to work for both i386 and x86_64 Linux systems.
+ archmap = {
+ 'i686' : 'i386',
+ 'i586' : 'i386',
+ 'i486' : 'i386',
+ }
+
+ buildarchitecture = os.uname()[4]
+ buildarchitecture = archmap.get(buildarchitecture, buildarchitecture)
+
+ if kw.has_key('ARCHITECTURE'):
+ buildarchitecture = kw['ARCHITECTURE']
+
+ fmt = '%s-%s-%s.%s.rpm'
+ srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src')
+ binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture)
+
+ target = [ srcrpm, binrpm ]
+
+ # get the correct arguments into the kw hash
+ loc=locals()
+ del loc['kw']
+ kw.update(loc)
+ del kw['source'], kw['target'], kw['env']
+
+ # if no "SOURCE_URL" tag is given add a default one.
+ if not kw.has_key('SOURCE_URL'):
+ #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '')
+ kw['SOURCE_URL']=string.replace(str(target[0])+".tar.gz", '.rpm', '')
+
+ # mangle the source and target list for the rpmbuild
+ env = OverrideEnvironment(env, kw)
+ target, source = stripinstallbuilder(target, source, env)
+ target, source = addspecfile(target, source, env)
+ target, source = collectintargz(target, source, env)
+
+ # now call the rpm builder to actually build the packet.
+ return apply(bld, [env, target, source], kw)
+
+def collectintargz(target, source, env):
+ """ Puts all source files into a tar.gz file. """
+ # the rpm tool depends on a source package, until this is chagned
+ # this hack needs to be here that tries to pack all sources in.
+ sources = env.FindSourceFiles()
+
+ # filter out the target we are building the source list for.
+ #sources = [s for s in sources if not (s in target)]
+ sources = filter(lambda s, t=target: not (s in t), sources)
+
+ # find the .spec file for rpm and add it since it is not necessarily found
+ # by the FindSourceFiles function.
+ #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] )
+ spec_file = lambda s: string.rfind(str(s), '.spec') != -1
+ sources.extend( filter(spec_file, source) )
+
+ # as the source contains the url of the source package this rpm package
+ # is built from, we extract the target name
+ #tarball = (str(target[0])+".tar.gz").replace('.rpm', '')
+ tarball = string.replace(str(target[0])+".tar.gz", '.rpm', '')
+ try:
+ #tarball = env['SOURCE_URL'].split('/')[-1]
+ tarball = string.split(env['SOURCE_URL'], '/')[-1]
+ except KeyError, e:
+ raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] )
+
+ tarball = src_targz.package(env, source=sources, target=tarball,
+ PACKAGEROOT=env['PACKAGEROOT'], )
+
+ return (target, tarball)
+
+def addspecfile(target, source, env):
+ specfile = "%s-%s" % (env['NAME'], env['VERSION'])
+
+ bld = SCons.Builder.Builder(action = build_specfile,
+ suffix = '.spec',
+ target_factory = SCons.Node.FS.File)
+
+ source.extend(bld(env, specfile, source))
+
+ return (target,source)
+
+def build_specfile(target, source, env):
+ """ Builds a RPM specfile from a dictionary with string metadata and
+ by analyzing a tree of nodes.
+ """
+ file = open(target[0].abspath, 'w')
+ str = ""
+
+ try:
+ file.write( build_specfile_header(env) )
+ file.write( build_specfile_sections(env) )
+ file.write( build_specfile_filesection(env, source) )
+ file.close()
+
+ # call a user specified function
+ if env.has_key('CHANGE_SPECFILE'):
+ env['CHANGE_SPECFILE'](target, source)
+
+ except KeyError, e:
+ raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] )
+
+
+#
+# mandatory and optional package tag section
+#
+def build_specfile_sections(spec):
+ """ Builds the sections of a rpm specfile.
+ """
+ str = ""
+
+ mandatory_sections = {
+ 'DESCRIPTION' : '\n%%description\n%s\n\n', }
+
+ str = str + SimpleTagCompiler(mandatory_sections).compile( spec )
+
+ optional_sections = {
+ 'DESCRIPTION_' : '%%description -l %s\n%s\n\n',
+ 'CHANGELOG' : '%%changelog\n%s\n\n',
+ 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n',
+ 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n',
+ 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n',
+ 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n',
+ 'X_RPM_VERIFY' : '%%verify\n%s\n\n',
+
+ # These are for internal use but could possibly be overriden
+ 'X_RPM_PREP' : '%%prep\n%s\n\n',
+ 'X_RPM_BUILD' : '%%build\n%s\n\n',
+ 'X_RPM_INSTALL' : '%%install\n%s\n\n',
+ 'X_RPM_CLEAN' : '%%clean\n%s\n\n',
+ }
+
+ # Default prep, build, install and clean rules
+ # TODO: optimize those build steps, to not compile the project a second time
+ if not spec.has_key('X_RPM_PREP'):
+ spec['X_RPM_PREP'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q'
+
+ if not spec.has_key('X_RPM_BUILD'):
+ spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"'
+
+ if not spec.has_key('X_RPM_INSTALL'):
+ spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"'
+
+ if not spec.has_key('X_RPM_CLEAN'):
+ spec['X_RPM_CLEAN'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"'
+
+ str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec )
+
+ return str
+
+def build_specfile_header(spec):
+ """ Builds all section but the %file of a rpm specfile
+ """
+ str = ""
+
+ # first the mandatory sections
+ mandatory_header_fields = {
+ 'NAME' : '%%define name %s\nName: %%{name}\n',
+ 'VERSION' : '%%define version %s\nVersion: %%{version}\n',
+ 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n',
+ 'X_RPM_GROUP' : 'Group: %s\n',
+ 'SUMMARY' : 'Summary: %s\n',
+ 'LICENSE' : 'License: %s\n', }
+
+ str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec )
+
+ # now the optional tags
+ optional_header_fields = {
+ 'VENDOR' : 'Vendor: %s\n',
+ 'X_RPM_URL' : 'Url: %s\n',
+ 'SOURCE_URL' : 'Source: %s\n',
+ 'SUMMARY_' : 'Summary(%s): %s\n',
+ 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n',
+ 'X_RPM_ICON' : 'Icon: %s\n',
+ 'X_RPM_PACKAGER' : 'Packager: %s\n',
+ 'X_RPM_GROUP_' : 'Group(%s): %s\n',
+
+ 'X_RPM_REQUIRES' : 'Requires: %s\n',
+ 'X_RPM_PROVIDES' : 'Provides: %s\n',
+ 'X_RPM_CONFLICTS' : 'Conflicts: %s\n',
+ 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n',
+
+ 'X_RPM_SERIAL' : 'Serial: %s\n',
+ 'X_RPM_EPOCH' : 'Epoch: %s\n',
+ 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n',
+ 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n',
+ 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n',
+ 'X_RPM_PREFIX' : 'Prefix: %s\n',
+ 'X_RPM_CONFLICTS' : 'Conflicts: %s\n',
+
+ # internal use
+ 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', }
+
+ # fill in default values:
+ # Adding a BuildRequires renders the .rpm unbuildable under System, which
+ # are not managed by rpm, since the database to resolve this dependency is
+ # missing (take Gentoo as an example)
+# if not s.has_key('x_rpm_BuildRequires'):
+# s['x_rpm_BuildRequires'] = 'scons'
+
+ if not spec.has_key('X_RPM_BUILDROOT'):
+ spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}'
+
+ str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec )
+ return str
+
+#
+# mandatory and optional file tags
+#
+def build_specfile_filesection(spec, files):
+ """ builds the %file section of the specfile
+ """
+ str = '%files\n'
+
+ if not spec.has_key('X_RPM_DEFATTR'):
+ spec['X_RPM_DEFATTR'] = '(-,root,root)'
+
+ str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR']
+
+ supported_tags = {
+ 'PACKAGING_CONFIG' : '%%config %s',
+ 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s',
+ 'PACKAGING_DOC' : '%%doc %s',
+ 'PACKAGING_UNIX_ATTR' : '%%attr %s',
+ 'PACKAGING_LANG_' : '%%lang(%s) %s',
+ 'PACKAGING_X_RPM_VERIFY' : '%%verify %s',
+ 'PACKAGING_X_RPM_DIR' : '%%dir %s',
+ 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s',
+ 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', }
+
+ for file in files:
+ # build the tagset
+ tags = {}
+ for k in supported_tags.keys():
+ try:
+ tags[k]=getattr(file, k)
+ except AttributeError:
+ pass
+
+ # compile the tagset
+ str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags )
+
+ str = str + ' '
+ str = str + file.PACKAGING_INSTALL_LOCATION
+ str = str + '\n\n'
+
+ return str
+
+class SimpleTagCompiler:
+ """ This class is a simple string substition utility:
+ the replacement specfication is stored in the tagset dictionary, something
+ like:
+ { "abc" : "cdef %s ",
+ "abc_" : "cdef %s %s" }
+
+ the compile function gets a value dictionary, which may look like:
+ { "abc" : "ghij",
+ "abc_gh" : "ij" }
+
+ The resulting string will be:
+ "cdef ghij cdef gh ij"
+ """
+ def __init__(self, tagset, mandatory=1):
+ self.tagset = tagset
+ self.mandatory = mandatory
+
+ def compile(self, values):
+ """ compiles the tagset and returns a str containing the result
+ """
+ def is_international(tag):
+ #return tag.endswith('_')
+ return tag[-1:] == '_'
+
+ def get_country_code(tag):
+ return tag[-2:]
+
+ def strip_country_code(tag):
+ return tag[:-2]
+
+ replacements = self.tagset.items()
+
+ str = ""
+ #domestic = [ (k,v) for k,v in replacements if not is_international(k) ]
+ domestic = filter(lambda t, i=is_international: not i(t[0]), replacements)
+ for key, replacement in domestic:
+ try:
+ str = str + replacement % values[key]
+ except KeyError, e:
+ if self.mandatory:
+ raise e
+
+ #international = [ (k,v) for k,v in replacements if is_international(k) ]
+ international = filter(lambda t, i=is_international: i(t[0]), replacements)
+ for key, replacement in international:
+ try:
+ #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ]
+ x = filter(lambda t,key=key,s=strip_country_code: s(t[0]) == key, values.items())
+ int_values_for_key = map(lambda t,g=get_country_code: (g(t[0]),t[1]), x)
+ for v in int_values_for_key:
+ str = str + replacement % v
+ except KeyError, e:
+ if self.mandatory:
+ raise e
+
+ return str
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/src_tarbz2.py b/src/engine/SCons/Tool/packaging/src_tarbz2.py
new file mode 100644
index 0000000..aaeb7a8
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/src_tarbz2.py
@@ -0,0 +1,43 @@
+"""SCons.Tool.Packaging.tarbz2
+
+The tarbz2 SRC packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.packaging import putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, **kw):
+ bld = env['BUILDERS']['Tar']
+ bld.set_suffix('.tar.bz2')
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0)
+ return bld(env, target, source, TARFLAGS='-jc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/src_targz.py b/src/engine/SCons/Tool/packaging/src_targz.py
new file mode 100644
index 0000000..5be52e6
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/src_targz.py
@@ -0,0 +1,43 @@
+"""SCons.Tool.Packaging.targz
+
+The targz SRC packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.packaging import putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, **kw):
+ bld = env['BUILDERS']['Tar']
+ bld.set_suffix('.tar.gz')
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0)
+ return bld(env, target, source, TARFLAGS='-zc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/src_zip.py b/src/engine/SCons/Tool/packaging/src_zip.py
new file mode 100644
index 0000000..cfa73b1
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/src_zip.py
@@ -0,0 +1,43 @@
+"""SCons.Tool.Packaging.zip
+
+The zip SRC packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.packaging import putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, **kw):
+ bld = env['BUILDERS']['Zip']
+ bld.set_suffix('.zip')
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0)
+ return bld(env, target, source)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/tarbz2.py b/src/engine/SCons/Tool/packaging/tarbz2.py
new file mode 100644
index 0000000..a0c2b81
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/tarbz2.py
@@ -0,0 +1,44 @@
+"""SCons.Tool.Packaging.tarbz2
+
+The tarbz2 SRC packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, **kw):
+ bld = env['BUILDERS']['Tar']
+ bld.set_suffix('.tar.gz')
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT)
+ target, source = stripinstallbuilder(target, source, env)
+ return bld(env, target, source, TARFLAGS='-jc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/targz.py b/src/engine/SCons/Tool/packaging/targz.py
new file mode 100644
index 0000000..b985163
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/targz.py
@@ -0,0 +1,44 @@
+"""SCons.Tool.Packaging.targz
+
+The targz SRC packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/targz.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, **kw):
+ bld = env['BUILDERS']['Tar']
+ bld.set_suffix('.tar.gz')
+ target, source = stripinstallbuilder(target, source, env)
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT)
+ return bld(env, target, source, TARFLAGS='-zc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/packaging/zip.py b/src/engine/SCons/Tool/packaging/zip.py
new file mode 100644
index 0000000..590c975
--- /dev/null
+++ b/src/engine/SCons/Tool/packaging/zip.py
@@ -0,0 +1,44 @@
+"""SCons.Tool.Packaging.zip
+
+The zip SRC packager.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/packaging/zip.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot
+
+def package(env, target, source, PACKAGEROOT, **kw):
+ bld = env['BUILDERS']['Zip']
+ bld.set_suffix('.zip')
+ target, source = stripinstallbuilder(target, source, env)
+ target, source = putintopackageroot(target, source, env, PACKAGEROOT)
+ return bld(env, target, source)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/pdf.py b/src/engine/SCons/Tool/pdf.py
new file mode 100644
index 0000000..1ce561e
--- /dev/null
+++ b/src/engine/SCons/Tool/pdf.py
@@ -0,0 +1,78 @@
+"""SCons.Tool.pdf
+
+Common PDF Builder definition for various other Tool modules that use it.
+Add an explicit action to run epstopdf to convert .eps files to .pdf
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/pdf.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Builder
+import SCons.Tool
+
+PDFBuilder = None
+
+EpsPdfAction = SCons.Action.Action('$EPSTOPDFCOM', '$EPSTOPDFCOMSTR')
+
+def generate(env):
+ try:
+ env['BUILDERS']['PDF']
+ except KeyError:
+ global PDFBuilder
+ if PDFBuilder is None:
+ PDFBuilder = SCons.Builder.Builder(action = {},
+ source_scanner = SCons.Tool.PDFLaTeXScanner,
+ prefix = '$PDFPREFIX',
+ suffix = '$PDFSUFFIX',
+ emitter = {},
+ source_ext_match = None,
+ single_source=True)
+ env['BUILDERS']['PDF'] = PDFBuilder
+
+ env['PDFPREFIX'] = ''
+ env['PDFSUFFIX'] = '.pdf'
+
+# put the epstopdf builder in this routine so we can add it after
+# the pdftex builder so that one is the default for no source suffix
+def generate2(env):
+ bld = env['BUILDERS']['PDF']
+ #bld.add_action('.ps', EpsPdfAction) # this is covered by direct Ghostcript action in gs.py
+ bld.add_action('.eps', EpsPdfAction)
+
+ env['EPSTOPDF'] = 'epstopdf'
+ env['EPSTOPDFFLAGS'] = SCons.Util.CLVar('')
+ env['EPSTOPDFCOM'] = '$EPSTOPDF $EPSTOPDFFLAGS ${SOURCE} --outfile=${TARGET}'
+
+def exists(env):
+ # This only puts a skeleton Builder in place, so if someone
+ # references this Tool directly, it's always "available."
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/pdf.xml b/src/engine/SCons/Tool/pdf.xml
new file mode 100644
index 0000000..a695079
--- /dev/null
+++ b/src/engine/SCons/Tool/pdf.xml
@@ -0,0 +1,49 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="pdf">
+<summary>
+Sets construction variables for the Portable Document Format builder.
+</summary>
+<sets>
+PDFPREFIX
+PDFSUFFIX
+</sets>
+</tool>
+
+<builder name="PDF">
+<summary>
+Builds a <filename>.pdf</filename> file
+from a <filename>.dvi</filename> input file
+(or, by extension, a <filename>.tex</filename>,
+<filename>.ltx</filename>,
+or
+<filename>.latex</filename> input file).
+The suffix specified by the &cv-link-PDFSUFFIX; construction variable
+(<filename>.pdf</filename> by default)
+is added automatically to the target
+if it is not already present. Example:
+
+<example>
+# builds from aaa.tex
+env.PDF(target = 'aaa.pdf', source = 'aaa.tex')
+# builds bbb.pdf from bbb.dvi
+env.PDF(target = 'bbb', source = 'bbb.dvi')
+</example>
+</summary>
+</builder>
+
+<cvar name="PDFPREFIX">
+<summary>
+The prefix used for PDF file names.
+</summary>
+</cvar>
+
+<cvar name="PDFSUFFIX">
+<summary>
+The suffix used for PDF file names.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/pdflatex.py b/src/engine/SCons/Tool/pdflatex.py
new file mode 100644
index 0000000..8a008b3
--- /dev/null
+++ b/src/engine/SCons/Tool/pdflatex.py
@@ -0,0 +1,83 @@
+"""SCons.Tool.pdflatex
+
+Tool-specific initialization for pdflatex.
+Generates .pdf files from .latex or .ltx files
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/pdflatex.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Util
+import SCons.Tool.pdf
+import SCons.Tool.tex
+
+PDFLaTeXAction = None
+
+def PDFLaTeXAuxFunction(target = None, source= None, env=None):
+ result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env )
+ if result != 0:
+ print env['PDFLATEX']," returned an error, check the log file"
+ return result
+
+PDFLaTeXAuxAction = None
+
+def generate(env):
+ """Add Builders and construction variables for pdflatex to an Environment."""
+ global PDFLaTeXAction
+ if PDFLaTeXAction is None:
+ PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR')
+
+ global PDFLaTeXAuxAction
+ if PDFLaTeXAuxAction is None:
+ PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction,
+ strfunction=SCons.Tool.tex.TeXLaTeXStrFunction)
+
+ env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes)
+
+ import pdf
+ pdf.generate(env)
+
+ bld = env['BUILDERS']['PDF']
+ bld.add_action('.ltx', PDFLaTeXAuxAction)
+ bld.add_action('.latex', PDFLaTeXAuxAction)
+ bld.add_emitter('.ltx', SCons.Tool.tex.tex_pdf_emitter)
+ bld.add_emitter('.latex', SCons.Tool.tex.tex_pdf_emitter)
+
+ SCons.Tool.tex.generate_common(env)
+
+def exists(env):
+ return env.Detect('pdflatex')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/pdflatex.xml b/src/engine/SCons/Tool/pdflatex.xml
new file mode 100644
index 0000000..3a4eaeb
--- /dev/null
+++ b/src/engine/SCons/Tool/pdflatex.xml
@@ -0,0 +1,49 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="pdflatex">
+<summary>
+Sets construction variables for the &pdflatex; utility.
+</summary>
+<sets>
+PDFLATEX
+PDFLATEXFLAGS
+PDFLATEXCOM
+LATEXRETRIES
+</sets>
+<uses>
+PDFLATEXCOMSTR
+</uses>
+</tool>
+
+<cvar name="PDFLATEX">
+<summary>
+The &pdflatex; utility.
+</summary>
+</cvar>
+
+<cvar name="PDFLATEXCOM">
+<summary>
+The command line used to call the &pdflatex; utility.
+</summary>
+</cvar>
+
+<cvar name="PDFLATEXCOMSTR">
+<summary>
+The string displayed when calling the &pdflatex; utility.
+If this is not set, then &cv-link-PDFLATEXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(PDFLATEX;COMSTR = "Building $TARGET from LaTeX input $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="PDFLATEXFLAGS">
+<summary>
+General options passed to the &pdflatex; utility.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/pdftex.py b/src/engine/SCons/Tool/pdftex.py
new file mode 100644
index 0000000..0733d16
--- /dev/null
+++ b/src/engine/SCons/Tool/pdftex.py
@@ -0,0 +1,108 @@
+"""SCons.Tool.pdftex
+
+Tool-specific initialization for pdftex.
+Generates .pdf files from .tex files
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/pdftex.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import SCons.Action
+import SCons.Util
+import SCons.Tool.tex
+
+PDFTeXAction = None
+
+# This action might be needed more than once if we are dealing with
+# labels and bibtex.
+PDFLaTeXAction = None
+
+def PDFLaTeXAuxAction(target = None, source= None, env=None):
+ result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env )
+ return result
+
+def PDFTeXLaTeXFunction(target = None, source= None, env=None):
+ """A builder for TeX and LaTeX that scans the source file to
+ decide the "flavor" of the source and then executes the appropriate
+ program."""
+ basedir = os.path.split(str(source[0]))[0]
+ abspath = os.path.abspath(basedir)
+
+ if SCons.Tool.tex.is_LaTeX(source,env,abspath):
+ result = PDFLaTeXAuxAction(target,source,env)
+ if result != 0:
+ print env['PDFLATEX']," returned an error, check the log file"
+ else:
+ result = PDFTeXAction(target,source,env)
+ if result != 0:
+ print env['PDFTEX']," returned an error, check the log file"
+ return result
+
+PDFTeXLaTeXAction = None
+
+def generate(env):
+ """Add Builders and construction variables for pdftex to an Environment."""
+ global PDFTeXAction
+ if PDFTeXAction is None:
+ PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR')
+
+ global PDFLaTeXAction
+ if PDFLaTeXAction is None:
+ PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR")
+
+ global PDFTeXLaTeXAction
+ if PDFTeXLaTeXAction is None:
+ PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction,
+ strfunction=SCons.Tool.tex.TeXLaTeXStrFunction)
+
+ env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes)
+
+ import pdf
+ pdf.generate(env)
+
+ bld = env['BUILDERS']['PDF']
+ bld.add_action('.tex', PDFTeXLaTeXAction)
+ bld.add_emitter('.tex', SCons.Tool.tex.tex_pdf_emitter)
+
+ # Add the epstopdf builder after the pdftex builder
+ # so pdftex is the default for no source suffix
+ pdf.generate2(env)
+
+ SCons.Tool.tex.generate_common(env)
+
+def exists(env):
+ return env.Detect('pdftex')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/pdftex.xml b/src/engine/SCons/Tool/pdftex.xml
new file mode 100644
index 0000000..15614a2
--- /dev/null
+++ b/src/engine/SCons/Tool/pdftex.xml
@@ -0,0 +1,53 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="pdftex">
+<summary>
+Sets construction variables for the &pdftex; utility.
+</summary>
+<sets>
+PDFTEX
+PDFTEXFLAGS
+PDFTEXCOM
+PDFLATEX
+PDFLATEXFLAGS
+PDFLATEXCOM
+LATEXRETRIES
+</sets>
+<uses>
+PDFTEXCOMSTR
+PDFLATEXCOMSTR
+</uses>
+</tool>
+
+<cvar name="PDFTEX">
+<summary>
+The &pdftex; utility.
+</summary>
+</cvar>
+
+<cvar name="PDFTEXCOM">
+<summary>
+The command line used to call the &pdftex; utility.
+</summary>
+</cvar>
+
+<cvar name="PDFTEXCOMSTR">
+<summary>
+The string displayed when calling the &pdftex; utility.
+If this is not set, then &cv-link-PDFTEXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(PDFTEXCOMSTR = "Building $TARGET from TeX input $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="PDFTEXFLAGS">
+<summary>
+General options passed to the &pdftex; utility.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py
new file mode 100644
index 0000000..72ae194
--- /dev/null
+++ b/src/engine/SCons/Tool/qt.py
@@ -0,0 +1,336 @@
+
+"""SCons.Tool.qt
+
+Tool-specific initialization for Qt.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/qt.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import re
+
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Scanner
+import SCons.Tool
+import SCons.Util
+
+class ToolQtWarning(SCons.Warnings.Warning):
+ pass
+
+class GeneratedMocFileNotIncluded(ToolQtWarning):
+ pass
+
+class QtdirNotFound(ToolQtWarning):
+ pass
+
+SCons.Warnings.enableWarningClass(ToolQtWarning)
+
+header_extensions = [".h", ".hxx", ".hpp", ".hh"]
+if SCons.Util.case_sensitive_suffixes('.h', '.H'):
+ header_extensions.append('.H')
+cplusplus = __import__('c++', globals(), locals(), [])
+cxx_suffixes = cplusplus.CXXSuffixes
+
+def checkMocIncluded(target, source, env):
+ moc = target[0]
+ cpp = source[0]
+ # looks like cpp.includes is cleared before the build stage :-(
+ # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
+ path = SCons.Defaults.CScan.path(env, moc.cwd)
+ includes = SCons.Defaults.CScan(cpp, env, path)
+ if not moc in includes:
+ SCons.Warnings.warn(
+ GeneratedMocFileNotIncluded,
+ "Generated moc file '%s' is not included by '%s'" %
+ (str(moc), str(cpp)))
+
+def find_file(filename, paths, node_factory):
+ for dir in paths:
+ node = node_factory(filename, dir)
+ if node.rexists():
+ return node
+ return None
+
+class _Automoc:
+ """
+ Callable class, which works as an emitter for Programs, SharedLibraries and
+ StaticLibraries.
+ """
+
+ def __init__(self, objBuilderName):
+ self.objBuilderName = objBuilderName
+
+ def __call__(self, target, source, env):
+ """
+ Smart autoscan function. Gets the list of objects for the Program
+ or Lib. Adds objects and builders for the special qt files.
+ """
+ try:
+ if int(env.subst('$QT_AUTOSCAN')) == 0:
+ return target, source
+ except ValueError:
+ pass
+ try:
+ debug = int(env.subst('$QT_DEBUG'))
+ except ValueError:
+ debug = 0
+
+ # some shortcuts used in the scanner
+ splitext = SCons.Util.splitext
+ objBuilder = getattr(env, self.objBuilderName)
+
+ # some regular expressions:
+ # Q_OBJECT detection
+ q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
+ # cxx and c comment 'eater'
+ #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
+ # CW: something must be wrong with the regexp. See also bug #998222
+ # CURRENTLY THERE IS NO TEST CASE FOR THAT
+
+ # The following is kind of hacky to get builders working properly (FIXME)
+ objBuilderEnv = objBuilder.env
+ objBuilder.env = env
+ mocBuilderEnv = env.Moc.env
+ env.Moc.env = env
+
+ # make a deep copy for the result; MocH objects will be appended
+ out_sources = source[:]
+
+ for obj in source:
+ if not obj.has_builder():
+ # binary obj file provided
+ if debug:
+ print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj)
+ continue
+ cpp = obj.sources[0]
+ if not splitext(str(cpp))[1] in cxx_suffixes:
+ if debug:
+ print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp)
+ # c or fortran source
+ continue
+ #cpp_contents = comment.sub('', cpp.get_text_contents())
+ cpp_contents = cpp.get_text_contents()
+ h=None
+ for h_ext in header_extensions:
+ # try to find the header file in the corresponding source
+ # directory
+ hname = splitext(cpp.name)[0] + h_ext
+ h = find_file(hname, (cpp.get_dir(),), env.File)
+ if h:
+ if debug:
+ print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))
+ #h_contents = comment.sub('', h.get_text_contents())
+ h_contents = h.get_text_contents()
+ break
+ if not h and debug:
+ print "scons: qt: no header for '%s'." % (str(cpp))
+ if h and q_object_search.search(h_contents):
+ # h file with the Q_OBJECT macro found -> add moc_cpp
+ moc_cpp = env.Moc(h)
+ moc_o = objBuilder(moc_cpp)
+ out_sources.append(moc_o)
+ #moc_cpp.target_scanner = SCons.Defaults.CScan
+ if debug:
+ print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp))
+ if cpp and q_object_search.search(cpp_contents):
+ # cpp file with Q_OBJECT macro found -> add moc
+ # (to be included in cpp)
+ moc = env.Moc(cpp)
+ env.Ignore(moc, moc)
+ if debug:
+ print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc))
+ #moc.source_scanner = SCons.Defaults.CScan
+ # restore the original env attributes (FIXME)
+ objBuilder.env = objBuilderEnv
+ env.Moc.env = mocBuilderEnv
+
+ return (target, out_sources)
+
+AutomocShared = _Automoc('SharedObject')
+AutomocStatic = _Automoc('StaticObject')
+
+def _detect(env):
+ """Not really safe, but fast method to detect the QT library"""
+ QTDIR = None
+ if not QTDIR:
+ QTDIR = env.get('QTDIR',None)
+ if not QTDIR:
+ QTDIR = os.environ.get('QTDIR',None)
+ if not QTDIR:
+ moc = env.WhereIs('moc')
+ if moc:
+ QTDIR = os.path.dirname(os.path.dirname(moc))
+ SCons.Warnings.warn(
+ QtdirNotFound,
+ "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR)
+ else:
+ QTDIR = None
+ SCons.Warnings.warn(
+ QtdirNotFound,
+ "Could not detect qt, using empty QTDIR")
+ return QTDIR
+
+def uicEmitter(target, source, env):
+ adjustixes = SCons.Util.adjustixes
+ bs = SCons.Util.splitext(str(source[0].name))[0]
+ bs = os.path.join(str(target[0].get_dir()),bs)
+ # first target (header) is automatically added by builder
+ if len(target) < 2:
+ # second target is implementation
+ target.append(adjustixes(bs,
+ env.subst('$QT_UICIMPLPREFIX'),
+ env.subst('$QT_UICIMPLSUFFIX')))
+ if len(target) < 3:
+ # third target is moc file
+ target.append(adjustixes(bs,
+ env.subst('$QT_MOCHPREFIX'),
+ env.subst('$QT_MOCHSUFFIX')))
+ return target, source
+
+def uicScannerFunc(node, env, path):
+ lookout = []
+ lookout.extend(env['CPPPATH'])
+ lookout.append(str(node.rfile().dir))
+ includes = re.findall("<include.*?>(.*?)</include>", node.get_text_contents())
+ result = []
+ for incFile in includes:
+ dep = env.FindFile(incFile,lookout)
+ if dep:
+ result.append(dep)
+ return result
+
+uicScanner = SCons.Scanner.Base(uicScannerFunc,
+ name = "UicScanner",
+ node_class = SCons.Node.FS.File,
+ node_factory = SCons.Node.FS.File,
+ recursive = 0)
+
+def generate(env):
+ """Add Builders and construction variables for qt to an Environment."""
+ CLVar = SCons.Util.CLVar
+ Action = SCons.Action.Action
+ Builder = SCons.Builder.Builder
+
+ env.SetDefault(QTDIR = _detect(env),
+ QT_BINPATH = os.path.join('$QTDIR', 'bin'),
+ QT_CPPPATH = os.path.join('$QTDIR', 'include'),
+ QT_LIBPATH = os.path.join('$QTDIR', 'lib'),
+ QT_MOC = os.path.join('$QT_BINPATH','moc'),
+ QT_UIC = os.path.join('$QT_BINPATH','uic'),
+ QT_LIB = 'qt', # may be set to qt-mt
+
+ QT_AUTOSCAN = 1, # scan for moc'able sources
+
+ # Some QT specific flags. I don't expect someone wants to
+ # manipulate those ...
+ QT_UICIMPLFLAGS = CLVar(''),
+ QT_UICDECLFLAGS = CLVar(''),
+ QT_MOCFROMHFLAGS = CLVar(''),
+ QT_MOCFROMCXXFLAGS = CLVar('-i'),
+
+ # suffixes/prefixes for the headers / sources to generate
+ QT_UICDECLPREFIX = '',
+ QT_UICDECLSUFFIX = '.h',
+ QT_UICIMPLPREFIX = 'uic_',
+ QT_UICIMPLSUFFIX = '$CXXFILESUFFIX',
+ QT_MOCHPREFIX = 'moc_',
+ QT_MOCHSUFFIX = '$CXXFILESUFFIX',
+ QT_MOCCXXPREFIX = '',
+ QT_MOCCXXSUFFIX = '.moc',
+ QT_UISUFFIX = '.ui',
+
+ # Commands for the qt support ...
+ # command to generate header, implementation and moc-file
+ # from a .ui file
+ QT_UICCOM = [
+ CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'),
+ CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} '
+ '-o ${TARGETS[1]} $SOURCE'),
+ CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')],
+ # command to generate meta object information for a class
+ # declarated in a header
+ QT_MOCFROMHCOM = (
+ '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'),
+ # command to generate meta object information for a class
+ # declarated in a cpp file
+ QT_MOCFROMCXXCOM = [
+ CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
+ Action(checkMocIncluded,None)])
+
+ # ... and the corresponding builders
+ uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'),
+ emitter=uicEmitter,
+ src_suffix='$QT_UISUFFIX',
+ suffix='$QT_UICDECLSUFFIX',
+ prefix='$QT_UICDECLPREFIX',
+ source_scanner=uicScanner)
+ mocBld = Builder(action={}, prefix={}, suffix={})
+ for h in header_extensions:
+ act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR')
+ mocBld.add_action(h, act)
+ mocBld.prefix[h] = '$QT_MOCHPREFIX'
+ mocBld.suffix[h] = '$QT_MOCHSUFFIX'
+ for cxx in cxx_suffixes:
+ act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR')
+ mocBld.add_action(cxx, act)
+ mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX'
+ mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX'
+
+ # register the builders
+ env['BUILDERS']['Uic'] = uicBld
+ env['BUILDERS']['Moc'] = mocBld
+ static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+ static_obj.add_src_builder('Uic')
+ shared_obj.add_src_builder('Uic')
+
+ # We use the emitters of Program / StaticLibrary / SharedLibrary
+ # to scan for moc'able files
+ # We can't refer to the builders directly, we have to fetch them
+ # as Environment attributes because that sets them up to be called
+ # correctly later by our emitter.
+ env.AppendUnique(PROGEMITTER =[AutomocStatic],
+ SHLIBEMITTER=[AutomocShared],
+ LIBEMITTER =[AutomocStatic],
+ # Of course, we need to link against the qt libraries
+ CPPPATH=["$QT_CPPPATH"],
+ LIBPATH=["$QT_LIBPATH"],
+ LIBS=['$QT_LIB'])
+
+def exists(env):
+ return _detect(env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/qt.xml b/src/engine/SCons/Tool/qt.xml
new file mode 100644
index 0000000..3e41c68
--- /dev/null
+++ b/src/engine/SCons/Tool/qt.xml
@@ -0,0 +1,314 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="qt">
+<summary>
+Sets construction variables for building Qt applications.
+</summary>
+<sets>
+QTDIR
+QT_BINPATH
+QT_CPPPATH
+QT_LIBPATH
+QT_MOC
+QT_UIC
+QT_LIB
+QT_AUTOSCAN
+QT_UICIMPLFLAGS
+QT_UICDECLFLAGS
+QT_MOCFROMHFLAGS
+QT_MOCFROMCXXFLAGS
+QT_UICDECLPREFIX
+QT_UICDECLSUFFIX
+QT_UICIMPLPREFIX
+QT_UICIMPLSUFFIX
+QT_MOCHPREFIX
+QT_MOCHSUFFIX
+QT_MOCCXXPREFIX
+QT_MOCCXXSUFFIX
+QT_UISUFFIX
+QT_UICCOM
+QT_MOCFROMHCOM
+QT_MOCFROMCXXCOM
+</sets>
+<uses>
+</uses>
+</tool>
+
+<builder name="Moc">
+<summary>
+Builds an output file from a moc input file. Moc input files are either
+header files or cxx files. This builder is only available after using the
+tool 'qt'. See the &cv-link-QTDIR; variable for more information.
+Example:
+
+<example>
+env.Moc('foo.h') # generates moc_foo.cc
+env.Moc('foo.cpp') # generates foo.moc
+</example>
+</summary>
+</builder>
+
+<builder name="Uic">
+<summary>
+Builds a header file, an implementation file and a moc file from an ui file.
+and returns the corresponding nodes in the above order.
+This builder is only available after using the tool 'qt'. Note: you can
+specify <filename>.ui</filename> files directly as source
+files to the &b-Program;,
+&b-Library; and &b-SharedLibrary; builders
+without using this builder. Using this builder lets you override the standard
+naming conventions (be careful: prefixes are always prepended to names of
+built files; if you don't want prefixes, you may set them to ``).
+See the &cv-link-QTDIR; variable for more information.
+Example:
+
+<example>
+env.Uic('foo.ui') # -> ['foo.h', 'uic_foo.cc', 'moc_foo.cc']
+env.Uic(target = Split('include/foo.h gen/uicfoo.cc gen/mocfoo.cc'),
+ source = 'foo.ui') # -> ['include/foo.h', 'gen/uicfoo.cc', 'gen/mocfoo.cc']
+</example>
+</summary>
+</builder>
+
+<cvar name="QTDIR">
+<summary>
+The qt tool tries to take this from os.environ.
+It also initializes all QT_*
+construction variables listed below.
+(Note that all paths are constructed
+with python's os.path.join() method,
+but are listed here with the '/' separator
+for easier reading.)
+In addition, the construction environment
+variables &cv-link-CPPPATH;,
+&cv-link-LIBPATH; and
+&cv-link-LIBS; may be modified
+and the variables
+PROGEMITTER, SHLIBEMITTER and LIBEMITTER
+are modified. Because the build-performance is affected when using this tool,
+you have to explicitly specify it at Environment creation:
+
+<example>
+Environment(tools=['default','qt'])
+</example>
+
+The qt tool supports the following operations:
+
+<emphasis Role="strong">Automatic moc file generation from header files.</emphasis>
+You do not have to specify moc files explicitly, the tool does it for you.
+However, there are a few preconditions to do so: Your header file must have
+the same filebase as your implementation file and must stay in the same
+directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You
+can turn off automatic moc file generation by setting QT_AUTOSCAN to 0.
+See also the corresponding builder method
+.B Moc()
+
+<emphasis Role="strong">Automatic moc file generation from cxx files.</emphasis>
+As stated in the qt documentation, include the moc file at the end of
+the cxx file. Note that you have to include the file, which is generated
+by the transformation ${QT_MOCCXXPREFIX}&lt;basename&gt;${QT_MOCCXXSUFFIX}, by default
+&lt;basename&gt;.moc. A warning is generated after building the moc file, if you
+do not include the correct file. If you are using VariantDir, you may
+need to specify duplicate=1. You can turn off automatic moc file generation
+by setting QT_AUTOSCAN to 0. See also the corresponding
+&b-Moc;
+builder method.
+
+<emphasis Role="strong">Automatic handling of .ui files.</emphasis>
+The implementation files generated from .ui files are handled much the same
+as yacc or lex files. Each .ui file given as a source of Program, Library or
+SharedLibrary will generate three files, the declaration file, the
+implementation file and a moc file. Because there are also generated headers,
+you may need to specify duplicate=1 in calls to VariantDir.
+See also the corresponding
+&b-Uic;
+builder method.
+</summary>
+</cvar>
+
+<cvar name="QT_AUTOSCAN">
+<summary>
+Turn off scanning for mocable files. Use the Moc Builder to explicitely
+specify files to run moc on.
+</summary>
+</cvar>
+
+<cvar name="QT_BINPATH">
+<summary>
+The path where the qt binaries are installed.
+The default value is '&cv-link-QTDIR;/bin'.
+</summary>
+</cvar>
+
+<cvar name="QT_CPPPATH">
+<summary>
+The path where the qt header files are installed.
+The default value is '&cv-link-QTDIR;/include'.
+Note: If you set this variable to None,
+the tool won't change the &cv-link-CPPPATH;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="QT_DEBUG">
+<summary>
+Prints lots of debugging information while scanning for moc files.
+</summary>
+</cvar>
+
+<cvar name="QT_LIB">
+<summary>
+Default value is 'qt'. You may want to set this to 'qt-mt'. Note: If you set
+this variable to None, the tool won't change the &cv-link-LIBS; variable.
+</summary>
+</cvar>
+
+<cvar name="QT_LIBPATH">
+<summary>
+The path where the qt libraries are installed.
+The default value is '&cv-link-QTDIR;/lib'.
+Note: If you set this variable to None,
+the tool won't change the &cv-link-LIBPATH;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="QT_MOC">
+<summary>
+Default value is '&cv-link-QT_BINPATH;/moc'.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCCXXPREFIX">
+<summary>
+Default value is ''. Prefix for moc output files, when source is a cxx file.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCCXXSUFFIX">
+<summary>
+Default value is '.moc'. Suffix for moc output files, when source is a cxx
+file.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCFROMCXXFLAGS">
+<summary>
+Default value is '-i'. These flags are passed to moc, when moccing a
+C++ file.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCFROMCXXCOM">
+<summary>
+Command to generate a moc file from a cpp file.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCFROMCXXCOMSTR">
+<summary>
+The string displayed when generating a moc file from a cpp file.
+If this is not set, then &cv-link-QT_MOCFROMCXXCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCFROMHCOM">
+<summary>
+Command to generate a moc file from a header.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCFROMHCOMSTR">
+<summary>
+The string displayed when generating a moc file from a cpp file.
+If this is not set, then &cv-link-QT_MOCFROMHCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCFROMHFLAGS">
+<summary>
+Default value is ''. These flags are passed to moc, when moccing a header
+file.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCHPREFIX">
+<summary>
+Default value is 'moc_'. Prefix for moc output files, when source is a header.
+</summary>
+</cvar>
+
+<cvar name="QT_MOCHSUFFIX">
+<summary>
+Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for moc output files, when source is
+a header.
+</summary>
+</cvar>
+
+<cvar name="QT_UIC">
+<summary>
+Default value is '&cv-link-QT_BINPATH;/uic'.
+</summary>
+</cvar>
+
+<cvar name="QT_UICCOM">
+<summary>
+Command to generate header files from .ui files.
+</summary>
+</cvar>
+
+<cvar name="QT_UICCOMSTR">
+<summary>
+The string displayed when generating header files from .ui files.
+If this is not set, then &cv-link-QT_UICCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="QT_UICDECLFLAGS">
+<summary>
+Default value is ''. These flags are passed to uic, when creating a a h
+file from a .ui file.
+</summary>
+</cvar>
+
+<cvar name="QT_UICDECLPREFIX">
+<summary>
+Default value is ''. Prefix for uic generated header files.
+</summary>
+</cvar>
+
+<cvar name="QT_UICDECLSUFFIX">
+<summary>
+Default value is '.h'. Suffix for uic generated header files.
+</summary>
+</cvar>
+
+<cvar name="QT_UICIMPLFLAGS">
+<summary>
+Default value is ''. These flags are passed to uic, when creating a cxx
+file from a .ui file.
+</summary>
+</cvar>
+
+<cvar name="QT_UICIMPLPREFIX">
+<summary>
+Default value is 'uic_'. Prefix for uic generated implementation files.
+</summary>
+</cvar>
+
+<cvar name="QT_UICIMPLSUFFIX">
+<summary>
+Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for uic generated implementation
+files.
+</summary>
+</cvar>
+
+<cvar name="QT_UISUFFIX">
+<summary>
+Default value is '.ui'. Suffix of designer input files.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/rmic.py b/src/engine/SCons/Tool/rmic.py
new file mode 100644
index 0000000..f41b38c
--- /dev/null
+++ b/src/engine/SCons/Tool/rmic.py
@@ -0,0 +1,121 @@
+"""SCons.Tool.rmic
+
+Tool-specific initialization for rmic.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/rmic.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+
+import SCons.Action
+import SCons.Builder
+import SCons.Node.FS
+import SCons.Util
+
+def emit_rmic_classes(target, source, env):
+ """Create and return lists of Java RMI stub and skeleton
+ class files to be created from a set of class files.
+ """
+ class_suffix = env.get('JAVACLASSSUFFIX', '.class')
+ classdir = env.get('JAVACLASSDIR')
+
+ if not classdir:
+ try:
+ s = source[0]
+ except IndexError:
+ classdir = '.'
+ else:
+ try:
+ classdir = s.attributes.java_classdir
+ except AttributeError:
+ classdir = '.'
+ classdir = env.Dir(classdir).rdir()
+ if str(classdir) == '.':
+ c_ = None
+ else:
+ c_ = str(classdir) + os.sep
+
+ slist = []
+ for src in source:
+ try:
+ classname = src.attributes.java_classname
+ except AttributeError:
+ classname = str(src)
+ if c_ and classname[:len(c_)] == c_:
+ classname = classname[len(c_):]
+ if class_suffix and classname[:-len(class_suffix)] == class_suffix:
+ classname = classname[-len(class_suffix):]
+ s = src.rfile()
+ s.attributes.java_classdir = classdir
+ s.attributes.java_classname = classname
+ slist.append(s)
+
+ stub_suffixes = ['_Stub']
+ if env.get('JAVAVERSION') == '1.4':
+ stub_suffixes.append('_Skel')
+
+ tlist = []
+ for s in source:
+ for suff in stub_suffixes:
+ fname = string.replace(s.attributes.java_classname, '.', os.sep) + \
+ suff + class_suffix
+ t = target[0].File(fname)
+ t.attributes.java_lookupdir = target[0]
+ tlist.append(t)
+
+ return tlist, source
+
+RMICAction = SCons.Action.Action('$RMICCOM', '$RMICCOMSTR')
+
+RMICBuilder = SCons.Builder.Builder(action = RMICAction,
+ emitter = emit_rmic_classes,
+ src_suffix = '$JAVACLASSSUFFIX',
+ target_factory = SCons.Node.FS.Dir,
+ source_factory = SCons.Node.FS.File)
+
+def generate(env):
+ """Add Builders and construction variables for rmic to an Environment."""
+ env['BUILDERS']['RMIC'] = RMICBuilder
+
+ env['RMIC'] = 'rmic'
+ env['RMICFLAGS'] = SCons.Util.CLVar('')
+ env['RMICCOM'] = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}'
+ env['JAVACLASSSUFFIX'] = '.class'
+
+def exists(env):
+ return env.Detect('rmic')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/rmic.xml b/src/engine/SCons/Tool/rmic.xml
new file mode 100644
index 0000000..eacc6bf
--- /dev/null
+++ b/src/engine/SCons/Tool/rmic.xml
@@ -0,0 +1,93 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="rmic">
+<summary>
+Sets construction variables for the &rmic; utility.
+</summary>
+<sets>
+RMIC
+RMICFLAGS
+RMICCOM
+JAVACLASSSUFFIX
+</sets>
+<uses>
+RMICCOMSTR
+</uses>
+</tool>
+
+<builder name="RMIC">
+<summary>
+Builds stub and skeleton class files
+for remote objects
+from Java <filename>.class</filename> files.
+The target is a directory
+relative to which the stub
+and skeleton class files will be written.
+The source can be the names of <filename>.class</filename> files,
+or the objects return from the
+&b-Java;
+builder method.
+
+If the construction variable
+&cv-link-JAVACLASSDIR;
+is set, either in the environment
+or in the call to the
+&b-RMIC;
+builder method itself,
+then the value of the variable
+will be stripped from the
+beginning of any <filename>.class </filename>
+file names.
+
+<example>
+classes = env.Java(target = 'classdir', source = 'src')
+env.RMIC(target = 'outdir1', source = classes)
+
+env.RMIC(target = 'outdir2',
+ source = ['package/foo.class', 'package/bar.class'])
+
+env.RMIC(target = 'outdir3',
+ source = ['classes/foo.class', 'classes/bar.class'],
+ JAVACLASSDIR = 'classes')
+</example>
+</summary>
+</builder>
+
+<cvar name="RMIC">
+<summary>
+The Java RMI stub compiler.
+</summary>
+</cvar>
+
+<cvar name="RMICCOM">
+<summary>
+The command line used to compile stub
+and skeleton class files
+from Java classes that contain RMI implementations.
+Any options specified in the &cv-link-RMICFLAGS; construction variable
+are included on this command line.
+</summary>
+</cvar>
+
+<cvar name="RMICCOMSTR">
+<summary>
+The string displayed when compiling
+stub and skeleton class files
+from Java classes that contain RMI implementations.
+If this is not set, then &cv-link-RMICCOM; (the command line) is displayed.
+
+<example>
+env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="RMICFLAGS">
+<summary>
+General options passed to the Java RMI stub compiler.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/rpcgen.py b/src/engine/SCons/Tool/rpcgen.py
new file mode 100644
index 0000000..86feac9
--- /dev/null
+++ b/src/engine/SCons/Tool/rpcgen.py
@@ -0,0 +1,70 @@
+"""SCons.Tool.rpcgen
+
+Tool-specific initialization for RPCGEN tools.
+
+Three normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/rpcgen.py 4577 2009/12/27 19:44:43 scons"
+
+from SCons.Builder import Builder
+import SCons.Util
+
+cmd = "cd ${SOURCE.dir} && $RPCGEN -%s $RPCGENFLAGS %s -o ${TARGET.abspath} ${SOURCE.file}"
+
+rpcgen_client = cmd % ('l', '$RPCGENCLIENTFLAGS')
+rpcgen_header = cmd % ('h', '$RPCGENHEADERFLAGS')
+rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS')
+rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS')
+
+def generate(env):
+ "Add RPCGEN Builders and construction variables for an Environment."
+
+ client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x')
+ header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x')
+ service = Builder(action=rpcgen_service, suffix='_svc.c', src_suffix='.x')
+ xdr = Builder(action=rpcgen_xdr, suffix='_xdr.c', src_suffix='.x')
+ env.Append(BUILDERS={'RPCGenClient' : client,
+ 'RPCGenHeader' : header,
+ 'RPCGenService' : service,
+ 'RPCGenXDR' : xdr})
+ env['RPCGEN'] = 'rpcgen'
+ env['RPCGENFLAGS'] = SCons.Util.CLVar('')
+ env['RPCGENCLIENTFLAGS'] = SCons.Util.CLVar('')
+ env['RPCGENHEADERFLAGS'] = SCons.Util.CLVar('')
+ env['RPCGENSERVICEFLAGS'] = SCons.Util.CLVar('')
+ env['RPCGENXDRFLAGS'] = SCons.Util.CLVar('')
+
+def exists(env):
+ return env.Detect('rpcgen')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/rpcgen.xml b/src/engine/SCons/Tool/rpcgen.xml
new file mode 100644
index 0000000..60848be
--- /dev/null
+++ b/src/engine/SCons/Tool/rpcgen.xml
@@ -0,0 +1,137 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="rpcgen">
+<summary>
+Sets construction variables for building with RPCGEN.
+</summary>
+<sets>
+RPCGEN
+RPCGENFLAGS
+RPCGENCLIENTFLAGS
+RPCGENHEADERFLAGS
+RPCGENSERVICEFLAGS
+RPCGENXDRFLAGS
+</sets>
+<uses>
+</uses>
+</tool>
+
+<builder name="RPCGenClient">
+<summary>
+Generates an RPC client stub (<filename>_clnt.c</filename>) file
+from a specified RPC (<filename>.x</filename>) source file.
+Because rpcgen only builds output files
+in the local directory,
+the command will be executed
+in the source file's directory by default.
+
+<example>
+# Builds src/rpcif_clnt.c
+env.RPCGenClient('src/rpcif.x')
+</example>
+</summary>
+</builder>
+
+<builder name="RPCGenHeader">
+<summary>
+Generates an RPC header (<filename>.h</filename>) file
+from a specified RPC (<filename>.x</filename>) source file.
+Because rpcgen only builds output files
+in the local directory,
+the command will be executed
+in the source file's directory by default.
+
+<example>
+# Builds src/rpcif.h
+env.RPCGenHeader('src/rpcif.x')
+</example>
+</summary>
+</builder>
+
+<builder name="RPCGenService">
+<summary>
+Generates an RPC server-skeleton (<filename>_svc.c</filename>) file
+from a specified RPC (<filename>.x</filename>) source file.
+Because rpcgen only builds output files
+in the local directory,
+the command will be executed
+in the source file's directory by default.
+
+<example>
+# Builds src/rpcif_svc.c
+env.RPCGenClient('src/rpcif.x')
+</example>
+</summary>
+</builder>
+
+<builder name="RPCGenXDR">
+<summary>
+Generates an RPC XDR routine (<filename>_xdr.c</filename>) file
+from a specified RPC (<filename>.x</filename>) source file.
+Because rpcgen only builds output files
+in the local directory,
+the command will be executed
+in the source file's directory by default.
+
+<example>
+# Builds src/rpcif_xdr.c
+env.RPCGenClient('src/rpcif.x')
+</example>
+</summary>
+</builder>
+
+<cvar name="RPCGEN">
+<summary>
+The RPC protocol compiler.
+</summary>
+</cvar>
+
+<cvar name="RPCGENCLIENTFLAGS">
+<summary>
+Options passed to the RPC protocol compiler
+when generating client side stubs.
+These are in addition to any flags specified in the
+&cv-link-RPCGENFLAGS;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="RPCGENFLAGS">
+<summary>
+General options passed to the RPC protocol compiler.
+</summary>
+</cvar>
+
+<cvar name="RPCGENHEADERFLAGS">
+<summary>
+Options passed to the RPC protocol compiler
+when generating a header file.
+These are in addition to any flags specified in the
+&cv-link-RPCGENFLAGS;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="RPCGENSERVICEFLAGS">
+<summary>
+Options passed to the RPC protocol compiler
+when generating server side stubs.
+These are in addition to any flags specified in the
+&cv-link-RPCGENFLAGS;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="RPCGENXDRFLAGS">
+<summary>
+Options passed to the RPC protocol compiler
+when generating XDR routines.
+These are in addition to any flags specified in the
+&cv-link-RPCGENFLAGS;
+construction variable.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/rpm.py b/src/engine/SCons/Tool/rpm.py
new file mode 100644
index 0000000..19edd56
--- /dev/null
+++ b/src/engine/SCons/Tool/rpm.py
@@ -0,0 +1,132 @@
+"""SCons.Tool.rpm
+
+Tool-specific initialization for rpm.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+The rpm tool calls the rpmbuild command. The first and only argument should a
+tar.gz consisting of the source file and a specfile.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/rpm.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import re
+import shutil
+import subprocess
+
+import SCons.Builder
+import SCons.Node.FS
+import SCons.Util
+import SCons.Action
+import SCons.Defaults
+
+def get_cmd(source, env):
+ tar_file_with_included_specfile = source
+ if SCons.Util.is_List(source):
+ tar_file_with_included_specfile = source[0]
+ return "%s %s %s"%(env['RPM'], env['RPMFLAGS'],
+ tar_file_with_included_specfile.abspath )
+
+def build_rpm(target, source, env):
+ # create a temporary rpm build root.
+ tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' )
+ if os.path.exists(tmpdir):
+ shutil.rmtree(tmpdir)
+
+ # now create the mandatory rpm directory structure.
+ for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']:
+ os.makedirs( os.path.join( tmpdir, d ) )
+
+ # set the topdir as an rpmflag.
+ env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir )
+
+ # now call rpmbuild to create the rpm package.
+ handle = subprocess.Popen(get_cmd(source, env),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ shell=True)
+ output = handle.stdout.read()
+ status = handle.wait()
+
+ if status:
+ raise SCons.Errors.BuildError( node=target[0],
+ errstr=output,
+ filename=str(target[0]) )
+ else:
+ # XXX: assume that LC_ALL=c is set while running rpmbuild
+ output_files = re.compile( 'Wrote: (.*)' ).findall( output )
+
+ for output, input in zip( output_files, target ):
+ rpm_output = os.path.basename(output)
+ expected = os.path.basename(input.get_path())
+
+ assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected)
+ shutil.copy( output, input.abspath )
+
+
+ # cleanup before leaving.
+ shutil.rmtree(tmpdir)
+
+ return status
+
+def string_rpm(target, source, env):
+ try:
+ return env['RPMCOMSTR']
+ except KeyError:
+ return get_cmd(source, env)
+
+rpmAction = SCons.Action.Action(build_rpm, string_rpm)
+
+RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'),
+ source_scanner = SCons.Defaults.DirScanner,
+ suffix = '$RPMSUFFIX')
+
+
+
+def generate(env):
+ """Add Builders and construction variables for rpm to an Environment."""
+ try:
+ bld = env['BUILDERS']['Rpm']
+ except KeyError:
+ bld = RpmBuilder
+ env['BUILDERS']['Rpm'] = bld
+
+ env.SetDefault(RPM = 'LC_ALL=c rpmbuild')
+ env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta'))
+ env.SetDefault(RPMCOM = rpmAction)
+ env.SetDefault(RPMSUFFIX = '.rpm')
+
+def exists(env):
+ return env.Detect('rpmbuild')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sgiar.py b/src/engine/SCons/Tool/sgiar.py
new file mode 100644
index 0000000..278b4c6
--- /dev/null
+++ b/src/engine/SCons/Tool/sgiar.py
@@ -0,0 +1,68 @@
+"""SCons.Tool.sgiar
+
+Tool-specific initialization for SGI ar (library archive). If CC
+exists, static libraries should be built with it, so the prelinker has
+a chance to resolve C++ template instantiations.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sgiar.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+def generate(env):
+ """Add Builders and construction variables for ar to an Environment."""
+ SCons.Tool.createStaticLibBuilder(env)
+
+ if env.Detect('CC'):
+ env['AR'] = 'CC'
+ env['ARFLAGS'] = SCons.Util.CLVar('-ar')
+ env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES'
+ else:
+ env['AR'] = 'ar'
+ env['ARFLAGS'] = SCons.Util.CLVar('r')
+ env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES'
+
+ env['SHLINK'] = '$LINK'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
+ env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['LIBPREFIX'] = 'lib'
+ env['LIBSUFFIX'] = '.a'
+
+def exists(env):
+ return env.Detect('CC') or env.Detect('ar')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sgiar.xml b/src/engine/SCons/Tool/sgiar.xml
new file mode 100644
index 0000000..38be862
--- /dev/null
+++ b/src/engine/SCons/Tool/sgiar.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sgiar">
+<summary>
+Sets construction variables for the SGI library archiver.
+</summary>
+<sets>
+AR
+ARFLAGS
+ARCOMSTR
+SHLINK
+SHLINKFLAGS
+LIBPREFIX
+LIBSUFFIX
+</sets>
+<uses>
+ARCOMSTR
+SHLINKCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/sgic++.py b/src/engine/SCons/Tool/sgic++.py
new file mode 100644
index 0000000..8aece3a
--- /dev/null
+++ b/src/engine/SCons/Tool/sgic++.py
@@ -0,0 +1,58 @@
+"""SCons.Tool.sgic++
+
+Tool-specific initialization for MIPSpro C++ on SGI.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sgic++.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+def generate(env):
+ """Add Builders and construction variables for SGI MIPS C++ to an Environment."""
+
+ cplusplus.generate(env)
+
+ env['CXX'] = 'CC'
+ env['CXXFLAGS'] = SCons.Util.CLVar('-LANG:std')
+ env['SHCXX'] = '$CXX'
+ env['SHOBJSUFFIX'] = '.o'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
+
+def exists(env):
+ return env.Detect('CC')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sgic++.xml b/src/engine/SCons/Tool/sgic++.xml
new file mode 100644
index 0000000..918988b
--- /dev/null
+++ b/src/engine/SCons/Tool/sgic++.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sgic++">
+<summary>
+Sets construction variables for the SGI C++ compiler.
+</summary>
+<sets>
+CXX
+CXXFLAGS
+SHCXX
+SHOBJSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/sgicc.py b/src/engine/SCons/Tool/sgicc.py
new file mode 100644
index 0000000..ac311db
--- /dev/null
+++ b/src/engine/SCons/Tool/sgicc.py
@@ -0,0 +1,53 @@
+"""SCons.Tool.sgicc
+
+Tool-specific initialization for MIPSPro cc on SGI.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sgicc.py 4577 2009/12/27 19:44:43 scons"
+
+import cc
+
+def generate(env):
+ """Add Builders and construction variables for gcc to an Environment."""
+ cc.generate(env)
+
+ env['CXX'] = 'CC'
+ env['SHOBJSUFFIX'] = '.o'
+ env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
+
+def exists(env):
+ return env.Detect('cc')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sgicc.xml b/src/engine/SCons/Tool/sgicc.xml
new file mode 100644
index 0000000..668ecd1
--- /dev/null
+++ b/src/engine/SCons/Tool/sgicc.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sgicc">
+<summary>
+Sets construction variables for the SGI C compiler.
+</summary>
+<sets>
+CXX
+SHOBJSUFFIX
+<!--STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME-->
+</sets>
+<uses>
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/sgilink.py b/src/engine/SCons/Tool/sgilink.py
new file mode 100644
index 0000000..3138782
--- /dev/null
+++ b/src/engine/SCons/Tool/sgilink.py
@@ -0,0 +1,63 @@
+"""SCons.Tool.sgilink
+
+Tool-specific initialization for the SGI MIPSPro linker on SGI.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sgilink.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+import link
+
+linkers = ['CC', 'cc']
+
+def generate(env):
+ """Add Builders and construction variables for MIPSPro to an Environment."""
+ link.generate(env)
+
+ env['LINK'] = env.Detect(linkers) or 'cc'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
+
+ # __RPATH is set to $_RPATH in the platform specification if that
+ # platform supports it.
+ env.Append(LINKFLAGS=['$__RPATH'])
+ env['RPATHPREFIX'] = '-rpath '
+ env['RPATHSUFFIX'] = ''
+ env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}'
+
+def exists(env):
+ return env.Detect(linkers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sgilink.xml b/src/engine/SCons/Tool/sgilink.xml
new file mode 100644
index 0000000..2618133
--- /dev/null
+++ b/src/engine/SCons/Tool/sgilink.xml
@@ -0,0 +1,17 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sgilink">
+<summary>
+Sets construction variables for the SGI linker.
+</summary>
+<sets>
+LINK
+SHLINKFLAGS
+RPATHPREFIX
+RPATHSUFFIX
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/sunar.py b/src/engine/SCons/Tool/sunar.py
new file mode 100644
index 0000000..afb1add
--- /dev/null
+++ b/src/engine/SCons/Tool/sunar.py
@@ -0,0 +1,67 @@
+"""engine.SCons.Tool.sunar
+
+Tool-specific initialization for Solaris (Forte) ar (library archive). If CC
+exists, static libraries should be built with it, so that template
+instantians can be resolved.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sunar.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+def generate(env):
+ """Add Builders and construction variables for ar to an Environment."""
+ SCons.Tool.createStaticLibBuilder(env)
+
+ if env.Detect('CC'):
+ env['AR'] = 'CC'
+ env['ARFLAGS'] = SCons.Util.CLVar('-xar')
+ env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES'
+ else:
+ env['AR'] = 'ar'
+ env['ARFLAGS'] = SCons.Util.CLVar('r')
+ env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES'
+
+ env['SHLINK'] = '$LINK'
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G')
+ env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['LIBPREFIX'] = 'lib'
+ env['LIBSUFFIX'] = '.a'
+
+def exists(env):
+ return env.Detect('CC') or env.Detect('ar')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sunar.xml b/src/engine/SCons/Tool/sunar.xml
new file mode 100644
index 0000000..ab40f8f
--- /dev/null
+++ b/src/engine/SCons/Tool/sunar.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sunar">
+<summary>
+Sets construction variables for the Sun library archiver.
+</summary>
+<sets>
+AR
+ARFLAGS
+ARCOM
+SHLINK
+SHLINKFLAGS
+SHLINKCOM
+LIBPREFIX
+LIBSUFFIX
+</sets>
+<uses>
+ARCOMSTR
+SHLINKCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/sunc++.py b/src/engine/SCons/Tool/sunc++.py
new file mode 100644
index 0000000..dcd88ef
--- /dev/null
+++ b/src/engine/SCons/Tool/sunc++.py
@@ -0,0 +1,142 @@
+"""SCons.Tool.sunc++
+
+Tool-specific initialization for C++ on SunOS / Solaris.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sunc++.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons
+
+import os
+import re
+import subprocess
+
+cplusplus = __import__('c++', globals(), locals(), [])
+
+package_info = {}
+
+def get_package_info(package_name, pkginfo, pkgchk):
+ try:
+ return package_info[package_name]
+ except KeyError:
+ version = None
+ pathname = None
+ try:
+ sadm_contents = open('/var/sadm/install/contents', 'r').read()
+ except EnvironmentError:
+ pass
+ else:
+ sadm_re = re.compile('^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M)
+ sadm_match = sadm_re.search(sadm_contents)
+ if sadm_match:
+ pathname = os.path.dirname(sadm_match.group(1))
+
+ try:
+ p = subprocess.Popen([pkginfo, '-l', package_name],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ except EnvironmentError:
+ pass
+ else:
+ pkginfo_contents = p.communicate()[0]
+ version_re = re.compile('^ *VERSION:\s*(.*)$', re.M)
+ version_match = version_re.search(pkginfo_contents)
+ if version_match:
+ version = version_match.group(1)
+
+ if pathname is None:
+ try:
+ p = subprocess.Popen([pkgchk, '-l', package_name],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ except EnvironmentError:
+ pass
+ else:
+ pkgchk_contents = p.communicate()[0]
+ pathname_re = re.compile(r'^Pathname:\s*(.*/bin/CC)$', re.M)
+ pathname_match = pathname_re.search(pkgchk_contents)
+ if pathname_match:
+ pathname = os.path.dirname(pathname_match.group(1))
+
+ package_info[package_name] = (pathname, version)
+ return package_info[package_name]
+
+# use the package installer tool lslpp to figure out where cppc and what
+# version of it is installed
+def get_cppc(env):
+ cxx = env.subst('$CXX')
+ if cxx:
+ cppcPath = os.path.dirname(cxx)
+ else:
+ cppcPath = None
+
+ cppcVersion = None
+
+ pkginfo = env.subst('$PKGINFO')
+ pkgchk = env.subst('$PKGCHK')
+
+ for package in ['SPROcpl']:
+ path, version = get_package_info(package, pkginfo, pkgchk)
+ if path and version:
+ cppcPath, cppcVersion = path, version
+ break
+
+ return (cppcPath, 'CC', 'CC', cppcVersion)
+
+def generate(env):
+ """Add Builders and construction variables for SunPRO C++."""
+ path, cxx, shcxx, version = get_cppc(env)
+ if path:
+ cxx = os.path.join(path, cxx)
+ shcxx = os.path.join(path, shcxx)
+
+ cplusplus.generate(env)
+
+ env['CXX'] = cxx
+ env['SHCXX'] = shcxx
+ env['CXXVERSION'] = version
+ env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -KPIC')
+ env['SHOBJPREFIX'] = 'so_'
+ env['SHOBJSUFFIX'] = '.o'
+
+def exists(env):
+ path, cxx, shcxx, version = get_cppc(env)
+ if path and cxx:
+ cppc = os.path.join(path, cxx)
+ if os.path.exists(cppc):
+ return cppc
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sunc++.xml b/src/engine/SCons/Tool/sunc++.xml
new file mode 100644
index 0000000..e6c6dbf
--- /dev/null
+++ b/src/engine/SCons/Tool/sunc++.xml
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sunc++">
+<summary>
+Sets construction variables for the Sun C++ compiler.
+</summary>
+<sets>
+CXX
+SHCXX
+CXXVERSION
+SHCXXFLAGS
+SHOBJPREFIX
+SHOBJSUFFIX
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/suncc.py b/src/engine/SCons/Tool/suncc.py
new file mode 100644
index 0000000..a37af82
--- /dev/null
+++ b/src/engine/SCons/Tool/suncc.py
@@ -0,0 +1,58 @@
+"""SCons.Tool.suncc
+
+Tool-specific initialization for Sun Solaris (Forte) CC and cc.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/suncc.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+import cc
+
+def generate(env):
+ """
+ Add Builders and construction variables for Forte C and C++ compilers
+ to an Environment.
+ """
+ cc.generate(env)
+
+ env['CXX'] = 'CC'
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -KPIC')
+ env['SHOBJPREFIX'] = 'so_'
+ env['SHOBJSUFFIX'] = '.o'
+
+def exists(env):
+ return env.Detect('CC')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/suncc.xml b/src/engine/SCons/Tool/suncc.xml
new file mode 100644
index 0000000..0511180
--- /dev/null
+++ b/src/engine/SCons/Tool/suncc.xml
@@ -0,0 +1,17 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="suncc">
+<summary>
+Sets construction variables for the Sun C compiler.
+</summary>
+<sets>
+CXX
+SHCCFLAGS
+SHOBJPREFIX
+SHOBJSUFFIX
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/sunf77.py b/src/engine/SCons/Tool/sunf77.py
new file mode 100644
index 0000000..66b98ad
--- /dev/null
+++ b/src/engine/SCons/Tool/sunf77.py
@@ -0,0 +1,63 @@
+"""SCons.Tool.sunf77
+
+Tool-specific initialization for sunf77, the Sun Studio F77 compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sunf77.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+from FortranCommon import add_all_to_env
+
+compilers = ['sunf77', 'f77']
+
+def generate(env):
+ """Add Builders and construction variables for sunf77 to an Environment."""
+ add_all_to_env(env)
+
+ fcomp = env.Detect(compilers) or 'f77'
+ env['FORTRAN'] = fcomp
+ env['F77'] = fcomp
+
+ env['SHFORTRAN'] = '$FORTRAN'
+ env['SHF77'] = '$F77'
+
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC')
+ env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -KPIC')
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sunf77.xml b/src/engine/SCons/Tool/sunf77.xml
new file mode 100644
index 0000000..0365bea
--- /dev/null
+++ b/src/engine/SCons/Tool/sunf77.xml
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sunf77">
+<summary>
+Set construction variables for the Sun &f77; Fortran compiler.
+</summary>
+<sets>
+FORTRAN
+F77
+SHFORTRAN
+SHF77
+SHFORTRANFLAGS
+SHF77FLAGS
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/sunf90.py b/src/engine/SCons/Tool/sunf90.py
new file mode 100644
index 0000000..70fb2d1
--- /dev/null
+++ b/src/engine/SCons/Tool/sunf90.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.sunf90
+
+Tool-specific initialization for sunf90, the Sun Studio F90 compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sunf90.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+from FortranCommon import add_all_to_env
+
+compilers = ['sunf90', 'f90']
+
+def generate(env):
+ """Add Builders and construction variables for sun f90 compiler to an
+ Environment."""
+ add_all_to_env(env)
+
+ fcomp = env.Detect(compilers) or 'f90'
+ env['FORTRAN'] = fcomp
+ env['F90'] = fcomp
+
+ env['SHFORTRAN'] = '$FORTRAN'
+ env['SHF90'] = '$F90'
+
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC')
+ env['SHF90FLAGS'] = SCons.Util.CLVar('$F90FLAGS -KPIC')
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sunf90.xml b/src/engine/SCons/Tool/sunf90.xml
new file mode 100644
index 0000000..f7a069a
--- /dev/null
+++ b/src/engine/SCons/Tool/sunf90.xml
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sunf90">
+<summary>
+Set construction variables for the Sun &f90; Fortran compiler.
+</summary>
+<sets>
+FORTRAN
+F90
+SHFORTRAN
+SHF90
+SHFORTRANFLAGS
+SHF90FLAGS
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/sunf95.py b/src/engine/SCons/Tool/sunf95.py
new file mode 100644
index 0000000..16800e8
--- /dev/null
+++ b/src/engine/SCons/Tool/sunf95.py
@@ -0,0 +1,64 @@
+"""SCons.Tool.sunf95
+
+Tool-specific initialization for sunf95, the Sun Studio F95 compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sunf95.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Util
+
+from FortranCommon import add_all_to_env
+
+compilers = ['sunf95', 'f95']
+
+def generate(env):
+ """Add Builders and construction variables for sunf95 to an
+ Environment."""
+ add_all_to_env(env)
+
+ fcomp = env.Detect(compilers) or 'f95'
+ env['FORTRAN'] = fcomp
+ env['F95'] = fcomp
+
+ env['SHFORTRAN'] = '$FORTRAN'
+ env['SHF95'] = '$F95'
+
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC')
+ env['SHF95FLAGS'] = SCons.Util.CLVar('$F95FLAGS -KPIC')
+
+def exists(env):
+ return env.Detect(compilers)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sunf95.xml b/src/engine/SCons/Tool/sunf95.xml
new file mode 100644
index 0000000..0f1e005
--- /dev/null
+++ b/src/engine/SCons/Tool/sunf95.xml
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sunf95">
+<summary>
+Set construction variables for the Sun &f95; Fortran compiler.
+</summary>
+<sets>
+FORTRAN
+F95
+SHFORTRAN
+SHF95
+SHFORTRANFLAGS
+SHF95FLAGS
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py
new file mode 100644
index 0000000..88abae9
--- /dev/null
+++ b/src/engine/SCons/Tool/sunlink.py
@@ -0,0 +1,77 @@
+"""SCons.Tool.sunlink
+
+Tool-specific initialization for the Sun Solaris (Forte) linker.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/sunlink.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+
+import SCons.Util
+
+import link
+
+ccLinker = None
+
+# search for the acc compiler and linker front end
+
+try:
+ dirs = os.listdir('/opt')
+except (IOError, OSError):
+ # Not being able to read the directory because it doesn't exist
+ # (IOError) or isn't readable (OSError) is okay.
+ dirs = []
+
+for d in dirs:
+ linker = '/opt/' + d + '/bin/CC'
+ if os.path.exists(linker):
+ ccLinker = linker
+ break
+
+def generate(env):
+ """Add Builders and construction variables for Forte to an Environment."""
+ link.generate(env)
+
+ env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G')
+
+ env.Append(LINKFLAGS=['$__RPATH'])
+ env['RPATHPREFIX'] = '-R'
+ env['RPATHSUFFIX'] = ''
+ env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}'
+
+def exists(env):
+ return ccLinker
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/sunlink.xml b/src/engine/SCons/Tool/sunlink.xml
new file mode 100644
index 0000000..17b407f
--- /dev/null
+++ b/src/engine/SCons/Tool/sunlink.xml
@@ -0,0 +1,16 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="sunlink">
+<summary>
+Sets construction variables for the Sun linker.
+</summary>
+<sets>
+SHLINKFLAGS
+RPATHPREFIX
+RPATHSUFFIX
+</sets>
+</tool>
diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py
new file mode 100644
index 0000000..1dfbb40
--- /dev/null
+++ b/src/engine/SCons/Tool/swig.py
@@ -0,0 +1,186 @@
+"""SCons.Tool.swig
+
+Tool-specific initialization for swig.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/swig.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import re
+import string
+import subprocess
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Scanner
+import SCons.Tool
+import SCons.Util
+
+SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR')
+
+def swigSuffixEmitter(env, source):
+ if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)):
+ return '$SWIGCXXFILESUFFIX'
+ else:
+ return '$SWIGCFILESUFFIX'
+
+# Match '%module test', as well as '%module(directors="1") test'
+# Also allow for test to be quoted (SWIG permits double quotes, but not single)
+_reModule = re.compile(r'%module(\s*\(.*\))?\s+("?)(.+)\2')
+
+def _find_modules(src):
+ """Find all modules referenced by %module lines in `src`, a SWIG .i file.
+ Returns a list of all modules, and a flag set if SWIG directors have
+ been requested (SWIG will generate an additional header file in this
+ case.)"""
+ directors = 0
+ mnames = []
+ try:
+ matches = _reModule.findall(open(src).read())
+ except IOError:
+ # If the file's not yet generated, guess the module name from the filename
+ matches = []
+ mnames.append(os.path.splitext(src)[0])
+
+ for m in matches:
+ mnames.append(m[2])
+ directors = directors or string.find(m[0], 'directors') >= 0
+ return mnames, directors
+
+def _add_director_header_targets(target, env):
+ # Directors only work with C++ code, not C
+ suffix = env.subst(env['SWIGCXXFILESUFFIX'])
+ # For each file ending in SWIGCXXFILESUFFIX, add a new target director
+ # header by replacing the ending with SWIGDIRECTORSUFFIX.
+ for x in target[:]:
+ n = x.name
+ d = x.dir
+ if n[-len(suffix):] == suffix:
+ target.append(d.File(n[:-len(suffix)] + env['SWIGDIRECTORSUFFIX']))
+
+def _swigEmitter(target, source, env):
+ swigflags = env.subst("$SWIGFLAGS", target=target, source=source)
+ flags = SCons.Util.CLVar(swigflags)
+ for src in source:
+ src = str(src.rfile())
+ mnames = None
+ if "-python" in flags and "-noproxy" not in flags:
+ if mnames is None:
+ mnames, directors = _find_modules(src)
+ if directors:
+ _add_director_header_targets(target, env)
+ python_files = map(lambda m: m + ".py", mnames)
+ outdir = env.subst('$SWIGOUTDIR', target=target, source=source)
+ # .py files should be generated in SWIGOUTDIR if specified,
+ # otherwise in the same directory as the target
+ if outdir:
+ python_files = map(lambda j, o=outdir, e=env:
+ e.fs.File(os.path.join(o, j)),
+ python_files)
+ else:
+ python_files = map(lambda m, d=target[0].dir:
+ d.File(m), python_files)
+ target.extend(python_files)
+ if "-java" in flags:
+ if mnames is None:
+ mnames, directors = _find_modules(src)
+ if directors:
+ _add_director_header_targets(target, env)
+ java_files = map(lambda m: [m + ".java", m + "JNI.java"], mnames)
+ java_files = SCons.Util.flatten(java_files)
+ outdir = env.subst('$SWIGOUTDIR', target=target, source=source)
+ if outdir:
+ java_files = map(lambda j, o=outdir: os.path.join(o, j), java_files)
+ java_files = map(env.fs.File, java_files)
+ for jf in java_files:
+ t_from_s = lambda t, p, s, x: t.dir
+ SCons.Util.AddMethod(jf, t_from_s, 'target_from_source')
+ target.extend(java_files)
+ return (target, source)
+
+def _get_swig_version(env):
+ """Run the SWIG command line tool to get and return the version number"""
+ pipe = SCons.Action._subproc(env, [env['SWIG'], '-version'],
+ stdin = 'devnull',
+ stderr = 'devnull',
+ stdout = subprocess.PIPE)
+ if pipe.wait() != 0: return
+
+ out = pipe.stdout.read()
+ match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE)
+ if match:
+ return match.group(1)
+
+def generate(env):
+ """Add Builders and construction variables for swig to an Environment."""
+ c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
+
+ c_file.suffix['.i'] = swigSuffixEmitter
+ cxx_file.suffix['.i'] = swigSuffixEmitter
+
+ c_file.add_action('.i', SwigAction)
+ c_file.add_emitter('.i', _swigEmitter)
+ cxx_file.add_action('.i', SwigAction)
+ cxx_file.add_emitter('.i', _swigEmitter)
+
+ java_file = SCons.Tool.CreateJavaFileBuilder(env)
+
+ java_file.suffix['.i'] = swigSuffixEmitter
+
+ java_file.add_action('.i', SwigAction)
+ java_file.add_emitter('.i', _swigEmitter)
+
+ env['SWIG'] = 'swig'
+ env['SWIGVERSION'] = _get_swig_version(env)
+ env['SWIGFLAGS'] = SCons.Util.CLVar('')
+ env['SWIGDIRECTORSUFFIX'] = '_wrap.h'
+ env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX'
+ env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX'
+ env['_SWIGOUTDIR'] = r'${"-outdir \"%s\"" % SWIGOUTDIR}'
+ env['SWIGPATH'] = []
+ env['SWIGINCPREFIX'] = '-I'
+ env['SWIGINCSUFFIX'] = ''
+ env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+ env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES'
+
+ expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)'
+ scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr)
+
+ env.Append(SCANNERS = scanner)
+
+def exists(env):
+ return env.Detect(['swig'])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/swig.xml b/src/engine/SCons/Tool/swig.xml
new file mode 100644
index 0000000..2716a33
--- /dev/null
+++ b/src/engine/SCons/Tool/swig.xml
@@ -0,0 +1,212 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="swig">
+<summary>
+Sets construction variables for the SWIG interface generator.
+</summary>
+<sets>
+SWIG
+SWIGFLAGS
+SWIGDIRECTORSUFFIX
+SWIGCFILESUFFIX
+SWIGCXXFILESUFFIX
+_SWIGINCFLAGS
+SWIGINCPREFIX
+SWIGINCSUFFIX
+SWIGCOM
+SWIGPATH
+SWIGVERSION
+</sets>
+<uses>
+SWIGCOMSTR
+</uses>
+</tool>
+
+<cvar name="SWIG">
+<summary>
+The scripting language wrapper and interface generator.
+</summary>
+</cvar>
+
+<cvar name="SWIGCFILESUFFIX">
+<summary>
+The suffix that will be used for intermediate C
+source files generated by
+the scripting language wrapper and interface generator.
+The default value is
+<filename>_wrap</filename>&cv-link-CFILESUFFIX;.
+By default, this value is used whenever the
+<option>-c++</option>
+option is
+<emphasis>not</emphasis>
+specified as part of the
+&cv-link-SWIGFLAGS;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="SWIGDIRECTORSUFFIX">
+<summary>
+The suffix that will be used for intermediate C++ header
+files generated by the scripting language wrapper and interface generator.
+These are only generated for C++ code when the SWIG 'directors' feature is
+turned on.
+The default value is
+<filename>_wrap.h</filename>.
+</summary>
+</cvar>
+
+<cvar name="SWIGCOM">
+<summary>
+The command line used to call
+the scripting language wrapper and interface generator.
+</summary>
+</cvar>
+
+<cvar name="SWIGCOMSTR">
+<summary>
+The string displayed when calling
+the scripting language wrapper and interface generator.
+If this is not set, then &cv-link-SWIGCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="SWIGCXXFILESUFFIX">
+<summary>
+The suffix that will be used for intermediate C++
+source files generated by
+the scripting language wrapper and interface generator.
+The default value is
+<filename>_wrap</filename>&cv-link-CFILESUFFIX;.
+By default, this value is used whenever the
+<filename>-c++</filename>
+option is specified as part of the
+&cv-link-SWIGFLAGS;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="SWIGFLAGS">
+<summary>
+General options passed to
+the scripting language wrapper and interface generator.
+This is where you should set
+<option>-python</option>,
+<option>-perl5</option>,
+<option>-tcl</option>,
+or whatever other options you want to specify to SWIG.
+If you set the
+<option>-c++</option>
+option in this variable,
+&scons;
+will, by default,
+generate a C++ intermediate source file
+with the extension that is specified as the
+&cv-link-CXXFILESUFFIX;
+variable.
+</summary>
+</cvar>
+
+<cvar name="_SWIGINCFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the SWIG command-line options
+for specifying directories to be searched for included files.
+The value of &cv-_SWIGINCFLAGS; is created
+by appending &cv-SWIGINCPREFIX; and &cv-SWIGINCSUFFIX;
+to the beginning and end
+of each directory in &cv-SWIGPATH;.
+</summary>
+</cvar>
+
+<cvar name="SWIGINCPREFIX">
+<summary>
+The prefix used to specify an include directory on the SWIG command line.
+This will be appended to the beginning of each directory
+in the &cv-SWIGPATH; construction variable
+when the &cv-_SWIGINCFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="SWIGINCSUFFIX">
+<summary>
+The suffix used to specify an include directory on the SWIG command line.
+This will be appended to the end of each directory
+in the &cv-SWIGPATH; construction variable
+when the &cv-_SWIGINCFLAGS; variable is automatically generated.
+</summary>
+</cvar>
+
+<cvar name="SWIGOUTDIR">
+<summary>
+Specifies the output directory in which
+the scripting language wrapper and interface generator
+should place generated language-specific files.
+This will be used by SCons to identify
+the files that will be generated by the &swig; call,
+and translated into the
+<literal>swig -outdir</literal> option on the command line.
+</summary>
+</cvar>
+
+<cvar name="SWIGPATH">
+<summary>
+The list of directories that the scripting language wrapper
+and interface generate will search for included files.
+The SWIG implicit dependency scanner will search these
+directories for include files.
+The default is to use the same path
+specified as &cv-CPPPATH;.
+
+Don't explicitly put include directory
+arguments in SWIGFLAGS;
+the result will be non-portable
+and the directories will not be searched by the dependency scanner.
+Note: directory names in SWIGPATH will be looked-up relative to the SConscript
+directory when they are used in a command.
+To force
+&scons;
+to look-up a directory relative to the root of the source tree use #:
+
+<example>
+env = Environment(SWIGPATH='#/include')
+</example>
+
+The directory look-up can also be forced using the
+&Dir;()
+function:
+
+<example>
+include = Dir('include')
+env = Environment(SWIGPATH=include)
+</example>
+
+The directory list will be added to command lines
+through the automatically-generated
+&cv-_SWIGINCFLAGS;
+construction variable,
+which is constructed by
+appending the values of the
+&cv-SWIGINCPREFIX; and &cv-SWIGINCSUFFIX;
+construction variables
+to the beginning and end
+of each directory in &cv-SWIGPATH;.
+Any command lines you define that need
+the SWIGPATH directory list should
+include &cv-_SWIGINCFLAGS;:
+
+<example>
+env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SORUCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="SWIGVERSION">
+<summary>
+The version number of the SWIG tool.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/tar.py b/src/engine/SCons/Tool/tar.py
new file mode 100644
index 0000000..7d74004
--- /dev/null
+++ b/src/engine/SCons/Tool/tar.py
@@ -0,0 +1,73 @@
+"""SCons.Tool.tar
+
+Tool-specific initialization for tar.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/tar.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Node.FS
+import SCons.Util
+
+tars = ['tar', 'gtar']
+
+TarAction = SCons.Action.Action('$TARCOM', '$TARCOMSTR')
+
+TarBuilder = SCons.Builder.Builder(action = TarAction,
+ source_factory = SCons.Node.FS.Entry,
+ source_scanner = SCons.Defaults.DirScanner,
+ suffix = '$TARSUFFIX',
+ multi = 1)
+
+
+def generate(env):
+ """Add Builders and construction variables for tar to an Environment."""
+ try:
+ bld = env['BUILDERS']['Tar']
+ except KeyError:
+ bld = TarBuilder
+ env['BUILDERS']['Tar'] = bld
+
+ env['TAR'] = env.Detect(tars) or 'gtar'
+ env['TARFLAGS'] = SCons.Util.CLVar('-c')
+ env['TARCOM'] = '$TAR $TARFLAGS -f $TARGET $SOURCES'
+ env['TARSUFFIX'] = '.tar'
+
+def exists(env):
+ return env.Detect(tars)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/tar.xml b/src/engine/SCons/Tool/tar.xml
new file mode 100644
index 0000000..4e6926a
--- /dev/null
+++ b/src/engine/SCons/Tool/tar.xml
@@ -0,0 +1,95 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="tar">
+<summary>
+Sets construction variables for the &tar; archiver.
+</summary>
+<sets>
+TAR
+TARFLAGS
+TARCOM
+TARSUFFIX
+</sets>
+<uses>
+TARCOMSTR
+</uses>
+</tool>
+
+<builder name="Tar">
+<summary>
+Builds a tar archive of the specified files
+and/or directories.
+Unlike most builder methods,
+the
+&b-Tar;
+builder method may be called multiple times
+for a given target;
+each additional call
+adds to the list of entries
+that will be built into the archive.
+Any source directories will
+be scanned for changes to
+any on-disk files,
+regardless of whether or not
+&scons;
+knows about them from other Builder or function calls.
+
+<example>
+env.Tar('src.tar', 'src')
+
+# Create the stuff.tar file.
+env.Tar('stuff', ['subdir1', 'subdir2'])
+# Also add "another" to the stuff.tar file.
+env.Tar('stuff', 'another')
+
+# Set TARFLAGS to create a gzip-filtered archive.
+env = Environment(TARFLAGS = '-c -z')
+env.Tar('foo.tar.gz', 'foo')
+
+# Also set the suffix to .tgz.
+env = Environment(TARFLAGS = '-c -z',
+ TARSUFFIX = '.tgz')
+env.Tar('foo')
+</example>
+</summary>
+</builder>
+
+<cvar name="TAR">
+<summary>
+The tar archiver.
+</summary>
+</cvar>
+
+<cvar name="TARCOM">
+<summary>
+The command line used to call the tar archiver.
+</summary>
+</cvar>
+
+<cvar name="TARCOMSTR">
+<summary>
+The string displayed when archiving files
+using the tar archiver.
+If this is not set, then &cv-link-TARCOM; (the command line) is displayed.
+
+<example>
+env = Environment(TARCOMSTR = "Archiving $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="TARFLAGS">
+<summary>
+General options passed to the tar archiver.
+</summary>
+</cvar>
+
+<cvar name="TARSUFFIX">
+<summary>
+The suffix used for tar file names.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py
new file mode 100644
index 0000000..f12675f
--- /dev/null
+++ b/src/engine/SCons/Tool/tex.py
@@ -0,0 +1,792 @@
+"""SCons.Tool.tex
+
+Tool-specific initialization for TeX.
+Generates .dvi files from .tex files
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/tex.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import re
+import string
+import shutil
+
+import SCons.Action
+import SCons.Node
+import SCons.Node.FS
+import SCons.Util
+import SCons.Scanner.LaTeX
+
+Verbose = False
+
+must_rerun_latex = True
+
+# these are files that just need to be checked for changes and then rerun latex
+check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm']
+
+# these are files that require bibtex or makeindex to be run when they change
+all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo', '.acn']
+
+#
+# regular expressions used to search for Latex features
+# or outputs that require rerunning latex
+#
+# search for all .aux files opened by latex (recorded in the .fls file)
+openout_aux_re = re.compile(r"INPUT *(.*\.aux)")
+
+#printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE)
+#printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE)
+#printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE)
+
+# search to find rerun warnings
+warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)'
+warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE)
+
+# search to find citation rerun warnings
+rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct"
+rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE)
+
+# search to find undefined references or citations warnings
+undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)'
+undefined_references_re = re.compile(undefined_references_str, re.MULTILINE)
+
+# used by the emitter
+auxfile_re = re.compile(r".", re.MULTILINE)
+tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE)
+makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE)
+bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE)
+listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE)
+listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE)
+hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE)
+makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE)
+makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE)
+makeglossaries_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE)
+makeacronyms_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE)
+beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE)
+
+# search to find all files included by Latex
+include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE)
+
+# search to find all graphics files included by Latex
+includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE)
+
+# search to find all files opened by Latex (recorded in .log file)
+openout_re = re.compile(r"OUTPUT *(.*)")
+
+# list of graphics file extensions for TeX and LaTeX
+TexGraphics = SCons.Scanner.LaTeX.TexGraphics
+LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics
+
+# An Action sufficient to build any generic tex file.
+TeXAction = None
+
+# An action to build a latex file. This action might be needed more
+# than once if we are dealing with labels and bibtex.
+LaTeXAction = None
+
+# An action to run BibTeX on a file.
+BibTeXAction = None
+
+# An action to run MakeIndex on a file.
+MakeIndexAction = None
+
+# An action to run MakeIndex (for nomencl) on a file.
+MakeNclAction = None
+
+# An action to run MakeIndex (for glossary) on a file.
+MakeGlossaryAction = None
+
+# An action to run MakeIndex (for acronyms) on a file.
+MakeAcronymsAction = None
+
+# Used as a return value of modify_env_var if the variable is not set.
+_null = SCons.Scanner.LaTeX._null
+
+modify_env_var = SCons.Scanner.LaTeX.modify_env_var
+
+def FindFile(name,suffixes,paths,env,requireExt=False):
+ if requireExt:
+ name,ext = SCons.Util.splitext(name)
+ # if the user gave an extension use it.
+ if ext:
+ name = name + ext
+ if Verbose:
+ print " searching for '%s' with extensions: " % name,suffixes
+
+ for path in paths:
+ testName = os.path.join(path,name)
+ if Verbose:
+ print " look for '%s'" % testName
+ if os.path.exists(testName):
+ if Verbose:
+ print " found '%s'" % testName
+ return env.fs.File(testName)
+ else:
+ name_ext = SCons.Util.splitext(testName)[1]
+ if name_ext:
+ continue
+
+ # if no suffix try adding those passed in
+ for suffix in suffixes:
+ testNameExt = testName + suffix
+ if Verbose:
+ print " look for '%s'" % testNameExt
+
+ if os.path.exists(testNameExt):
+ if Verbose:
+ print " found '%s'" % testNameExt
+ return env.fs.File(testNameExt)
+ if Verbose:
+ print " did not find '%s'" % name
+ return None
+
+def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None):
+ """A builder for LaTeX files that checks the output in the aux file
+ and decides how many times to use LaTeXAction, and BibTeXAction."""
+
+ global must_rerun_latex
+
+ # This routine is called with two actions. In this file for DVI builds
+ # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction
+ # set this up now for the case where the user requests a different extension
+ # for the target filename
+ if (XXXLaTeXAction == LaTeXAction):
+ callerSuffix = ".dvi"
+ else:
+ callerSuffix = env['PDFSUFFIX']
+
+ basename = SCons.Util.splitext(str(source[0]))[0]
+ basedir = os.path.split(str(source[0]))[0]
+ basefile = os.path.split(str(basename))[1]
+ abspath = os.path.abspath(basedir)
+
+ targetext = os.path.splitext(str(target[0]))[1]
+ targetdir = os.path.split(str(target[0]))[0]
+
+ saved_env = {}
+ for var in SCons.Scanner.LaTeX.LaTeX.env_variables:
+ saved_env[var] = modify_env_var(env, var, abspath)
+
+ # Create base file names with the target directory since the auxiliary files
+ # will be made there. That's because the *COM variables have the cd
+ # command in the prolog. We check
+ # for the existence of files before opening them--even ones like the
+ # aux file that TeX always creates--to make it possible to write tests
+ # with stubs that don't necessarily generate all of the same files.
+
+ targetbase = os.path.join(targetdir, basefile)
+
+ # if there is a \makeindex there will be a .idx and thus
+ # we have to run makeindex at least once to keep the build
+ # happy even if there is no index.
+ # Same for glossaries and nomenclature
+ src_content = source[0].get_text_contents()
+ run_makeindex = makeindex_re.search(src_content) and not os.path.exists(targetbase + '.idx')
+ run_nomenclature = makenomenclature_re.search(src_content) and not os.path.exists(targetbase + '.nlo')
+ run_glossary = makeglossary_re.search(src_content) and not os.path.exists(targetbase + '.glo')
+ run_glossaries = makeglossaries_re.search(src_content) and not os.path.exists(targetbase + '.glo')
+ run_acronyms = makeacronyms_re.search(src_content) and not os.path.exists(targetbase + '.acn')
+
+ saved_hashes = {}
+ suffix_nodes = {}
+
+ for suffix in all_suffixes:
+ theNode = env.fs.File(targetbase + suffix)
+ suffix_nodes[suffix] = theNode
+ saved_hashes[suffix] = theNode.get_csig()
+
+ if Verbose:
+ print "hashes: ",saved_hashes
+
+ must_rerun_latex = True
+
+ #
+ # routine to update MD5 hash and compare
+ #
+ # TODO(1.5): nested scopes
+ def check_MD5(filenode, suffix, saved_hashes=saved_hashes, targetbase=targetbase):
+ global must_rerun_latex
+ # two calls to clear old csig
+ filenode.clear_memoized_values()
+ filenode.ninfo = filenode.new_ninfo()
+ new_md5 = filenode.get_csig()
+
+ if saved_hashes[suffix] == new_md5:
+ if Verbose:
+ print "file %s not changed" % (targetbase+suffix)
+ return False # unchanged
+ saved_hashes[suffix] = new_md5
+ must_rerun_latex = True
+ if Verbose:
+ print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5
+ return True # changed
+
+ # generate the file name that latex will generate
+ resultfilename = targetbase + callerSuffix
+
+ count = 0
+
+ while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) :
+ result = XXXLaTeXAction(target, source, env)
+ if result != 0:
+ return result
+
+ count = count + 1
+
+ must_rerun_latex = False
+ # Decide if various things need to be run, or run again.
+
+ # Read the log file to find warnings/errors
+ logfilename = targetbase + '.log'
+ logContent = ''
+ if os.path.exists(logfilename):
+ logContent = open(logfilename, "rb").read()
+
+
+ # Read the fls file to find all .aux files
+ flsfilename = targetbase + '.fls'
+ flsContent = ''
+ auxfiles = []
+ if os.path.exists(flsfilename):
+ flsContent = open(flsfilename, "rb").read()
+ auxfiles = openout_aux_re.findall(flsContent)
+ if Verbose:
+ print "auxfiles ",auxfiles
+
+ # Now decide if bibtex will need to be run.
+ # The information that bibtex reads from the .aux file is
+ # pass-independent. If we find (below) that the .bbl file is unchanged,
+ # then the last latex saw a correct bibliography.
+ # Therefore only do this on the first pass
+ if count == 1:
+ for auxfilename in auxfiles:
+ target_aux = os.path.join(targetdir, auxfilename)
+ if os.path.exists(target_aux):
+ content = open(target_aux, "rb").read()
+ if string.find(content, "bibdata") != -1:
+ if Verbose:
+ print "Need to run bibtex"
+ bibfile = env.fs.File(targetbase)
+ result = BibTeXAction(bibfile, bibfile, env)
+ if result != 0:
+ print env['BIBTEX']," returned an error, check the blg file"
+ return result
+ must_rerun_latex = check_MD5(suffix_nodes['.bbl'],'.bbl')
+ break
+
+ # Now decide if latex will need to be run again due to index.
+ if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex):
+ # We must run makeindex
+ if Verbose:
+ print "Need to run makeindex"
+ idxfile = suffix_nodes['.idx']
+ result = MakeIndexAction(idxfile, idxfile, env)
+ if result != 0:
+ print env['MAKEINDEX']," returned an error, check the ilg file"
+ return result
+
+ # TO-DO: need to add a way for the user to extend this list for whatever
+ # auxiliary files they create in other (or their own) packages
+ # Harder is case is where an action needs to be called -- that should be rare (I hope?)
+
+ for index in check_suffixes:
+ check_MD5(suffix_nodes[index],index)
+
+ # Now decide if latex will need to be run again due to nomenclature.
+ if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature):
+ # We must run makeindex
+ if Verbose:
+ print "Need to run makeindex for nomenclature"
+ nclfile = suffix_nodes['.nlo']
+ result = MakeNclAction(nclfile, nclfile, env)
+ if result != 0:
+ print env['MAKENCL']," (nomenclature) returned an error, check the nlg file"
+ #return result
+
+ # Now decide if latex will need to be run again due to glossary.
+ if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary):
+ # We must run makeindex
+ if Verbose:
+ print "Need to run makeindex for glossary"
+ glofile = suffix_nodes['.glo']
+ result = MakeGlossaryAction(glofile, glofile, env)
+ if result != 0:
+ print env['MAKEGLOSSARY']," (glossary) returned an error, check the glg file"
+ #return result
+
+ # Now decide if latex will need to be run again due to acronyms.
+ if check_MD5(suffix_nodes['.acn'],'.acn') or (count == 1 and run_acronyms):
+ # We must run makeindex
+ if Verbose:
+ print "Need to run makeindex for acronyms"
+ acrfile = suffix_nodes['.acn']
+ result = MakeAcronymsAction(acrfile, acrfile, env)
+ if result != 0:
+ print env['MAKEACRONYMS']," (acronymns) returned an error, check the alg file"
+ return result
+
+ # Now decide if latex needs to be run yet again to resolve warnings.
+ if warning_rerun_re.search(logContent):
+ must_rerun_latex = True
+ if Verbose:
+ print "rerun Latex due to latex or package rerun warning"
+
+ if rerun_citations_re.search(logContent):
+ must_rerun_latex = True
+ if Verbose:
+ print "rerun Latex due to 'Rerun to get citations correct' warning"
+
+ if undefined_references_re.search(logContent):
+ must_rerun_latex = True
+ if Verbose:
+ print "rerun Latex due to undefined references or citations"
+
+ if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex):
+ print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES'))
+# end of while loop
+
+ # rename Latex's output to what the target name is
+ if not (str(target[0]) == resultfilename and os.path.exists(resultfilename)):
+ if os.path.exists(resultfilename):
+ print "move %s to %s" % (resultfilename, str(target[0]), )
+ shutil.move(resultfilename,str(target[0]))
+
+ # Original comment (when TEXPICTS was not restored):
+ # The TEXPICTS enviroment variable is needed by a dvi -> pdf step
+ # later on Mac OSX so leave it
+ #
+ # It is also used when searching for pictures (implicit dependencies).
+ # Why not set the variable again in the respective builder instead
+ # of leaving local modifications in the environment? What if multiple
+ # latex builds in different directories need different TEXPICTS?
+ for var in SCons.Scanner.LaTeX.LaTeX.env_variables:
+ if var == 'TEXPICTS':
+ continue
+ if saved_env[var] is _null:
+ try:
+ del env['ENV'][var]
+ except KeyError:
+ pass # was never set
+ else:
+ env['ENV'][var] = saved_env[var]
+
+ return result
+
+def LaTeXAuxAction(target = None, source= None, env=None):
+ result = InternalLaTeXAuxAction( LaTeXAction, target, source, env )
+ return result
+
+LaTeX_re = re.compile("\\\\document(style|class)")
+
+def is_LaTeX(flist,env,abspath):
+ """Scan a file list to decide if it's TeX- or LaTeX-flavored."""
+
+ # We need to scan files that are included in case the
+ # \documentclass command is in them.
+
+ # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS']
+ savedpath = modify_env_var(env, 'TEXINPUTS', abspath)
+ paths = env['ENV']['TEXINPUTS']
+ if SCons.Util.is_List(paths):
+ pass
+ else:
+ # Split at os.pathsep to convert into absolute path
+ # TODO(1.5)
+ #paths = paths.split(os.pathsep)
+ paths = string.split(paths, os.pathsep)
+
+ # now that we have the path list restore the env
+ if savedpath is _null:
+ try:
+ del env['ENV']['TEXINPUTS']
+ except KeyError:
+ pass # was never set
+ else:
+ env['ENV']['TEXINPUTS'] = savedpath
+ if Verbose:
+ print "is_LaTeX search path ",paths
+ print "files to search :",flist
+
+ # Now that we have the search path and file list, check each one
+ for f in flist:
+ if Verbose:
+ print " checking for Latex source ",str(f)
+
+ content = f.get_text_contents()
+ if LaTeX_re.search(content):
+ if Verbose:
+ print "file %s is a LaTeX file" % str(f)
+ return 1
+ if Verbose:
+ print "file %s is not a LaTeX file" % str(f)
+
+ # now find included files
+ inc_files = [ ]
+ inc_files.extend( include_re.findall(content) )
+ if Verbose:
+ print "files included by '%s': "%str(f),inc_files
+ # inc_files is list of file names as given. need to find them
+ # using TEXINPUTS paths.
+
+ # search the included files
+ for src in inc_files:
+ srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False)
+ # make this a list since is_LaTeX takes a list.
+ fileList = [srcNode,]
+ if Verbose:
+ print "FindFile found ",srcNode
+ if srcNode is not None:
+ file_test = is_LaTeX(fileList, env, abspath)
+
+ # return on first file that finds latex is needed.
+ if file_test:
+ return file_test
+
+ if Verbose:
+ print " done scanning ",str(f)
+
+ return 0
+
+def TeXLaTeXFunction(target = None, source= None, env=None):
+ """A builder for TeX and LaTeX that scans the source file to
+ decide the "flavor" of the source and then executes the appropriate
+ program."""
+
+ # find these paths for use in is_LaTeX to search for included files
+ basedir = os.path.split(str(source[0]))[0]
+ abspath = os.path.abspath(basedir)
+
+ if is_LaTeX(source,env,abspath):
+ result = LaTeXAuxAction(target,source,env)
+ if result != 0:
+ print env['LATEX']," returned an error, check the log file"
+ else:
+ result = TeXAction(target,source,env)
+ if result != 0:
+ print env['TEX']," returned an error, check the log file"
+ return result
+
+def TeXLaTeXStrFunction(target = None, source= None, env=None):
+ """A strfunction for TeX and LaTeX that scans the source file to
+ decide the "flavor" of the source and then returns the appropriate
+ command string."""
+ if env.GetOption("no_exec"):
+
+ # find these paths for use in is_LaTeX to search for included files
+ basedir = os.path.split(str(source[0]))[0]
+ abspath = os.path.abspath(basedir)
+
+ if is_LaTeX(source,env,abspath):
+ result = env.subst('$LATEXCOM',0,target,source)+" ..."
+ else:
+ result = env.subst("$TEXCOM",0,target,source)+" ..."
+ else:
+ result = ''
+ return result
+
+def tex_eps_emitter(target, source, env):
+ """An emitter for TeX and LaTeX sources when
+ executing tex or latex. It will accept .ps and .eps
+ graphics files
+ """
+ (target, source) = tex_emitter_core(target, source, env, TexGraphics)
+
+ return (target, source)
+
+def tex_pdf_emitter(target, source, env):
+ """An emitter for TeX and LaTeX sources when
+ executing pdftex or pdflatex. It will accept graphics
+ files of types .pdf, .jpg, .png, .gif, and .tif
+ """
+ (target, source) = tex_emitter_core(target, source, env, LatexGraphics)
+
+ return (target, source)
+
+def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir):
+ """ For theFile (a Node) update any file_tests and search for graphics files
+ then find all included files and call ScanFiles recursively for each of them"""
+
+ content = theFile.get_text_contents()
+ if Verbose:
+ print " scanning ",str(theFile)
+
+ for i in range(len(file_tests_search)):
+ if file_tests[i][0] is None:
+ file_tests[i][0] = file_tests_search[i].search(content)
+
+ # recursively call this on each of the included files
+ inc_files = [ ]
+ inc_files.extend( include_re.findall(content) )
+ if Verbose:
+ print "files included by '%s': "%str(theFile),inc_files
+ # inc_files is list of file names as given. need to find them
+ # using TEXINPUTS paths.
+
+ for src in inc_files:
+ srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False)
+ if srcNode is not None:
+ file_tests = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir)
+ if Verbose:
+ print " done scanning ",str(theFile)
+ return file_tests
+
+def tex_emitter_core(target, source, env, graphics_extensions):
+ """An emitter for TeX and LaTeX sources.
+ For LaTeX sources we try and find the common created files that
+ are needed on subsequent runs of latex to finish tables of contents,
+ bibliographies, indices, lists of figures, and hyperlink references.
+ """
+ basename = SCons.Util.splitext(str(source[0]))[0]
+ basefile = os.path.split(str(basename))[1]
+ targetdir = os.path.split(str(target[0]))[0]
+ targetbase = os.path.join(targetdir, basefile)
+
+ basedir = os.path.split(str(source[0]))[0]
+ abspath = os.path.abspath(basedir)
+ target[0].attributes.path = abspath
+
+ #
+ # file names we will make use of in searching the sources and log file
+ #
+ emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg', '.alg'] + all_suffixes
+ auxfilename = targetbase + '.aux'
+ logfilename = targetbase + '.log'
+ flsfilename = targetbase + '.fls'
+
+ env.SideEffect(auxfilename,target[0])
+ env.SideEffect(logfilename,target[0])
+ env.SideEffect(flsfilename,target[0])
+ if Verbose:
+ print "side effect :",auxfilename,logfilename,flsfilename
+ env.Clean(target[0],auxfilename)
+ env.Clean(target[0],logfilename)
+ env.Clean(target[0],flsfilename)
+
+ content = source[0].get_text_contents()
+
+ idx_exists = os.path.exists(targetbase + '.idx')
+ nlo_exists = os.path.exists(targetbase + '.nlo')
+ glo_exists = os.path.exists(targetbase + '.glo')
+ acr_exists = os.path.exists(targetbase + '.acn')
+
+ # set up list with the regular expressions
+ # we use to find features used
+ file_tests_search = [auxfile_re,
+ makeindex_re,
+ bibliography_re,
+ tableofcontents_re,
+ listoffigures_re,
+ listoftables_re,
+ hyperref_re,
+ makenomenclature_re,
+ makeglossary_re,
+ makeglossaries_re,
+ makeacronyms_re,
+ beamer_re ]
+ # set up list with the file suffixes that need emitting
+ # when a feature is found
+ file_tests_suff = [['.aux'],
+ ['.idx', '.ind', '.ilg'],
+ ['.bbl', '.blg'],
+ ['.toc'],
+ ['.lof'],
+ ['.lot'],
+ ['.out'],
+ ['.nlo', '.nls', '.nlg'],
+ ['.glo', '.gls', '.glg'],
+ ['.glo', '.gls', '.glg'],
+ ['.acn', '.acr', '.alg'],
+ ['.nav', '.snm', '.out', '.toc'] ]
+ # build the list of lists
+ file_tests = []
+ for i in range(len(file_tests_search)):
+ file_tests.append( [None, file_tests_suff[i]] )
+
+ # TO-DO: need to add a way for the user to extend this list for whatever
+ # auxiliary files they create in other (or their own) packages
+
+ # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS']
+ savedpath = modify_env_var(env, 'TEXINPUTS', abspath)
+ paths = env['ENV']['TEXINPUTS']
+ if SCons.Util.is_List(paths):
+ pass
+ else:
+ # Split at os.pathsep to convert into absolute path
+ # TODO(1.5)
+ #paths = paths.split(os.pathsep)
+ paths = string.split(paths, os.pathsep)
+
+ # now that we have the path list restore the env
+ if savedpath is _null:
+ try:
+ del env['ENV']['TEXINPUTS']
+ except KeyError:
+ pass # was never set
+ else:
+ env['ENV']['TEXINPUTS'] = savedpath
+ if Verbose:
+ print "search path ",paths
+
+ file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir)
+
+ for (theSearch,suffix_list) in file_tests:
+ if theSearch:
+ for suffix in suffix_list:
+ env.SideEffect(targetbase + suffix,target[0])
+ if Verbose:
+ print "side effect :",targetbase + suffix
+ env.Clean(target[0],targetbase + suffix)
+
+ # read fls file to get all other files that latex creates and will read on the next pass
+ # remove files from list that we explicitly dealt with above
+ if os.path.exists(flsfilename):
+ content = open(flsfilename, "rb").read()
+ out_files = openout_re.findall(content)
+ myfiles = [auxfilename, logfilename, flsfilename, targetbase+'.dvi',targetbase+'.pdf']
+ for filename in out_files[:]:
+ if filename in myfiles:
+ out_files.remove(filename)
+ env.SideEffect(out_files,target[0])
+ if Verbose:
+ print "side effect :",out_files
+ env.Clean(target[0],out_files)
+
+ return (target, source)
+
+
+TeXLaTeXAction = None
+
+def generate(env):
+ """Add Builders and construction variables for TeX to an Environment."""
+
+ global TeXLaTeXAction
+ if TeXLaTeXAction is None:
+ TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction,
+ strfunction=TeXLaTeXStrFunction)
+
+ env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes)
+
+ generate_common(env)
+
+ import dvi
+ dvi.generate(env)
+
+ bld = env['BUILDERS']['DVI']
+ bld.add_action('.tex', TeXLaTeXAction)
+ bld.add_emitter('.tex', tex_eps_emitter)
+
+def generate_common(env):
+ """Add internal Builders and construction variables for LaTeX to an Environment."""
+
+ # A generic tex file Action, sufficient for all tex files.
+ global TeXAction
+ if TeXAction is None:
+ TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR")
+
+ # An Action to build a latex file. This might be needed more
+ # than once if we are dealing with labels and bibtex.
+ global LaTeXAction
+ if LaTeXAction is None:
+ LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR")
+
+ # Define an action to run BibTeX on a file.
+ global BibTeXAction
+ if BibTeXAction is None:
+ BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR")
+
+ # Define an action to run MakeIndex on a file.
+ global MakeIndexAction
+ if MakeIndexAction is None:
+ MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR")
+
+ # Define an action to run MakeIndex on a file for nomenclatures.
+ global MakeNclAction
+ if MakeNclAction is None:
+ MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR")
+
+ # Define an action to run MakeIndex on a file for glossaries.
+ global MakeGlossaryAction
+ if MakeGlossaryAction is None:
+ MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR")
+
+ # Define an action to run MakeIndex on a file for acronyms.
+ global MakeAcronymsAction
+ if MakeAcronymsAction is None:
+ MakeAcronymsAction = SCons.Action.Action("$MAKEACRONYMSCOM", "$MAKEACRONYMSCOMSTR")
+
+ env['TEX'] = 'tex'
+ env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder')
+ env['TEXCOM'] = 'cd ${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}'
+
+ env['PDFTEX'] = 'pdftex'
+ env['PDFTEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder')
+ env['PDFTEXCOM'] = 'cd ${TARGET.dir} && $PDFTEX $PDFTEXFLAGS ${SOURCE.file}'
+
+ env['LATEX'] = 'latex'
+ env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder')
+ env['LATEXCOM'] = 'cd ${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}'
+ env['LATEXRETRIES'] = 3
+
+ env['PDFLATEX'] = 'pdflatex'
+ env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder')
+ env['PDFLATEXCOM'] = 'cd ${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}'
+
+ env['BIBTEX'] = 'bibtex'
+ env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
+ env['BIBTEXCOM'] = 'cd ${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}'
+
+ env['MAKEINDEX'] = 'makeindex'
+ env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('')
+ env['MAKEINDEXCOM'] = 'cd ${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}'
+
+ env['MAKEGLOSSARY'] = 'makeindex'
+ env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist'
+ env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg')
+ env['MAKEGLOSSARYCOM'] = 'cd ${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls'
+
+ env['MAKEACRONYMS'] = 'makeindex'
+ env['MAKEACRONYMSSTYLE'] = '${SOURCE.filebase}.ist'
+ env['MAKEACRONYMSFLAGS'] = SCons.Util.CLVar('-s ${MAKEACRONYMSSTYLE} -t ${SOURCE.filebase}.alg')
+ env['MAKEACRONYMSCOM'] = 'cd ${TARGET.dir} && $MAKEACRONYMS ${SOURCE.filebase}.acn $MAKEACRONYMSFLAGS -o ${SOURCE.filebase}.acr'
+
+ env['MAKENCL'] = 'makeindex'
+ env['MAKENCLSTYLE'] = 'nomencl.ist'
+ env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg'
+ env['MAKENCLCOM'] = 'cd ${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls'
+
+def exists(env):
+ return env.Detect('tex')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/tex.xml b/src/engine/SCons/Tool/tex.xml
new file mode 100644
index 0000000..e8c61c7
--- /dev/null
+++ b/src/engine/SCons/Tool/tex.xml
@@ -0,0 +1,126 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="tex">
+<summary>
+Sets construction variables for the TeX formatter and typesetter.
+</summary>
+<sets>
+TEX
+TEXFLAGS
+TEXCOM
+LATEX
+LATEXFLAGS
+LATEXCOM
+BIBTEX
+BIBTEXFLAGS
+BIBTEXCOM
+MAKEINDEX
+MAKEINDEXFLAGS
+MAKEINDEXCOM
+</sets>
+<uses>
+TEXCOMSTR
+LATEXCOMSTR
+BIBTEXCOMSTR
+MAKEINDEXCOMSTR
+</uses>
+</tool>
+
+<cvar name="BIBTEX">
+<summary>
+The bibliography generator for the TeX formatter and typesetter and the
+LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="BIBTEXCOM">
+<summary>
+The command line used to call the bibliography generator for the
+TeX formatter and typesetter and the LaTeX structured formatter and
+typesetter.
+</summary>
+</cvar>
+
+<cvar name="BIBTEXCOMSTR">
+<summary>
+The string displayed when generating a bibliography
+for TeX or LaTeX.
+If this is not set, then &cv-link-BIBTEXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(BIBTEXCOMSTR = "Generating bibliography $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="BIBTEXFLAGS">
+<summary>
+General options passed to the bibliography generator for the TeX formatter
+and typesetter and the LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEX">
+<summary>
+The makeindex generator for the TeX formatter and typesetter and the
+LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEXCOM">
+<summary>
+The command line used to call the makeindex generator for the
+TeX formatter and typesetter and the LaTeX structured formatter and
+typesetter.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEXCOMSTR">
+<summary>
+The string displayed when calling the makeindex generator for the
+TeX formatter and typesetter
+and the LaTeX structured formatter and typesetter.
+If this is not set, then &cv-link-MAKEINDEXCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEXFLAGS">
+<summary>
+General options passed to the makeindex generator for the TeX formatter
+and typesetter and the LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="TEX">
+<summary>
+The TeX formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="TEXCOM">
+<summary>
+The command line used to call the TeX formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="TEXCOMSTR">
+<summary>
+The string displayed when calling
+the TeX formatter and typesetter.
+If this is not set, then &cv-link-TEXCOM; (the command line) is displayed.
+
+<example>
+env = Environment(TEXCOMSTR = "Building $TARGET from TeX input $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="TEXFLAGS">
+<summary>
+General options passed to the TeX formatter and typesetter.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/textfile.py b/src/engine/SCons/Tool/textfile.py
new file mode 100644
index 0000000..8dea678
--- /dev/null
+++ b/src/engine/SCons/Tool/textfile.py
@@ -0,0 +1,175 @@
+# -*- python -*-
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = """
+Textfile/Substfile builder for SCons.
+
+ Create file 'target' which typically is a textfile. The 'source'
+ may be any combination of strings, Nodes, or lists of same. A
+ 'linesep' will be put between any part written and defaults to
+ os.linesep.
+
+ The only difference between the Textfile builder and the Substfile
+ builder is that strings are converted to Value() nodes for the
+ former and File() nodes for the latter. To insert files in the
+ former or strings in the latter, wrap them in a File() or Value(),
+ respectively.
+
+ The values of SUBST_DICT first have any construction variables
+ expanded (its keys are not expanded). If a value of SUBST_DICT is
+ a python callable function, it is called and the result is expanded
+ as the value. Values are substituted in a "random" order; if any
+ substitution could be further expanded by another subsitition, it
+ is unpredictible whether the expansion will occur.
+"""
+
+__revision__ = "src/engine/SCons/Tool/textfile.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons
+
+import os
+import re
+
+from SCons.Node import Node
+from SCons.Node.Python import Value
+from SCons.Util import is_String, is_Sequence, is_Dict
+
+def _do_subst(node, subs):
+ """
+ Fetch the node contents and replace all instances of the keys with
+ their values. For example, if subs is
+ {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'},
+ then all instances of %VERSION% in the file will be replaced with
+ 1.2345 and so forth.
+ """
+ contents = node.get_text_contents()
+ if not subs: return contents
+ for (k,v) in subs:
+ contents = re.sub(k, v, contents)
+ return contents
+
+def _action(target, source, env):
+ # prepare the line separator
+ linesep = env['LINESEPARATOR']
+ if linesep is None:
+ linesep = os.linesep
+ elif is_String(linesep):
+ pass
+ elif isinstance(linesep, Value):
+ linesep = linesep.get_text_contents()
+ else:
+ raise SCons.Errors.UserError(
+ 'unexpected type/class for LINESEPARATOR: %s'
+ % repr(linesep), None)
+
+ # create a dictionary to use for the substitutions
+ if not env.has_key('SUBST_DICT'):
+ subs = None # no substitutions
+ else:
+ d = env['SUBST_DICT']
+ if is_Dict(d):
+ d = d.items()
+ elif is_Sequence(d):
+ pass
+ else:
+ raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence')
+ subs = []
+ for (k,v) in d:
+ if callable(v):
+ v = v()
+ if is_String(v):
+ v = env.subst(v)
+ else:
+ v = str(v)
+ subs.append((k,v))
+
+ # write the file
+ try:
+ fd = open(target[0].get_path(), "wb")
+ except (OSError,IOError), e:
+ raise SCons.Errors.UserError("Can't write target file %s" % target[0])
+ # separate lines by 'linesep' only if linesep is not empty
+ lsep = None
+ for s in source:
+ if lsep: fd.write(lsep)
+ fd.write(_do_subst(s, subs))
+ lsep = linesep
+ fd.close()
+
+def _strfunc(target, source, env):
+ return "Creating '%s'" % target[0]
+
+def _convert_list_R(newlist, sources):
+ for elem in sources:
+ if is_Sequence(elem):
+ _convert_list_R(newlist, elem)
+ elif isinstance(elem, Node):
+ newlist.append(elem)
+ else:
+ newlist.append(Value(elem))
+def _convert_list(target, source, env):
+ if len(target) != 1:
+ raise SCons.Errors.UserError("Only one target file allowed")
+ newlist = []
+ _convert_list_R(newlist, source)
+ return target, newlist
+
+_common_varlist = ['SUBST_DICT', 'LINESEPARATOR']
+
+_text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX']
+_text_builder = SCons.Builder.Builder(
+ action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist),
+ source_factory = Value,
+ emitter = _convert_list,
+ prefix = '$TEXTFILEPREFIX',
+ suffix = '$TEXTFILESUFFIX',
+ )
+
+_subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX']
+_subst_builder = SCons.Builder.Builder(
+ action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist),
+ source_factory = SCons.Node.FS.File,
+ emitter = _convert_list,
+ prefix = '$SUBSTFILEPREFIX',
+ suffix = '$SUBSTFILESUFFIX',
+ src_suffix = ['.in'],
+ )
+
+def generate(env):
+ env['LINESEPARATOR'] = os.linesep
+ env['BUILDERS']['Textfile'] = _text_builder
+ env['TEXTFILEPREFIX'] = ''
+ env['TEXTFILESUFFIX'] = '.txt'
+ env['BUILDERS']['Substfile'] = _subst_builder
+ env['SUBSTFILEPREFIX'] = ''
+ env['SUBSTFILESUFFIX'] = ''
+
+def exists(env):
+ return 1
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/textfile.xml b/src/engine/SCons/Tool/textfile.xml
new file mode 100644
index 0000000..9b66607
--- /dev/null
+++ b/src/engine/SCons/Tool/textfile.xml
@@ -0,0 +1,205 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="textfile">
+<summary>
+Set construction variables for the &b-Textfile; and &b-Substfile; builders.
+</summary>
+<sets>
+LINESEPARATOR
+SUBSTFILEPREFIX
+SUBSTFILESUFFIX
+TEXTFILEPREFIX
+TEXTFILESUFFIX
+</sets>
+<uses>
+SUBST_DICT
+</uses>
+</tool>
+
+<builder name="Textfile">
+<summary>
+The &b-Textfile; builder generates a single text file.
+The source strings constitute the lines;
+nested lists of sources are flattened.
+&cv-LINESEPARATOR; is used to separate the strings.
+
+If present, the &cv-SUBST_DICT; construction variable
+is used to modify the strings before they are written;
+see the &b-Substfile; description for details.
+
+The prefix and suffix specified by the &cv-TEXTFILEPREFIX;
+and &cv-TEXTFILESUFFIX; construction variables
+(the null string and <filename>.txt</filename> by default, respectively)
+are automatically added to the target if they are not already present.
+Examples:
+
+<example>
+# builds/writes foo.txt
+env.Textfile(target = 'foo.txt', source = ['Goethe', 42, 'Schiller'])
+
+# builds/writes bar.txt
+env.Textfile(target = 'bar',
+ source = ['lalala', 'tanteratei'],
+ LINESEPARATOR='|*')
+
+# nested lists are flattened automatically
+env.Textfile(target = 'blob',
+ source = ['lalala', ['Goethe', 42 'Schiller'], 'tanteratei'])
+
+# files may be used as input by wraping them in File()
+env.Textfile(target = 'concat', # concatenate files with a marker between
+ source = [File('concat1'), File('concat2')],
+ LINESEPARATOR = '====================\n')
+
+Results are:
+foo.txt
+ ....8&lt;----
+ Goethe
+ 42
+ Schiller
+ ....8&lt;---- (no linefeed at the end)
+
+bar.txt:
+ ....8&lt;----
+ lalala|*tanteratei
+ ....8&lt;---- (no linefeed at the end)
+
+blob.txt
+ ....8&lt;----
+ lalala
+ Goethe
+ 42
+ Schiller
+ tanteratei
+ ....8&lt;---- (no linefeed at the end)
+</example>
+</summary>
+</builder>
+
+<builder name="Substfile">
+<summary>
+The &b-Substfile; builder generates a single text file
+by concatenating the source files.
+Nested lists of sources are flattened.
+&cv-LINESEPARATOR; is used to separate the source files;
+see the description of &b-Textfile; for details.
+
+If a single source file is present with an <filename>.in</filename> suffix,
+the suffix is stripped and the remainder is used as the default target name.
+
+The prefix and suffix specified by the &cv-SUBSTFILEPREFIX;
+and &cv-SUBSTFILESUFFIX; construction variables
+(the null string by default in both cases)
+are automatically added to the target if they are not already present.
+
+If a construction variable named &cv-SUBST_DICT; is present,
+it may be either a Python dictionary or a sequence of (key,value) tuples.
+If the former,
+the dictionary is converted into a list of tuples in an arbitrary order,
+so if one key is a prefix of another key
+or if one substitution could be further expanded by another subsitition,
+it is unpredictible whether the expansion will occur.
+
+Any occurences in the source of a key
+are replaced by the corresponding value,
+which may be a Python callable function or a string.
+If a value is a function,
+it is first called (with no arguments) to produce a string.
+The string is <emphasis>subst</emphasis>-expanded
+and the result replaces the key.
+
+<example>
+env = Environment(tools = ['default', 'textfile'])
+
+env['prefix'] = '/usr/bin'
+script_dict = {'@prefix@': '/bin', @exec_prefix@: '$prefix'}
+env.Substfile('script.in', SUBST_DICT = script_dict)
+
+conf_dict = {'%VERSION%': '1.2.3', '%BASE%': 'MyProg'}
+env.Substfile('config.h.in', conf_dict, SUBST_DICT = conf_dict)
+
+# UNPREDICTABLE - one key is a prefix of another
+bad_foo = {'$foo': '$foo', '$foobar': '$foobar'}
+env.Substfile('foo.in', SUBST_DICT = bad_foo)
+
+# PREDICTABLE - keys are applied longest first
+good_foo = [('$foobar', '$foobar'), ('$foo', '$foo')]
+env.Substfile('foo.in', SUBST_DICT = good_foo)
+
+# UNPREDICTABLE - one substitution could be futher expanded
+bad_bar = {'@bar@': '@soap@', '@soap@': 'lye'}
+env.Substfile('bar.in', SUBST_DICT = bad_bar)
+
+# PREDICTABLE - substitutions are expanded in order
+good_bar = (('@bar@', '@soap@'), ('@soap@', 'lye'))
+env.Substfile('bar.in', SUBST_DICT = good_bar)
+
+# the SUBST_DICT may be in common (and not an override)
+substutions = {}
+subst = Environment(tools = ['textfile', SUBST_DICT = substitutions)
+substitutions['@foo@'] = 'foo'
+subst['SUBST_DICT']['@bar@'] = 'bar'
+subst.Substfile('pgm1.c', [Value('#include "@foo@.h"'),
+ Value('#include "@bar@.h"'),
+ "common.in",
+ "pgm1.in"
+ ])
+subst.Substfile('pgm2.c', [Value('#include "@foo@.h"'),
+ Value('#include "@bar@.h"'),
+ "common.in",
+ "pgm2.in"
+ ])
+
+</example>
+</summary>
+</builder>
+
+<cvar name="LINESEPARATOR">
+<summary>
+The separator used by the &b-Substfile; and &b-Textfile; builders.
+This value is used between sources when constructing the target.
+It defaults to the current system line separator.
+</summary>
+</cvar>
+
+<cvar name="SUBST_DICT">
+<summary>
+The dictionary used by the &b-Substfile; or &b-Textfile; builders
+for substitution values.
+It can be anything acceptable to the dict() constructor,
+so in addition to a dictionary,
+lists of tuples are also acceptable.
+</summary>
+</cvar>
+
+<cvar name="SUBSTFILEPREFIX">
+<summary>
+The prefix used for &b-Substfile; file names,
+the null string by default.
+</summary>
+</cvar>
+
+<cvar name="SUBSTFILESUFFIX">
+<summary>
+The suffix used for &b-Substfile; file names,
+the null string by default.
+</summary>
+</cvar>
+
+<cvar name="TEXTFILEPREFIX">
+<summary>
+The prefix used for &b-Textfile; file names,
+the null string by default.
+</summary>
+</cvar>
+
+<cvar name="TEXTFILESUFFIX">
+<summary>
+The suffix used for &b-Textfile; file names;
+<filename>.txt</filename> by default.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/tlib.py b/src/engine/SCons/Tool/tlib.py
new file mode 100644
index 0000000..0d6f802
--- /dev/null
+++ b/src/engine/SCons/Tool/tlib.py
@@ -0,0 +1,53 @@
+"""SCons.Tool.tlib
+
+XXX
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/tlib.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Tool
+import SCons.Tool.bcc32
+import SCons.Util
+
+def generate(env):
+ SCons.Tool.bcc32.findIt('tlib', env)
+ """Add Builders and construction variables for ar to an Environment."""
+ SCons.Tool.createStaticLibBuilder(env)
+ env['AR'] = 'tlib'
+ env['ARFLAGS'] = SCons.Util.CLVar('')
+ env['ARCOM'] = '$AR $TARGET $ARFLAGS /a $SOURCES'
+ env['LIBPREFIX'] = ''
+ env['LIBSUFFIX'] = '.lib'
+
+def exists(env):
+ return SCons.Tool.bcc32.findIt('tlib', env)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/tlib.xml b/src/engine/SCons/Tool/tlib.xml
new file mode 100644
index 0000000..fb386ec
--- /dev/null
+++ b/src/engine/SCons/Tool/tlib.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="tlib">
+<summary>
+Sets construction variables for the Borlan
+<application>tib</application> library archiver.
+</summary>
+<sets>
+AR
+ARFLAGS
+ARCOM
+LIBPREFIX
+LIBSUFFIX
+</sets>
+<uses>
+ARCOMSTR
+</uses>
+</tool>
diff --git a/src/engine/SCons/Tool/wix.py b/src/engine/SCons/Tool/wix.py
new file mode 100644
index 0000000..350ae49
--- /dev/null
+++ b/src/engine/SCons/Tool/wix.py
@@ -0,0 +1,100 @@
+"""SCons.Tool.wix
+
+Tool-specific initialization for wix, the Windows Installer XML Tool.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/wix.py 4577 2009/12/27 19:44:43 scons"
+
+import SCons.Builder
+import SCons.Action
+import os
+import string
+
+def generate(env):
+ """Add Builders and construction variables for WiX to an Environment."""
+ if not exists(env):
+ return
+
+ env['WIXCANDLEFLAGS'] = ['-nologo']
+ env['WIXCANDLEINCLUDE'] = []
+ env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}'
+
+ env['WIXLIGHTFLAGS'].append( '-nologo' )
+ env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}"
+
+ object_builder = SCons.Builder.Builder(
+ action = '$WIXCANDLECOM',
+ suffix = '.wxiobj',
+ src_suffix = '.wxs')
+
+ linker_builder = SCons.Builder.Builder(
+ action = '$WIXLIGHTCOM',
+ src_suffix = '.wxiobj',
+ src_builder = object_builder)
+
+ env['BUILDERS']['WiX'] = linker_builder
+
+def exists(env):
+ env['WIXCANDLE'] = 'candle.exe'
+ env['WIXLIGHT'] = 'light.exe'
+
+ # try to find the candle.exe and light.exe tools and
+ # add the install directory to light libpath.
+ #for path in os.environ['PATH'].split(os.pathsep):
+ for path in string.split(os.environ['PATH'], os.pathsep):
+ if not path:
+ continue
+
+ # workaround for some weird python win32 bug.
+ if path[0] == '"' and path[-1:]=='"':
+ path = path[1:-1]
+
+ # normalize the path
+ path = os.path.normpath(path)
+
+ # search for the tools in the PATH environment variable
+ try:
+ if env['WIXCANDLE'] in os.listdir(path) and\
+ env['WIXLIGHT'] in os.listdir(path):
+ env.PrependENVPath('PATH', path)
+ env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ),
+ '-loc',
+ os.path.join( path, 'WixUI_en-us.wxl' ) ]
+ return 1
+ except OSError:
+ pass # ignore this, could be a stale PATH entry.
+
+ return None
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py
new file mode 100644
index 0000000..83636d7
--- /dev/null
+++ b/src/engine/SCons/Tool/yacc.py
@@ -0,0 +1,131 @@
+"""SCons.Tool.yacc
+
+Tool-specific initialization for yacc.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/yacc.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+
+import SCons.Defaults
+import SCons.Tool
+import SCons.Util
+
+YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR")
+
+def _yaccEmitter(target, source, env, ysuf, hsuf):
+ yaccflags = env.subst("$YACCFLAGS", target=target, source=source)
+ flags = SCons.Util.CLVar(yaccflags)
+ targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0]))
+
+ if '.ym' in ysuf: # If using Objective-C
+ target = [targetBase + ".m"] # the extension is ".m".
+
+
+ # If -d is specified on the command line, yacc will emit a .h
+ # or .hpp file with the same name as the .c or .cpp output file.
+ if '-d' in flags:
+ target.append(targetBase + env.subst(hsuf, target=target, source=source))
+
+ # If -g is specified on the command line, yacc will emit a .vcg
+ # file with the same base name as the .y, .yacc, .ym or .yy file.
+ if "-g" in flags:
+ base, ext = os.path.splitext(SCons.Util.to_String(source[0]))
+ target.append(base + env.subst("$YACCVCGFILESUFFIX"))
+
+ # With --defines and --graph, the name of the file is totally defined
+ # in the options.
+ fileGenOptions = ["--defines=", "--graph="]
+ for option in flags:
+ for fileGenOption in fileGenOptions:
+ l = len(fileGenOption)
+ if option[:l] == fileGenOption:
+ # A file generating option is present, so add the file
+ # name to the list of targets.
+ fileName = string.strip(option[l:])
+ target.append(fileName)
+
+ return (target, source)
+
+def yEmitter(target, source, env):
+ return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX')
+
+def ymEmitter(target, source, env):
+ return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX')
+
+def yyEmitter(target, source, env):
+ return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX')
+
+def generate(env):
+ """Add Builders and construction variables for yacc to an Environment."""
+ c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
+
+ # C
+ c_file.add_action('.y', YaccAction)
+ c_file.add_emitter('.y', yEmitter)
+
+ c_file.add_action('.yacc', YaccAction)
+ c_file.add_emitter('.yacc', yEmitter)
+
+ # Objective-C
+ c_file.add_action('.ym', YaccAction)
+ c_file.add_emitter('.ym', ymEmitter)
+
+ # C++
+ cxx_file.add_action('.yy', YaccAction)
+ cxx_file.add_emitter('.yy', yyEmitter)
+
+ env['YACC'] = env.Detect('bison') or 'yacc'
+ env['YACCFLAGS'] = SCons.Util.CLVar('')
+ env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES'
+ env['YACCHFILESUFFIX'] = '.h'
+
+ # Apparently, OS X now creates file.hpp like everybody else
+ # I have no idea when it changed; it was fixed in 10.4
+ #if env['PLATFORM'] == 'darwin':
+ # # Bison on Mac OS X just appends ".h" to the generated target .cc
+ # # or .cpp file name. Hooray for delayed expansion of variables.
+ # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h'
+ #else:
+ # env['YACCHXXFILESUFFIX'] = '.hpp'
+ env['YACCHXXFILESUFFIX'] = '.hpp'
+
+ env['YACCVCGFILESUFFIX'] = '.vcg'
+
+def exists(env):
+ return env.Detect(['bison', 'yacc'])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/yacc.xml b/src/engine/SCons/Tool/yacc.xml
new file mode 100644
index 0000000..456c379
--- /dev/null
+++ b/src/engine/SCons/Tool/yacc.xml
@@ -0,0 +1,115 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="yacc">
+<summary>
+Sets construction variables for the &yacc; parse generator.
+</summary>
+<sets>
+YACC
+YACCFLAGS
+YACCCOM
+YACCHFILESUFFIX
+YACCHXXFILESUFFIX
+YACCVCGFILESUFFIX
+</sets>
+<uses>
+YACCCOMSTR
+</uses>
+</tool>
+
+<cvar name="YACC">
+<summary>
+The parser generator.
+</summary>
+</cvar>
+
+<cvar name="YACCCOM">
+<summary>
+The command line used to call the parser generator
+to generate a source file.
+</summary>
+</cvar>
+
+<cvar name="YACCCOMSTR">
+<summary>
+The string displayed when generating a source file
+using the parser generator.
+If this is not set, then &cv-link-YACCCOM; (the command line) is displayed.
+
+<example>
+env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES")
+</example>
+</summary>
+</cvar>
+
+<cvar name="YACCFLAGS">
+<summary>
+General options passed to the parser generator.
+If &cv-link-YACCFLAGS; contains a <option>-d</option> option,
+SCons assumes that the call will also create a .h file
+(if the yacc source file ends in a .y suffix)
+or a .hpp file
+(if the yacc source file ends in a .yy suffix)
+</summary>
+</cvar>
+
+<cvar name="YACCHFILESUFFIX">
+<summary>
+The suffix of the C
+header file generated by the parser generator
+when the
+<option>-d</option>
+option is used.
+Note that setting this variable does not cause
+the parser generator to generate a header
+file with the specified suffix,
+it exists to allow you to specify
+what suffix the parser generator will use of its own accord.
+The default value is
+<filename>.h</filename>.
+</summary>
+</cvar>
+
+<cvar name="YACCHXXFILESUFFIX">
+<summary>
+The suffix of the C++
+header file generated by the parser generator
+when the
+<option>-d</option>
+option is used.
+Note that setting this variable does not cause
+the parser generator to generate a header
+file with the specified suffix,
+it exists to allow you to specify
+what suffix the parser generator will use of its own accord.
+The default value is
+<filename>.hpp</filename>,
+except on Mac OS X,
+where the default is
+<filename>${TARGET.suffix}.h</filename>.
+because the default &bison; parser generator just
+appends <filename>.h</filename>
+to the name of the generated C++ file.
+</summary>
+</cvar>
+
+<cvar name="YACCVCGFILESUFFIX">
+<summary>
+The suffix of the file
+containing the VCG grammar automaton definition
+when the
+<option>--graph=</option>
+option is used.
+Note that setting this variable does not cause
+the parser generator to generate a VCG
+file with the specified suffix,
+it exists to allow you to specify
+what suffix the parser generator will use of its own accord.
+The default value is
+<filename>.vcg</filename>.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Tool/zip.py b/src/engine/SCons/Tool/zip.py
new file mode 100644
index 0000000..1b31ad9
--- /dev/null
+++ b/src/engine/SCons/Tool/zip.py
@@ -0,0 +1,100 @@
+"""SCons.Tool.zip
+
+Tool-specific initialization for zip.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Tool/zip.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+
+import SCons.Builder
+import SCons.Defaults
+import SCons.Node.FS
+import SCons.Util
+
+try:
+ import zipfile
+ internal_zip = 1
+except ImportError:
+ internal_zip = 0
+
+if internal_zip:
+ zipcompression = zipfile.ZIP_DEFLATED
+ def zip(target, source, env):
+ def visit(arg, dirname, names):
+ for name in names:
+ path = os.path.join(dirname, name)
+ if os.path.isfile(path):
+ arg.write(path)
+ compression = env.get('ZIPCOMPRESSION', 0)
+ zf = zipfile.ZipFile(str(target[0]), 'w', compression)
+ for s in source:
+ if s.isdir():
+ os.path.walk(str(s), visit, zf)
+ else:
+ zf.write(str(s))
+ zf.close()
+else:
+ zipcompression = 0
+ zip = "$ZIP $ZIPFLAGS ${TARGET.abspath} $SOURCES"
+
+
+zipAction = SCons.Action.Action(zip, varlist=['ZIPCOMPRESSION'])
+
+ZipBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'),
+ source_factory = SCons.Node.FS.Entry,
+ source_scanner = SCons.Defaults.DirScanner,
+ suffix = '$ZIPSUFFIX',
+ multi = 1)
+
+
+def generate(env):
+ """Add Builders and construction variables for zip to an Environment."""
+ try:
+ bld = env['BUILDERS']['Zip']
+ except KeyError:
+ bld = ZipBuilder
+ env['BUILDERS']['Zip'] = bld
+
+ env['ZIP'] = 'zip'
+ env['ZIPFLAGS'] = SCons.Util.CLVar('')
+ env['ZIPCOM'] = zipAction
+ env['ZIPCOMPRESSION'] = zipcompression
+ env['ZIPSUFFIX'] = '.zip'
+
+def exists(env):
+ return internal_zip or env.Detect('zip')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Tool/zip.xml b/src/engine/SCons/Tool/zip.xml
new file mode 100644
index 0000000..b08b5f6
--- /dev/null
+++ b/src/engine/SCons/Tool/zip.xml
@@ -0,0 +1,110 @@
+<!--
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="zip">
+<summary>
+Sets construction variables for the &zip; archiver.
+</summary>
+<sets>
+ZIP
+ZIPFLAGS
+ZIPCOM
+ZIPCOMPRESSION
+ZIPSUFFIX
+</sets>
+<uses>
+ZIPCOMSTR
+</uses>
+</tool>
+
+<builder name="Zip">
+<summary>
+Builds a zip archive of the specified files
+and/or directories.
+Unlike most builder methods,
+the
+&b-Zip;
+builder method may be called multiple times
+for a given target;
+each additional call
+adds to the list of entries
+that will be built into the archive.
+Any source directories will
+be scanned for changes to
+any on-disk files,
+regardless of whether or not
+&scons;
+knows about them from other Builder or function calls.
+
+<example>
+env.Zip('src.zip', 'src')
+
+# Create the stuff.zip file.
+env.Zip('stuff', ['subdir1', 'subdir2'])
+# Also add "another" to the stuff.tar file.
+env.Zip('stuff', 'another')
+</example>
+</summary>
+</builder>
+
+<cvar name="ZIP">
+<summary>
+The zip compression and file packaging utility.
+</summary>
+</cvar>
+
+<cvar name="ZIPCOM">
+<summary>
+The command line used to call the zip utility,
+or the internal Python function used to create a
+zip archive.
+</summary>
+</cvar>
+
+<cvar name="ZIPCOMSTR">
+<summary>
+The string displayed when archiving files
+using the zip utility.
+If this is not set, then &cv-link-ZIPCOM;
+(the command line or internal Python function) is displayed.
+
+<example>
+env = Environment(ZIPCOMSTR = "Zipping $TARGET")
+</example>
+</summary>
+</cvar>
+
+<cvar name="ZIPCOMPRESSION">
+<summary>
+The
+<varname>compression</varname>
+flag
+from the Python
+<filename>zipfile</filename>
+module used by the internal Python function
+to control whether the zip archive
+is compressed or not.
+The default value is
+<literal>zipfile.ZIP_DEFLATED</literal>,
+which creates a compressed zip archive.
+This value has no effect when using Python 1.5.2
+or if the
+<literal>zipfile</literal>
+module is otherwise unavailable.
+</summary>
+</cvar>
+
+<cvar name="ZIPFLAGS">
+<summary>
+General options passed to the zip utility.
+</summary>
+</cvar>
+
+<cvar name="ZIPSUFFIX">
+<summary>
+The suffix used for zip file names.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
new file mode 100644
index 0000000..c7ebf3e
--- /dev/null
+++ b/src/engine/SCons/Util.py
@@ -0,0 +1,1645 @@
+"""SCons.Util
+
+Various utility functions go here.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Util.py 4577 2009/12/27 19:44:43 scons"
+
+import copy
+import os
+import os.path
+import re
+import string
+import sys
+import types
+
+from UserDict import UserDict
+from UserList import UserList
+from UserString import UserString
+
+# Don't "from types import ..." these because we need to get at the
+# types module later to look for UnicodeType.
+DictType = types.DictType
+InstanceType = types.InstanceType
+ListType = types.ListType
+StringType = types.StringType
+TupleType = types.TupleType
+
+def dictify(keys, values, result={}):
+ for k, v in zip(keys, values):
+ result[k] = v
+ return result
+
+_altsep = os.altsep
+if _altsep is None and sys.platform == 'win32':
+ # My ActivePython 2.0.1 doesn't set os.altsep! What gives?
+ _altsep = '/'
+if _altsep:
+ def rightmost_separator(path, sep, _altsep=_altsep):
+ rfind = string.rfind
+ return max(rfind(path, sep), rfind(path, _altsep))
+else:
+ rightmost_separator = string.rfind
+
+# First two from the Python Cookbook, just for completeness.
+# (Yeah, yeah, YAGNI...)
+def containsAny(str, set):
+ """Check whether sequence str contains ANY of the items in set."""
+ for c in set:
+ if c in str: return 1
+ return 0
+
+def containsAll(str, set):
+ """Check whether sequence str contains ALL of the items in set."""
+ for c in set:
+ if c not in str: return 0
+ return 1
+
+def containsOnly(str, set):
+ """Check whether sequence str contains ONLY items in set."""
+ for c in str:
+ if c not in set: return 0
+ return 1
+
+def splitext(path):
+ "Same as os.path.splitext() but faster."
+ sep = rightmost_separator(path, os.sep)
+ dot = string.rfind(path, '.')
+ # An ext is only real if it has at least one non-digit char
+ if dot > sep and not containsOnly(path[dot:], "0123456789."):
+ return path[:dot],path[dot:]
+ else:
+ return path,""
+
+def updrive(path):
+ """
+ Make the drive letter (if any) upper case.
+ This is useful because Windows is inconsitent on the case
+ of the drive letter, which can cause inconsistencies when
+ calculating command signatures.
+ """
+ drive, rest = os.path.splitdrive(path)
+ if drive:
+ path = string.upper(drive) + rest
+ return path
+
+class NodeList(UserList):
+ """This class is almost exactly like a regular list of Nodes
+ (actually it can hold any object), with one important difference.
+ If you try to get an attribute from this list, it will return that
+ attribute from every item in the list. For example:
+
+ >>> someList = NodeList([ ' foo ', ' bar ' ])
+ >>> someList.strip()
+ [ 'foo', 'bar' ]
+ """
+ def __nonzero__(self):
+ return len(self.data) != 0
+
+ def __str__(self):
+ return string.join(map(str, self.data))
+
+ def __iter__(self):
+ return iter(self.data)
+
+ def __call__(self, *args, **kwargs):
+ result = map(lambda x, args=args, kwargs=kwargs: apply(x,
+ args,
+ kwargs),
+ self.data)
+ return self.__class__(result)
+
+ def __getattr__(self, name):
+ result = map(lambda x, n=name: getattr(x, n), self.data)
+ return self.__class__(result)
+
+
+_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
+
+def get_environment_var(varstr):
+ """Given a string, first determine if it looks like a reference
+ to a single environment variable, like "$FOO" or "${FOO}".
+ If so, return that variable with no decorations ("FOO").
+ If not, return None."""
+ mo=_get_env_var.match(to_String(varstr))
+ if mo:
+ var = mo.group(1)
+ if var[0] == '{':
+ return var[1:-1]
+ else:
+ return var
+ else:
+ return None
+
+class DisplayEngine:
+ def __init__(self):
+ self.__call__ = self.print_it
+
+ def print_it(self, text, append_newline=1):
+ if append_newline: text = text + '\n'
+ try:
+ sys.stdout.write(text)
+ except IOError:
+ # Stdout might be connected to a pipe that has been closed
+ # by now. The most likely reason for the pipe being closed
+ # is that the user has press ctrl-c. It this is the case,
+ # then SCons is currently shutdown. We therefore ignore
+ # IOError's here so that SCons can continue and shutdown
+ # properly so that the .sconsign is correctly written
+ # before SCons exits.
+ pass
+
+ def dont_print(self, text, append_newline=1):
+ pass
+
+ def set_mode(self, mode):
+ if mode:
+ self.__call__ = self.print_it
+ else:
+ self.__call__ = self.dont_print
+
+def render_tree(root, child_func, prune=0, margin=[0], visited={}):
+ """
+ Render a tree of nodes into an ASCII tree view.
+ root - the root node of the tree
+ child_func - the function called to get the children of a node
+ prune - don't visit the same node twice
+ margin - the format of the left margin to use for children of root.
+ 1 results in a pipe, and 0 results in no pipe.
+ visited - a dictionary of visited nodes in the current branch if not prune,
+ or in the whole tree if prune.
+ """
+
+ rname = str(root)
+
+ children = child_func(root)
+ retval = ""
+ for pipe in margin[:-1]:
+ if pipe:
+ retval = retval + "| "
+ else:
+ retval = retval + " "
+
+ if visited.has_key(rname):
+ return retval + "+-[" + rname + "]\n"
+
+ retval = retval + "+-" + rname + "\n"
+ if not prune:
+ visited = copy.copy(visited)
+ visited[rname] = 1
+
+ for i in range(len(children)):
+ margin.append(i<len(children)-1)
+ retval = retval + render_tree(children[i], child_func, prune, margin, visited
+)
+ margin.pop()
+
+ return retval
+
+IDX = lambda N: N and 1 or 0
+
+def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
+ """
+ Print a tree of nodes. This is like render_tree, except it prints
+ lines directly instead of creating a string representation in memory,
+ so that huge trees can be printed.
+
+ root - the root node of the tree
+ child_func - the function called to get the children of a node
+ prune - don't visit the same node twice
+ showtags - print status information to the left of each node line
+ margin - the format of the left margin to use for children of root.
+ 1 results in a pipe, and 0 results in no pipe.
+ visited - a dictionary of visited nodes in the current branch if not prune,
+ or in the whole tree if prune.
+ """
+
+ rname = str(root)
+
+ if showtags:
+
+ if showtags == 2:
+ print ' E = exists'
+ print ' R = exists in repository only'
+ print ' b = implicit builder'
+ print ' B = explicit builder'
+ print ' S = side effect'
+ print ' P = precious'
+ print ' A = always build'
+ print ' C = current'
+ print ' N = no clean'
+ print ' H = no cache'
+ print ''
+
+ tags = ['[']
+ tags.append(' E'[IDX(root.exists())])
+ tags.append(' R'[IDX(root.rexists() and not root.exists())])
+ tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
+ [0,2][IDX(root.has_builder())]])
+ tags.append(' S'[IDX(root.side_effect)])
+ tags.append(' P'[IDX(root.precious)])
+ tags.append(' A'[IDX(root.always_build)])
+ tags.append(' C'[IDX(root.is_up_to_date())])
+ tags.append(' N'[IDX(root.noclean)])
+ tags.append(' H'[IDX(root.nocache)])
+ tags.append(']')
+
+ else:
+ tags = []
+
+ def MMM(m):
+ return [" ","| "][m]
+ margins = map(MMM, margin[:-1])
+
+ children = child_func(root)
+
+ if prune and visited.has_key(rname) and children:
+ print string.join(tags + margins + ['+-[', rname, ']'], '')
+ return
+
+ print string.join(tags + margins + ['+-', rname], '')
+
+ visited[rname] = 1
+
+ if children:
+ margin.append(1)
+ idx = IDX(showtags)
+ for C in children[:-1]:
+ print_tree(C, child_func, prune, idx, margin, visited)
+ margin[-1] = 0
+ print_tree(children[-1], child_func, prune, idx, margin, visited)
+ margin.pop()
+
+
+
+# Functions for deciding if things are like various types, mainly to
+# handle UserDict, UserList and UserString like their underlying types.
+#
+# Yes, all of this manual testing breaks polymorphism, and the real
+# Pythonic way to do all of this would be to just try it and handle the
+# exception, but handling the exception when it's not the right type is
+# often too slow.
+
+try:
+ class mystr(str):
+ pass
+except TypeError:
+ # An older Python version without new-style classes.
+ #
+ # The actual implementations here have been selected after timings
+ # coded up in in bench/is_types.py (from the SCons source tree,
+ # see the scons-src distribution), mostly against Python 1.5.2.
+ # Key results from those timings:
+ #
+ # -- Storing the type of the object in a variable (t = type(obj))
+ # slows down the case where it's a native type and the first
+ # comparison will match, but nicely speeds up the case where
+ # it's a different native type. Since that's going to be
+ # common, it's a good tradeoff.
+ #
+ # -- The data show that calling isinstance() on an object that's
+ # a native type (dict, list or string) is expensive enough
+ # that checking up front for whether the object is of type
+ # InstanceType is a pretty big win, even though it does slow
+ # down the case where it really *is* an object instance a
+ # little bit.
+ def is_Dict(obj):
+ t = type(obj)
+ return t is DictType or \
+ (t is InstanceType and isinstance(obj, UserDict))
+
+ def is_List(obj):
+ t = type(obj)
+ return t is ListType \
+ or (t is InstanceType and isinstance(obj, UserList))
+
+ def is_Sequence(obj):
+ t = type(obj)
+ return t is ListType \
+ or t is TupleType \
+ or (t is InstanceType and isinstance(obj, UserList))
+
+ def is_Tuple(obj):
+ t = type(obj)
+ return t is TupleType
+
+ if hasattr(types, 'UnicodeType'):
+ def is_String(obj):
+ t = type(obj)
+ return t is StringType \
+ or t is UnicodeType \
+ or (t is InstanceType and isinstance(obj, UserString))
+ else:
+ def is_String(obj):
+ t = type(obj)
+ return t is StringType \
+ or (t is InstanceType and isinstance(obj, UserString))
+
+ def is_Scalar(obj):
+ return is_String(obj) or not is_Sequence(obj)
+
+ def flatten(obj, result=None):
+ """Flatten a sequence to a non-nested list.
+
+ Flatten() converts either a single scalar or a nested sequence
+ to a non-nested list. Note that flatten() considers strings
+ to be scalars instead of sequences like Python would.
+ """
+ if is_Scalar(obj):
+ return [obj]
+ if result is None:
+ result = []
+ for item in obj:
+ if is_Scalar(item):
+ result.append(item)
+ else:
+ flatten_sequence(item, result)
+ return result
+
+ def flatten_sequence(sequence, result=None):
+ """Flatten a sequence to a non-nested list.
+
+ Same as flatten(), but it does not handle the single scalar
+ case. This is slightly more efficient when one knows that
+ the sequence to flatten can not be a scalar.
+ """
+ if result is None:
+ result = []
+ for item in sequence:
+ if is_Scalar(item):
+ result.append(item)
+ else:
+ flatten_sequence(item, result)
+ return result
+
+ #
+ # Generic convert-to-string functions that abstract away whether or
+ # not the Python we're executing has Unicode support. The wrapper
+ # to_String_for_signature() will use a for_signature() method if the
+ # specified object has one.
+ #
+ if hasattr(types, 'UnicodeType'):
+ UnicodeType = types.UnicodeType
+ def to_String(s):
+ if isinstance(s, UserString):
+ t = type(s.data)
+ else:
+ t = type(s)
+ if t is UnicodeType:
+ return unicode(s)
+ else:
+ return str(s)
+ else:
+ to_String = str
+
+ def to_String_for_signature(obj):
+ try:
+ f = obj.for_signature
+ except AttributeError:
+ return to_String_for_subst(obj)
+ else:
+ return f()
+
+ def to_String_for_subst(s):
+ if is_Sequence( s ):
+ return string.join( map(to_String_for_subst, s) )
+
+ return to_String( s )
+
+else:
+ # A modern Python version with new-style classes, so we can just use
+ # isinstance().
+ #
+ # We are using the following trick to speed-up these
+ # functions. Default arguments are used to take a snapshot of the
+ # the global functions and constants used by these functions. This
+ # transforms accesses to global variable into local variables
+ # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
+
+ DictTypes = (dict, UserDict)
+ ListTypes = (list, UserList)
+ SequenceTypes = (list, tuple, UserList)
+
+ # Empirically, Python versions with new-style classes all have
+ # unicode.
+ #
+ # Note that profiling data shows a speed-up when comparing
+ # explicitely with str and unicode instead of simply comparing
+ # with basestring. (at least on Python 2.5.1)
+ StringTypes = (str, unicode, UserString)
+
+ # Empirically, it is faster to check explicitely for str and
+ # unicode than for basestring.
+ BaseStringTypes = (str, unicode)
+
+ def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
+ return isinstance(obj, DictTypes)
+
+ def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
+ return isinstance(obj, ListTypes)
+
+ def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
+ return isinstance(obj, SequenceTypes)
+
+ def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
+ return isinstance(obj, tuple)
+
+ def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
+ return isinstance(obj, StringTypes)
+
+ def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
+ # Profiling shows that there is an impressive speed-up of 2x
+ # when explicitely checking for strings instead of just not
+ # sequence when the argument (i.e. obj) is already a string.
+ # But, if obj is a not string than it is twice as fast to
+ # check only for 'not sequence'. The following code therefore
+ # assumes that the obj argument is a string must of the time.
+ return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
+
+ def do_flatten(sequence, result, isinstance=isinstance,
+ StringTypes=StringTypes, SequenceTypes=SequenceTypes):
+ for item in sequence:
+ if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
+ result.append(item)
+ else:
+ do_flatten(item, result)
+
+ def flatten(obj, isinstance=isinstance, StringTypes=StringTypes,
+ SequenceTypes=SequenceTypes, do_flatten=do_flatten):
+ """Flatten a sequence to a non-nested list.
+
+ Flatten() converts either a single scalar or a nested sequence
+ to a non-nested list. Note that flatten() considers strings
+ to be scalars instead of sequences like Python would.
+ """
+ if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes):
+ return [obj]
+ result = []
+ for item in obj:
+ if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
+ result.append(item)
+ else:
+ do_flatten(item, result)
+ return result
+
+ def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes,
+ SequenceTypes=SequenceTypes, do_flatten=do_flatten):
+ """Flatten a sequence to a non-nested list.
+
+ Same as flatten(), but it does not handle the single scalar
+ case. This is slightly more efficient when one knows that
+ the sequence to flatten can not be a scalar.
+ """
+ result = []
+ for item in sequence:
+ if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
+ result.append(item)
+ else:
+ do_flatten(item, result)
+ return result
+
+
+ #
+ # Generic convert-to-string functions that abstract away whether or
+ # not the Python we're executing has Unicode support. The wrapper
+ # to_String_for_signature() will use a for_signature() method if the
+ # specified object has one.
+ #
+ def to_String(s,
+ isinstance=isinstance, str=str,
+ UserString=UserString, BaseStringTypes=BaseStringTypes):
+ if isinstance(s,BaseStringTypes):
+ # Early out when already a string!
+ return s
+ elif isinstance(s, UserString):
+ # s.data can only be either a unicode or a regular
+ # string. Please see the UserString initializer.
+ return s.data
+ else:
+ return str(s)
+
+ def to_String_for_subst(s,
+ isinstance=isinstance, join=string.join, str=str, to_String=to_String,
+ BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes,
+ UserString=UserString):
+
+ # Note that the test cases are sorted by order of probability.
+ if isinstance(s, BaseStringTypes):
+ return s
+ elif isinstance(s, SequenceTypes):
+ l = []
+ for e in s:
+ l.append(to_String_for_subst(e))
+ return join( s )
+ elif isinstance(s, UserString):
+ # s.data can only be either a unicode or a regular
+ # string. Please see the UserString initializer.
+ return s.data
+ else:
+ return str(s)
+
+ def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst,
+ AttributeError=AttributeError):
+ try:
+ f = obj.for_signature
+ except AttributeError:
+ return to_String_for_subst(obj)
+ else:
+ return f()
+
+
+
+# The SCons "semi-deep" copy.
+#
+# This makes separate copies of lists (including UserList objects)
+# dictionaries (including UserDict objects) and tuples, but just copies
+# references to anything else it finds.
+#
+# A special case is any object that has a __semi_deepcopy__() method,
+# which we invoke to create the copy, which is used by the BuilderDict
+# class because of its extra initialization argument.
+#
+# The dispatch table approach used here is a direct rip-off from the
+# normal Python copy module.
+
+_semi_deepcopy_dispatch = d = {}
+
+def _semi_deepcopy_dict(x):
+ copy = {}
+ for key, val in x.items():
+ # The regular Python copy.deepcopy() also deepcopies the key,
+ # as follows:
+ #
+ # copy[semi_deepcopy(key)] = semi_deepcopy(val)
+ #
+ # Doesn't seem like we need to, but we'll comment it just in case.
+ copy[key] = semi_deepcopy(val)
+ return copy
+d[types.DictionaryType] = _semi_deepcopy_dict
+
+def _semi_deepcopy_list(x):
+ return map(semi_deepcopy, x)
+d[types.ListType] = _semi_deepcopy_list
+
+def _semi_deepcopy_tuple(x):
+ return tuple(map(semi_deepcopy, x))
+d[types.TupleType] = _semi_deepcopy_tuple
+
+def _semi_deepcopy_inst(x):
+ if hasattr(x, '__semi_deepcopy__'):
+ return x.__semi_deepcopy__()
+ elif isinstance(x, UserDict):
+ return x.__class__(_semi_deepcopy_dict(x))
+ elif isinstance(x, UserList):
+ return x.__class__(_semi_deepcopy_list(x))
+ else:
+ return x
+d[types.InstanceType] = _semi_deepcopy_inst
+
+def semi_deepcopy(x):
+ copier = _semi_deepcopy_dispatch.get(type(x))
+ if copier:
+ return copier(x)
+ else:
+ return x
+
+
+
+class Proxy:
+ """A simple generic Proxy class, forwarding all calls to
+ subject. So, for the benefit of the python newbie, what does
+ this really mean? Well, it means that you can take an object, let's
+ call it 'objA', and wrap it in this Proxy class, with a statement
+ like this
+
+ proxyObj = Proxy(objA),
+
+ Then, if in the future, you do something like this
+
+ x = proxyObj.var1,
+
+ since Proxy does not have a 'var1' attribute (but presumably objA does),
+ the request actually is equivalent to saying
+
+ x = objA.var1
+
+ Inherit from this class to create a Proxy."""
+
+ def __init__(self, subject):
+ """Wrap an object as a Proxy object"""
+ self.__subject = subject
+
+ def __getattr__(self, name):
+ """Retrieve an attribute from the wrapped object. If the named
+ attribute doesn't exist, AttributeError is raised"""
+ return getattr(self.__subject, name)
+
+ def get(self):
+ """Retrieve the entire wrapped object"""
+ return self.__subject
+
+ def __cmp__(self, other):
+ if issubclass(other.__class__, self.__subject.__class__):
+ return cmp(self.__subject, other)
+ return cmp(self.__dict__, other.__dict__)
+
+# attempt to load the windows registry module:
+can_read_reg = 0
+try:
+ import _winreg
+
+ can_read_reg = 1
+ hkey_mod = _winreg
+
+ RegOpenKeyEx = _winreg.OpenKeyEx
+ RegEnumKey = _winreg.EnumKey
+ RegEnumValue = _winreg.EnumValue
+ RegQueryValueEx = _winreg.QueryValueEx
+ RegError = _winreg.error
+
+except ImportError:
+ try:
+ import win32api
+ import win32con
+ can_read_reg = 1
+ hkey_mod = win32con
+
+ RegOpenKeyEx = win32api.RegOpenKeyEx
+ RegEnumKey = win32api.RegEnumKey
+ RegEnumValue = win32api.RegEnumValue
+ RegQueryValueEx = win32api.RegQueryValueEx
+ RegError = win32api.error
+
+ except ImportError:
+ class _NoError(Exception):
+ pass
+ RegError = _NoError
+
+if can_read_reg:
+ HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
+ HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
+ HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
+ HKEY_USERS = hkey_mod.HKEY_USERS
+
+ def RegGetValue(root, key):
+ """This utility function returns a value in the registry
+ without having to open the key first. Only available on
+ Windows platforms with a version of Python that can read the
+ registry. Returns the same thing as
+ SCons.Util.RegQueryValueEx, except you just specify the entire
+ path to the value, and don't have to bother opening the key
+ first. So:
+
+ Instead of:
+ k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
+ r'SOFTWARE\Microsoft\Windows\CurrentVersion')
+ out = SCons.Util.RegQueryValueEx(k,
+ 'ProgramFilesDir')
+
+ You can write:
+ out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+ r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
+ """
+ # I would use os.path.split here, but it's not a filesystem
+ # path...
+ p = key.rfind('\\') + 1
+ keyp = key[:p-1] # -1 to omit trailing slash
+ val = key[p:]
+ k = RegOpenKeyEx(root, keyp)
+ return RegQueryValueEx(k,val)
+else:
+ try:
+ e = WindowsError
+ except NameError:
+ # Make sure we have a definition of WindowsError so we can
+ # run platform-independent tests of Windows functionality on
+ # platforms other than Windows. (WindowsError is, in fact, an
+ # OSError subclass on Windows.)
+ class WindowsError(OSError):
+ pass
+ import __builtin__
+ __builtin__.WindowsError = WindowsError
+ else:
+ del e
+
+ HKEY_CLASSES_ROOT = None
+ HKEY_LOCAL_MACHINE = None
+ HKEY_CURRENT_USER = None
+ HKEY_USERS = None
+
+ def RegGetValue(root, key):
+ raise WindowsError
+
+ def RegOpenKeyEx(root, key):
+ raise WindowsError
+
+if sys.platform == 'win32':
+
+ def WhereIs(file, path=None, pathext=None, reject=[]):
+ if path is None:
+ try:
+ path = os.environ['PATH']
+ except KeyError:
+ return None
+ if is_String(path):
+ path = string.split(path, os.pathsep)
+ if pathext is None:
+ try:
+ pathext = os.environ['PATHEXT']
+ except KeyError:
+ pathext = '.COM;.EXE;.BAT;.CMD'
+ if is_String(pathext):
+ pathext = string.split(pathext, os.pathsep)
+ for ext in pathext:
+ if string.lower(ext) == string.lower(file[-len(ext):]):
+ pathext = ['']
+ break
+ if not is_List(reject) and not is_Tuple(reject):
+ reject = [reject]
+ for dir in path:
+ f = os.path.join(dir, file)
+ for ext in pathext:
+ fext = f + ext
+ if os.path.isfile(fext):
+ try:
+ reject.index(fext)
+ except ValueError:
+ return os.path.normpath(fext)
+ continue
+ return None
+
+elif os.name == 'os2':
+
+ def WhereIs(file, path=None, pathext=None, reject=[]):
+ if path is None:
+ try:
+ path = os.environ['PATH']
+ except KeyError:
+ return None
+ if is_String(path):
+ path = string.split(path, os.pathsep)
+ if pathext is None:
+ pathext = ['.exe', '.cmd']
+ for ext in pathext:
+ if string.lower(ext) == string.lower(file[-len(ext):]):
+ pathext = ['']
+ break
+ if not is_List(reject) and not is_Tuple(reject):
+ reject = [reject]
+ for dir in path:
+ f = os.path.join(dir, file)
+ for ext in pathext:
+ fext = f + ext
+ if os.path.isfile(fext):
+ try:
+ reject.index(fext)
+ except ValueError:
+ return os.path.normpath(fext)
+ continue
+ return None
+
+else:
+
+ def WhereIs(file, path=None, pathext=None, reject=[]):
+ import stat
+ if path is None:
+ try:
+ path = os.environ['PATH']
+ except KeyError:
+ return None
+ if is_String(path):
+ path = string.split(path, os.pathsep)
+ if not is_List(reject) and not is_Tuple(reject):
+ reject = [reject]
+ for d in path:
+ f = os.path.join(d, file)
+ if os.path.isfile(f):
+ try:
+ st = os.stat(f)
+ except OSError:
+ # os.stat() raises OSError, not IOError if the file
+ # doesn't exist, so in this case we let IOError get
+ # raised so as to not mask possibly serious disk or
+ # network issues.
+ continue
+ if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
+ try:
+ reject.index(f)
+ except ValueError:
+ return os.path.normpath(f)
+ continue
+ return None
+
+def PrependPath(oldpath, newpath, sep = os.pathsep,
+ delete_existing=1, canonicalize=None):
+ """This prepends newpath elements to the given oldpath. Will only
+ add any particular path once (leaving the first one it encounters
+ and ignoring the rest, to preserve path order), and will
+ os.path.normpath and os.path.normcase all paths to help assure
+ this. This can also handle the case where the given old path
+ variable is a list instead of a string, in which case a list will
+ be returned instead of a string.
+
+ Example:
+ Old Path: "/foo/bar:/foo"
+ New Path: "/biz/boom:/foo"
+ Result: "/biz/boom:/foo:/foo/bar"
+
+ If delete_existing is 0, then adding a path that exists will
+ not move it to the beginning; it will stay where it is in the
+ list.
+
+ If canonicalize is not None, it is applied to each element of
+ newpath before use.
+ """
+
+ orig = oldpath
+ is_list = 1
+ paths = orig
+ if not is_List(orig) and not is_Tuple(orig):
+ paths = string.split(paths, sep)
+ is_list = 0
+
+ if is_String(newpath):
+ newpaths = string.split(newpath, sep)
+ elif not is_List(newpath) and not is_Tuple(newpath):
+ newpaths = [ newpath ] # might be a Dir
+ else:
+ newpaths = newpath
+
+ if canonicalize:
+ newpaths=map(canonicalize, newpaths)
+
+ if not delete_existing:
+ # First uniquify the old paths, making sure to
+ # preserve the first instance (in Unix/Linux,
+ # the first one wins), and remembering them in normpaths.
+ # Then insert the new paths at the head of the list
+ # if they're not already in the normpaths list.
+ result = []
+ normpaths = []
+ for path in paths:
+ if not path:
+ continue
+ normpath = os.path.normpath(os.path.normcase(path))
+ if normpath not in normpaths:
+ result.append(path)
+ normpaths.append(normpath)
+ newpaths.reverse() # since we're inserting at the head
+ for path in newpaths:
+ if not path:
+ continue
+ normpath = os.path.normpath(os.path.normcase(path))
+ if normpath not in normpaths:
+ result.insert(0, path)
+ normpaths.append(normpath)
+ paths = result
+
+ else:
+ newpaths = newpaths + paths # prepend new paths
+
+ normpaths = []
+ paths = []
+ # now we add them only if they are unique
+ for path in newpaths:
+ normpath = os.path.normpath(os.path.normcase(path))
+ if path and not normpath in normpaths:
+ paths.append(path)
+ normpaths.append(normpath)
+
+ if is_list:
+ return paths
+ else:
+ return string.join(paths, sep)
+
+def AppendPath(oldpath, newpath, sep = os.pathsep,
+ delete_existing=1, canonicalize=None):
+ """This appends new path elements to the given old path. Will
+ only add any particular path once (leaving the last one it
+ encounters and ignoring the rest, to preserve path order), and
+ will os.path.normpath and os.path.normcase all paths to help
+ assure this. This can also handle the case where the given old
+ path variable is a list instead of a string, in which case a list
+ will be returned instead of a string.
+
+ Example:
+ Old Path: "/foo/bar:/foo"
+ New Path: "/biz/boom:/foo"
+ Result: "/foo/bar:/biz/boom:/foo"
+
+ If delete_existing is 0, then adding a path that exists
+ will not move it to the end; it will stay where it is in the list.
+
+ If canonicalize is not None, it is applied to each element of
+ newpath before use.
+ """
+
+ orig = oldpath
+ is_list = 1
+ paths = orig
+ if not is_List(orig) and not is_Tuple(orig):
+ paths = string.split(paths, sep)
+ is_list = 0
+
+ if is_String(newpath):
+ newpaths = string.split(newpath, sep)
+ elif not is_List(newpath) and not is_Tuple(newpath):
+ newpaths = [ newpath ] # might be a Dir
+ else:
+ newpaths = newpath
+
+ if canonicalize:
+ newpaths=map(canonicalize, newpaths)
+
+ if not delete_existing:
+ # add old paths to result, then
+ # add new paths if not already present
+ # (I thought about using a dict for normpaths for speed,
+ # but it's not clear hashing the strings would be faster
+ # than linear searching these typically short lists.)
+ result = []
+ normpaths = []
+ for path in paths:
+ if not path:
+ continue
+ result.append(path)
+ normpaths.append(os.path.normpath(os.path.normcase(path)))
+ for path in newpaths:
+ if not path:
+ continue
+ normpath = os.path.normpath(os.path.normcase(path))
+ if normpath not in normpaths:
+ result.append(path)
+ normpaths.append(normpath)
+ paths = result
+ else:
+ # start w/ new paths, add old ones if not present,
+ # then reverse.
+ newpaths = paths + newpaths # append new paths
+ newpaths.reverse()
+
+ normpaths = []
+ paths = []
+ # now we add them only if they are unique
+ for path in newpaths:
+ normpath = os.path.normpath(os.path.normcase(path))
+ if path and not normpath in normpaths:
+ paths.append(path)
+ normpaths.append(normpath)
+ paths.reverse()
+
+ if is_list:
+ return paths
+ else:
+ return string.join(paths, sep)
+
+if sys.platform == 'cygwin':
+ def get_native_path(path):
+ """Transforms an absolute path into a native path for the system. In
+ Cygwin, this converts from a Cygwin path to a Windows one."""
+ return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
+else:
+ def get_native_path(path):
+ """Transforms an absolute path into a native path for the system.
+ Non-Cygwin version, just leave the path alone."""
+ return path
+
+display = DisplayEngine()
+
+def Split(arg):
+ if is_List(arg) or is_Tuple(arg):
+ return arg
+ elif is_String(arg):
+ return string.split(arg)
+ else:
+ return [arg]
+
+class CLVar(UserList):
+ """A class for command-line construction variables.
+
+ This is a list that uses Split() to split an initial string along
+ white-space arguments, and similarly to split any strings that get
+ added. This allows us to Do the Right Thing with Append() and
+ Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
+ arg2') regardless of whether a user adds a list or a string to a
+ command-line construction variable.
+ """
+ def __init__(self, seq = []):
+ UserList.__init__(self, Split(seq))
+ def __add__(self, other):
+ return UserList.__add__(self, CLVar(other))
+ def __radd__(self, other):
+ return UserList.__radd__(self, CLVar(other))
+ def __coerce__(self, other):
+ return (self, CLVar(other))
+ def __str__(self):
+ return string.join(self.data)
+
+# A dictionary that preserves the order in which items are added.
+# Submitted by David Benjamin to ActiveState's Python Cookbook web site:
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+# Including fixes/enhancements from the follow-on discussions.
+class OrderedDict(UserDict):
+ def __init__(self, dict = None):
+ self._keys = []
+ UserDict.__init__(self, dict)
+
+ def __delitem__(self, key):
+ UserDict.__delitem__(self, key)
+ self._keys.remove(key)
+
+ def __setitem__(self, key, item):
+ UserDict.__setitem__(self, key, item)
+ if key not in self._keys: self._keys.append(key)
+
+ def clear(self):
+ UserDict.clear(self)
+ self._keys = []
+
+ def copy(self):
+ dict = OrderedDict()
+ dict.update(self)
+ return dict
+
+ def items(self):
+ return zip(self._keys, self.values())
+
+ def keys(self):
+ return self._keys[:]
+
+ def popitem(self):
+ try:
+ key = self._keys[-1]
+ except IndexError:
+ raise KeyError('dictionary is empty')
+
+ val = self[key]
+ del self[key]
+
+ return (key, val)
+
+ def setdefault(self, key, failobj = None):
+ UserDict.setdefault(self, key, failobj)
+ if key not in self._keys: self._keys.append(key)
+
+ def update(self, dict):
+ for (key, val) in dict.items():
+ self.__setitem__(key, val)
+
+ def values(self):
+ return map(self.get, self._keys)
+
+class Selector(OrderedDict):
+ """A callable ordered dictionary that maps file suffixes to
+ dictionary values. We preserve the order in which items are added
+ so that get_suffix() calls always return the first suffix added."""
+ def __call__(self, env, source, ext=None):
+ if ext is None:
+ try:
+ ext = source[0].suffix
+ except IndexError:
+ ext = ""
+ try:
+ return self[ext]
+ except KeyError:
+ # Try to perform Environment substitution on the keys of
+ # the dictionary before giving up.
+ s_dict = {}
+ for (k,v) in self.items():
+ if k is not None:
+ s_k = env.subst(k)
+ if s_dict.has_key(s_k):
+ # We only raise an error when variables point
+ # to the same suffix. If one suffix is literal
+ # and a variable suffix contains this literal,
+ # the literal wins and we don't raise an error.
+ raise KeyError, (s_dict[s_k][0], k, s_k)
+ s_dict[s_k] = (k,v)
+ try:
+ return s_dict[ext][1]
+ except KeyError:
+ try:
+ return self[None]
+ except KeyError:
+ return None
+
+
+if sys.platform == 'cygwin':
+ # On Cygwin, os.path.normcase() lies, so just report back the
+ # fact that the underlying Windows OS is case-insensitive.
+ def case_sensitive_suffixes(s1, s2):
+ return 0
+else:
+ def case_sensitive_suffixes(s1, s2):
+ return (os.path.normcase(s1) != os.path.normcase(s2))
+
+def adjustixes(fname, pre, suf, ensure_suffix=False):
+ if pre:
+ path, fn = os.path.split(os.path.normpath(fname))
+ if fn[:len(pre)] != pre:
+ fname = os.path.join(path, pre + fn)
+ # Only append a suffix if the suffix we're going to add isn't already
+ # there, and if either we've been asked to ensure the specific suffix
+ # is present or there's no suffix on it at all.
+ if suf and fname[-len(suf):] != suf and \
+ (ensure_suffix or not splitext(fname)[1]):
+ fname = fname + suf
+ return fname
+
+
+
+# From Tim Peters,
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
+# ASPN: Python Cookbook: Remove duplicates from a sequence
+# (Also in the printed Python Cookbook.)
+
+def unique(s):
+ """Return a list of the elements in s, but without duplicates.
+
+ For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
+ unique("abcabc") some permutation of ["a", "b", "c"], and
+ unique(([1, 2], [2, 3], [1, 2])) some permutation of
+ [[2, 3], [1, 2]].
+
+ For best speed, all sequence elements should be hashable. Then
+ unique() will usually work in linear time.
+
+ If not possible, the sequence elements should enjoy a total
+ ordering, and if list(s).sort() doesn't raise TypeError it's
+ assumed that they do enjoy a total ordering. Then unique() will
+ usually work in O(N*log2(N)) time.
+
+ If that's not possible either, the sequence elements must support
+ equality-testing. Then unique() will usually work in quadratic
+ time.
+ """
+
+ n = len(s)
+ if n == 0:
+ return []
+
+ # Try using a dict first, as that's the fastest and will usually
+ # work. If it doesn't work, it will usually fail quickly, so it
+ # usually doesn't cost much to *try* it. It requires that all the
+ # sequence elements be hashable, and support equality comparison.
+ u = {}
+ try:
+ for x in s:
+ u[x] = 1
+ except TypeError:
+ pass # move on to the next method
+ else:
+ return u.keys()
+ del u
+
+ # We can't hash all the elements. Second fastest is to sort,
+ # which brings the equal elements together; then duplicates are
+ # easy to weed out in a single pass.
+ # NOTE: Python's list.sort() was designed to be efficient in the
+ # presence of many duplicate elements. This isn't true of all
+ # sort functions in all languages or libraries, so this approach
+ # is more effective in Python than it may be elsewhere.
+ try:
+ t = list(s)
+ t.sort()
+ except TypeError:
+ pass # move on to the next method
+ else:
+ assert n > 0
+ last = t[0]
+ lasti = i = 1
+ while i < n:
+ if t[i] != last:
+ t[lasti] = last = t[i]
+ lasti = lasti + 1
+ i = i + 1
+ return t[:lasti]
+ del t
+
+ # Brute force is all that's left.
+ u = []
+ for x in s:
+ if x not in u:
+ u.append(x)
+ return u
+
+
+
+# From Alex Martelli,
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
+# ASPN: Python Cookbook: Remove duplicates from a sequence
+# First comment, dated 2001/10/13.
+# (Also in the printed Python Cookbook.)
+
+def uniquer(seq, idfun=None):
+ if idfun is None:
+ def idfun(x): return x
+ seen = {}
+ result = []
+ for item in seq:
+ marker = idfun(item)
+ # in old Python versions:
+ # if seen.has_key(marker)
+ # but in new ones:
+ if marker in seen: continue
+ seen[marker] = 1
+ result.append(item)
+ return result
+
+# A more efficient implementation of Alex's uniquer(), this avoids the
+# idfun() argument and function-call overhead by assuming that all
+# items in the sequence are hashable.
+
+def uniquer_hashables(seq):
+ seen = {}
+ result = []
+ for item in seq:
+ #if not item in seen:
+ if not seen.has_key(item):
+ seen[item] = 1
+ result.append(item)
+ return result
+
+
+
+# Much of the logic here was originally based on recipe 4.9 from the
+# Python CookBook, but we had to dumb it way down for Python 1.5.2.
+class LogicalLines:
+
+ def __init__(self, fileobj):
+ self.fileobj = fileobj
+
+ def readline(self):
+ result = []
+ while 1:
+ line = self.fileobj.readline()
+ if not line:
+ break
+ if line[-2:] == '\\\n':
+ result.append(line[:-2])
+ else:
+ result.append(line)
+ break
+ return string.join(result, '')
+
+ def readlines(self):
+ result = []
+ while 1:
+ line = self.readline()
+ if not line:
+ break
+ result.append(line)
+ return result
+
+
+
+class UniqueList(UserList):
+ def __init__(self, seq = []):
+ UserList.__init__(self, seq)
+ self.unique = True
+ def __make_unique(self):
+ if not self.unique:
+ self.data = uniquer_hashables(self.data)
+ self.unique = True
+ def __lt__(self, other):
+ self.__make_unique()
+ return UserList.__lt__(self, other)
+ def __le__(self, other):
+ self.__make_unique()
+ return UserList.__le__(self, other)
+ def __eq__(self, other):
+ self.__make_unique()
+ return UserList.__eq__(self, other)
+ def __ne__(self, other):
+ self.__make_unique()
+ return UserList.__ne__(self, other)
+ def __gt__(self, other):
+ self.__make_unique()
+ return UserList.__gt__(self, other)
+ def __ge__(self, other):
+ self.__make_unique()
+ return UserList.__ge__(self, other)
+ def __cmp__(self, other):
+ self.__make_unique()
+ return UserList.__cmp__(self, other)
+ def __len__(self):
+ self.__make_unique()
+ return UserList.__len__(self)
+ def __getitem__(self, i):
+ self.__make_unique()
+ return UserList.__getitem__(self, i)
+ def __setitem__(self, i, item):
+ UserList.__setitem__(self, i, item)
+ self.unique = False
+ def __getslice__(self, i, j):
+ self.__make_unique()
+ return UserList.__getslice__(self, i, j)
+ def __setslice__(self, i, j, other):
+ UserList.__setslice__(self, i, j, other)
+ self.unique = False
+ def __add__(self, other):
+ result = UserList.__add__(self, other)
+ result.unique = False
+ return result
+ def __radd__(self, other):
+ result = UserList.__radd__(self, other)
+ result.unique = False
+ return result
+ def __iadd__(self, other):
+ result = UserList.__iadd__(self, other)
+ result.unique = False
+ return result
+ def __mul__(self, other):
+ result = UserList.__mul__(self, other)
+ result.unique = False
+ return result
+ def __rmul__(self, other):
+ result = UserList.__rmul__(self, other)
+ result.unique = False
+ return result
+ def __imul__(self, other):
+ result = UserList.__imul__(self, other)
+ result.unique = False
+ return result
+ def append(self, item):
+ UserList.append(self, item)
+ self.unique = False
+ def insert(self, i):
+ UserList.insert(self, i)
+ self.unique = False
+ def count(self, item):
+ self.__make_unique()
+ return UserList.count(self, item)
+ def index(self, item):
+ self.__make_unique()
+ return UserList.index(self, item)
+ def reverse(self):
+ self.__make_unique()
+ UserList.reverse(self)
+ def sort(self, *args, **kwds):
+ self.__make_unique()
+ #return UserList.sort(self, *args, **kwds)
+ return apply(UserList.sort, (self,)+args, kwds)
+ def extend(self, other):
+ UserList.extend(self, other)
+ self.unique = False
+
+
+
+class Unbuffered:
+ """
+ A proxy class that wraps a file object, flushing after every write,
+ and delegating everything else to the wrapped object.
+ """
+ def __init__(self, file):
+ self.file = file
+ def write(self, arg):
+ try:
+ self.file.write(arg)
+ self.file.flush()
+ except IOError:
+ # Stdout might be connected to a pipe that has been closed
+ # by now. The most likely reason for the pipe being closed
+ # is that the user has press ctrl-c. It this is the case,
+ # then SCons is currently shutdown. We therefore ignore
+ # IOError's here so that SCons can continue and shutdown
+ # properly so that the .sconsign is correctly written
+ # before SCons exits.
+ pass
+ def __getattr__(self, attr):
+ return getattr(self.file, attr)
+
+def make_path_relative(path):
+ """ makes an absolute path name to a relative pathname.
+ """
+ if os.path.isabs(path):
+ drive_s,path = os.path.splitdrive(path)
+
+ import re
+ if not drive_s:
+ path=re.compile("/*(.*)").findall(path)[0]
+ else:
+ path=path[1:]
+
+ assert( not os.path.isabs( path ) ), path
+ return path
+
+
+
+# The original idea for AddMethod() and RenameFunction() come from the
+# following post to the ActiveState Python Cookbook:
+#
+# ASPN: Python Cookbook : Install bound methods in an instance
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
+#
+# That code was a little fragile, though, so the following changes
+# have been wrung on it:
+#
+# * Switched the installmethod() "object" and "function" arguments,
+# so the order reflects that the left-hand side is the thing being
+# "assigned to" and the right-hand side is the value being assigned.
+#
+# * Changed explicit type-checking to the "try: klass = object.__class__"
+# block in installmethod() below so that it still works with the
+# old-style classes that SCons uses.
+#
+# * Replaced the by-hand creation of methods and functions with use of
+# the "new" module, as alluded to in Alex Martelli's response to the
+# following Cookbook post:
+#
+# ASPN: Python Cookbook : Dynamically added methods to a class
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
+
+def AddMethod(object, function, name = None):
+ """
+ Adds either a bound method to an instance or an unbound method to
+ a class. If name is ommited the name of the specified function
+ is used by default.
+ Example:
+ a = A()
+ def f(self, x, y):
+ self.z = x + y
+ AddMethod(f, A, "add")
+ a.add(2, 4)
+ print a.z
+ AddMethod(lambda self, i: self.l[i], a, "listIndex")
+ print a.listIndex(5)
+ """
+ import new
+
+ if name is None:
+ name = function.func_name
+ else:
+ function = RenameFunction(function, name)
+
+ try:
+ klass = object.__class__
+ except AttributeError:
+ # "object" is really a class, so it gets an unbound method.
+ object.__dict__[name] = new.instancemethod(function, None, object)
+ else:
+ # "object" is really an instance, so it gets a bound method.
+ object.__dict__[name] = new.instancemethod(function, object, klass)
+
+def RenameFunction(function, name):
+ """
+ Returns a function identical to the specified function, but with
+ the specified name.
+ """
+ import new
+
+ # Compatibility for Python 1.5 and 2.1. Can be removed in favor of
+ # passing function.func_defaults directly to new.function() once
+ # we base on Python 2.2 or later.
+ func_defaults = function.func_defaults
+ if func_defaults is None:
+ func_defaults = ()
+
+ return new.function(function.func_code,
+ function.func_globals,
+ name,
+ func_defaults)
+
+
+md5 = False
+def MD5signature(s):
+ return str(s)
+
+def MD5filesignature(fname, chunksize=65536):
+ f = open(fname, "rb")
+ result = f.read()
+ f.close()
+ return result
+
+try:
+ import hashlib
+except ImportError:
+ pass
+else:
+ if hasattr(hashlib, 'md5'):
+ md5 = True
+ def MD5signature(s):
+ m = hashlib.md5()
+ m.update(str(s))
+ return m.hexdigest()
+
+ def MD5filesignature(fname, chunksize=65536):
+ m = hashlib.md5()
+ f = open(fname, "rb")
+ while 1:
+ blck = f.read(chunksize)
+ if not blck:
+ break
+ m.update(str(blck))
+ f.close()
+ return m.hexdigest()
+
+def MD5collect(signatures):
+ """
+ Collects a list of signatures into an aggregate signature.
+
+ signatures - a list of signatures
+ returns - the aggregate signature
+ """
+ if len(signatures) == 1:
+ return signatures[0]
+ else:
+ return MD5signature(string.join(signatures, ', '))
+
+
+
+# Wrap the intern() function so it doesn't throw exceptions if ineligible
+# arguments are passed. The intern() function was moved into the sys module in
+# Python 3.
+try:
+ intern
+except NameError:
+ from sys import intern
+
+def silent_intern(x):
+ """
+ Perform intern() on the passed argument and return the result.
+ If the input is ineligible (e.g. a unicode string) the original argument is
+ returned and no exception is thrown.
+ """
+ try:
+ return intern(x)
+ except TypeError:
+ return x
+
+
+
+# From Dinu C. Gherman,
+# Python Cookbook, second edition, recipe 6.17, p. 277.
+# Also:
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
+# ASPN: Python Cookbook: Null Object Design Pattern
+
+# TODO(1.5):
+#class Null(object):
+class Null:
+ """ Null objects always and reliably "do nothing." """
+ def __new__(cls, *args, **kwargs):
+ if not '_inst' in vars(cls):
+ #cls._inst = type.__new__(cls, *args, **kwargs)
+ cls._inst = apply(type.__new__, (cls,) + args, kwargs)
+ return cls._inst
+ def __init__(self, *args, **kwargs):
+ pass
+ def __call__(self, *args, **kwargs):
+ return self
+ def __repr__(self):
+ return "Null(0x%08X)" % id(self)
+ def __nonzero__(self):
+ return False
+ def __getattr__(self, name):
+ return self
+ def __setattr__(self, name, value):
+ return self
+ def __delattr__(self, name):
+ return self
+
+class NullSeq(Null):
+ def __len__(self):
+ return 0
+ def __iter__(self):
+ return iter(())
+ def __getitem__(self, i):
+ return self
+ def __delitem__(self, i):
+ return self
+ def __setitem__(self, i, v):
+ return self
+
+
+del __revision__
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
new file mode 100644
index 0000000..f14e7c3
--- /dev/null
+++ b/src/engine/SCons/UtilTests.py
@@ -0,0 +1,802 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/UtilTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import string
+import StringIO
+import sys
+import types
+import unittest
+
+from UserDict import UserDict
+
+import TestCmd
+
+import SCons.Errors
+
+from SCons.Util import *
+
+class OutBuffer:
+ def __init__(self):
+ self.buffer = ""
+
+ def write(self, str):
+ self.buffer = self.buffer + str
+
+class dictifyTestCase(unittest.TestCase):
+ def test_dictify(self):
+ """Test the dictify() function"""
+ r = SCons.Util.dictify(['a', 'b', 'c'], [1, 2, 3])
+ assert r == {'a':1, 'b':2, 'c':3}, r
+
+ r = {}
+ SCons.Util.dictify(['a'], [1], r)
+ SCons.Util.dictify(['b'], [2], r)
+ SCons.Util.dictify(['c'], [3], r)
+ assert r == {'a':1, 'b':2, 'c':3}, r
+
+class UtilTestCase(unittest.TestCase):
+ def test_splitext(self):
+ assert splitext('foo') == ('foo','')
+ assert splitext('foo.bar') == ('foo','.bar')
+ assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
+
+ class Node:
+ def __init__(self, name, children=[]):
+ self.children = children
+ self.name = name
+ self.nocache = None
+ def __str__(self):
+ return self.name
+ def exists(self):
+ return 1
+ def rexists(self):
+ return 1
+ def has_builder(self):
+ return 1
+ def has_explicit_builder(self):
+ return 1
+ def side_effect(self):
+ return 1
+ def precious(self):
+ return 1
+ def always_build(self):
+ return 1
+ def is_up_to_date(self):
+ return 1
+ def noclean(self):
+ return 1
+
+ def tree_case_1(self):
+ """Fixture for the render_tree() and print_tree() tests."""
+ windows_h = self.Node("windows.h")
+ stdlib_h = self.Node("stdlib.h")
+ stdio_h = self.Node("stdio.h")
+ bar_c = self.Node("bar.c", [stdlib_h, windows_h])
+ bar_o = self.Node("bar.o", [bar_c])
+ foo_c = self.Node("foo.c", [stdio_h])
+ foo_o = self.Node("foo.o", [foo_c])
+ foo = self.Node("foo", [foo_o, bar_o])
+
+ expect = """\
++-foo
+ +-foo.o
+ | +-foo.c
+ | +-stdio.h
+ +-bar.o
+ +-bar.c
+ +-stdlib.h
+ +-windows.h
+"""
+
+ lines = string.split(expect, '\n')[:-1]
+ lines = map(lambda l: '[E BSPACN ]'+l, lines)
+ withtags = string.join(lines, '\n') + '\n'
+
+ return foo, expect, withtags
+
+ def tree_case_2(self, prune=1):
+ """Fixture for the render_tree() and print_tree() tests."""
+
+ stdlib_h = self.Node("stdlib.h")
+ bar_h = self.Node('bar.h', [stdlib_h])
+ blat_h = self.Node('blat.h', [stdlib_h])
+ blat_c = self.Node('blat.c', [blat_h, bar_h])
+ blat_o = self.Node('blat.o', [blat_c])
+
+ expect = """\
++-blat.o
+ +-blat.c
+ +-blat.h
+ | +-stdlib.h
+ +-bar.h
+ +-[stdlib.h]
+"""
+
+ if not prune:
+ expect = string.replace(expect, '[', '')
+ expect = string.replace(expect, ']', '')
+
+ lines = string.split(expect, '\n')[:-1]
+ lines = map(lambda l: '[E BSPACN ]'+l, lines)
+ withtags = string.join(lines, '\n') + '\n'
+
+ return blat_o, expect, withtags
+
+ def test_render_tree(self):
+ """Test the render_tree() function"""
+ def get_children(node):
+ return node.children
+
+ node, expect, withtags = self.tree_case_1()
+ actual = render_tree(node, get_children)
+ assert expect == actual, (expect, actual)
+
+ node, expect, withtags = self.tree_case_2()
+ actual = render_tree(node, get_children, 1)
+ assert expect == actual, (expect, actual)
+
+ def test_print_tree(self):
+ """Test the print_tree() function"""
+ def get_children(node):
+ return node.children
+
+ save_stdout = sys.stdout
+
+ try:
+ node, expect, withtags = self.tree_case_1()
+
+ sys.stdout = StringIO.StringIO()
+ print_tree(node, get_children)
+ actual = sys.stdout.getvalue()
+ assert expect == actual, (expect, actual)
+
+ sys.stdout = StringIO.StringIO()
+ print_tree(node, get_children, showtags=1)
+ actual = sys.stdout.getvalue()
+ assert withtags == actual, (withtags, actual)
+
+ node, expect, withtags = self.tree_case_2(prune=0)
+
+ sys.stdout = StringIO.StringIO()
+ print_tree(node, get_children, 1)
+ actual = sys.stdout.getvalue()
+ assert expect == actual, (expect, actual)
+
+ sys.stdout = StringIO.StringIO()
+ # The following call should work here:
+ # print_tree(node, get_children, 1, showtags=1)
+ # For some reason I don't understand, though, *this*
+ # time that we call print_tree, the visited dictionary
+ # is still populated with the values from the last call!
+ # I can't see why this would be, short of a bug in Python,
+ # and rather than continue banging my head against the
+ # brick wall for a *test*, we're going to going with
+ # the cheap, easy workaround:
+ print_tree(node, get_children, 1, showtags=1, visited={})
+ actual = sys.stdout.getvalue()
+ assert withtags == actual, (withtags, actual)
+ finally:
+ sys.stdout = save_stdout
+
+ def test_is_Dict(self):
+ assert is_Dict({})
+ assert is_Dict(UserDict())
+ try:
+ class mydict(dict):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_Dict(mydict({}))
+ assert not is_Dict([])
+ assert not is_Dict(())
+ assert not is_Dict("")
+ if hasattr(types, 'UnicodeType'):
+ exec "assert not is_Dict(u'')"
+
+ def test_is_List(self):
+ assert is_List([])
+ import UserList
+ assert is_List(UserList.UserList())
+ try:
+ class mylist(list):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_List(mylist([]))
+ assert not is_List(())
+ assert not is_List({})
+ assert not is_List("")
+ if hasattr(types, 'UnicodeType'):
+ exec "assert not is_List(u'')"
+
+ def test_is_String(self):
+ assert is_String("")
+ if hasattr(types, 'UnicodeType'):
+ exec "assert is_String(u'')"
+ try:
+ import UserString
+ except:
+ pass
+ else:
+ assert is_String(UserString.UserString(''))
+ try:
+ class mystr(str):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_String(mystr(''))
+ assert not is_String({})
+ assert not is_String([])
+ assert not is_String(())
+
+ def test_is_Tuple(self):
+ assert is_Tuple(())
+ try:
+ class mytuple(tuple):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_Tuple(mytuple(()))
+ assert not is_Tuple([])
+ assert not is_Tuple({})
+ assert not is_Tuple("")
+ if hasattr(types, 'UnicodeType'):
+ exec "assert not is_Tuple(u'')"
+
+ def test_to_String(self):
+ """Test the to_String() method."""
+ assert to_String(1) == "1", to_String(1)
+ assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
+ assert to_String("foo") == "foo", to_String("foo")
+
+ try:
+ import UserString
+
+ s1=UserString.UserString('blah')
+ assert to_String(s1) == s1, s1
+ assert to_String(s1) == 'blah', s1
+
+ class Derived(UserString.UserString):
+ pass
+ s2 = Derived('foo')
+ assert to_String(s2) == s2, s2
+ assert to_String(s2) == 'foo', s2
+
+ if hasattr(types, 'UnicodeType'):
+ s3=UserString.UserString(unicode('bar'))
+ assert to_String(s3) == s3, s3
+ assert to_String(s3) == unicode('bar'), s3
+ assert type(to_String(s3)) is types.UnicodeType, \
+ type(to_String(s3))
+ except ImportError:
+ pass
+
+ if hasattr(types, 'UnicodeType'):
+ s4 = unicode('baz')
+ assert to_String(s4) == unicode('baz'), to_String(s4)
+ assert type(to_String(s4)) is types.UnicodeType, \
+ type(to_String(s4))
+
+ def test_WhereIs(self):
+ test = TestCmd.TestCmd(workdir = '')
+
+ sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
+ sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
+ sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
+ sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
+
+ test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
+
+ if sys.platform != 'win32':
+ test.write(sub1_xxx_exe, "\n")
+
+ os.mkdir(sub2_xxx_exe)
+
+ test.write(sub3_xxx_exe, "\n")
+ os.chmod(sub3_xxx_exe, 0777)
+
+ test.write(sub4_xxx_exe, "\n")
+ os.chmod(sub4_xxx_exe, 0777)
+
+ env_path = os.environ['PATH']
+
+ try:
+ pathdirs_1234 = [ test.workpath('sub1'),
+ test.workpath('sub2'),
+ test.workpath('sub3'),
+ test.workpath('sub4'),
+ ] + string.split(env_path, os.pathsep)
+
+ pathdirs_1243 = [ test.workpath('sub1'),
+ test.workpath('sub2'),
+ test.workpath('sub4'),
+ test.workpath('sub3'),
+ ] + string.split(env_path, os.pathsep)
+
+ os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
+ wi = WhereIs('xxx.exe')
+ assert wi == test.workpath(sub3_xxx_exe), wi
+ wi = WhereIs('xxx.exe', pathdirs_1243)
+ assert wi == test.workpath(sub4_xxx_exe), wi
+ wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
+ assert wi == test.workpath(sub4_xxx_exe), wi
+
+ wi = WhereIs('xxx.exe',reject = sub3_xxx_exe)
+ assert wi == test.workpath(sub4_xxx_exe), wi
+ wi = WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
+ assert wi == test.workpath(sub4_xxx_exe), wi
+
+ os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
+ wi = WhereIs('xxx.exe')
+ assert wi == test.workpath(sub4_xxx_exe), wi
+ wi = WhereIs('xxx.exe', pathdirs_1234)
+ assert wi == test.workpath(sub3_xxx_exe), wi
+ wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
+ assert wi == test.workpath(sub3_xxx_exe), wi
+
+ if sys.platform == 'win32':
+ wi = WhereIs('xxx', pathext = '')
+ assert wi is None, wi
+
+ wi = WhereIs('xxx', pathext = '.exe')
+ assert wi == test.workpath(sub4_xxx_exe), wi
+
+ wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
+ assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+
+ # Test that we return a normalized path even when
+ # the path contains forward slashes.
+ forward_slash = test.workpath('') + '/sub3'
+ wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
+ assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+
+ del os.environ['PATH']
+ wi = WhereIs('xxx.exe')
+ assert wi is None, wi
+
+ finally:
+ os.environ['PATH'] = env_path
+
+ def test_get_env_var(self):
+ """Testing get_environment_var()."""
+ assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
+ assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
+ assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
+ assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
+ assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
+ assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
+ assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
+ assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
+ assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
+
+ def test_Proxy(self):
+ """Test generic Proxy class."""
+ class Subject:
+ def foo(self):
+ return 1
+ def bar(self):
+ return 2
+
+ s=Subject()
+ s.baz = 3
+
+ class ProxyTest(Proxy):
+ def bar(self):
+ return 4
+
+ p=ProxyTest(s)
+
+ assert p.foo() == 1, p.foo()
+ assert p.bar() == 4, p.bar()
+ assert p.baz == 3, p.baz
+
+ p.baz = 5
+ s.baz = 6
+
+ assert p.baz == 5, p.baz
+ assert p.get() == s, p.get()
+
+ def test_display(self):
+ old_stdout = sys.stdout
+ sys.stdout = OutBuffer()
+ display("line1")
+ display.set_mode(0)
+ display("line2")
+ display.set_mode(1)
+ display("line3")
+ display("line4\n", append_newline=0)
+ display.set_mode(0)
+ display("dont print1")
+ display("dont print2\n", append_newline=0)
+ display.set_mode(1)
+ assert sys.stdout.buffer == "line1\nline3\nline4\n"
+ sys.stdout = old_stdout
+
+ def test_get_native_path(self):
+ """Test the get_native_path() function."""
+ import tempfile
+ filename = tempfile.mktemp()
+ str = '1234567890 ' + filename
+ try:
+ open(filename, 'w').write(str)
+ assert open(get_native_path(filename)).read() == str
+ finally:
+ try:
+ os.unlink(filename)
+ except OSError:
+ pass
+
+ def test_PrependPath(self):
+ """Test prepending to a path"""
+ p1 = r'C:\dir\num\one;C:\dir\num\two'
+ p2 = r'C:\mydir\num\one;C:\mydir\num\two'
+ # have to include the pathsep here so that the test will work on UNIX too.
+ p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';')
+ p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
+ p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
+ p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
+ assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+ assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+
+ def test_AppendPath(self):
+ """Test appending to a path."""
+ p1 = r'C:\dir\num\one;C:\dir\num\two'
+ p2 = r'C:\mydir\num\one;C:\mydir\num\two'
+ # have to include the pathsep here so that the test will work on UNIX too.
+ p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';')
+ p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
+ p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
+ p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
+ assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+ assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
+
+ def test_PrependPathPreserveOld(self):
+ """Test prepending to a path while preserving old paths"""
+ p1 = r'C:\dir\num\one;C:\dir\num\two'
+ # have to include the pathsep here so that the test will work on UNIX too.
+ p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';', delete_existing=0)
+ p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
+ assert(p1 == r'C:\dir\num\three;C:\dir\num\one;C:\dir\num\two')
+
+ def test_AppendPathPreserveOld(self):
+ """Test appending to a path while preserving old paths"""
+ p1 = r'C:\dir\num\one;C:\dir\num\two'
+ # have to include the pathsep here so that the test will work on UNIX too.
+ p1 = AppendPath(p1,r'C:\dir\num\one',sep = ';', delete_existing=0)
+ p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
+ assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+
+ def test_CLVar(self):
+ """Test the command-line construction variable class"""
+ f = SCons.Util.CLVar('a b')
+
+ r = f + 'c d'
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ' c d'
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ['c d']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c d'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + [' c d']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', ' c d'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ['c', 'd']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + [' c', 'd']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', ' c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ f = SCons.Util.CLVar(['a b'])
+
+ r = f + 'c d'
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ' c d'
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ['c d']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a b', 'c d'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + [' c d']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a b', ' c d'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ['c', 'd']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + [' c', 'd']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a b', ' c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ f = SCons.Util.CLVar(['a', 'b'])
+
+ r = f + 'c d'
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ' c d'
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ['c d']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c d'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + [' c d']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', ' c d'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + ['c', 'd']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', 'c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ r = f + [' c', 'd']
+ assert isinstance(r, SCons.Util.CLVar), type(r)
+ assert r.data == ['a', 'b', ' c', 'd'], r.data
+ assert str(r) == 'a b c d', str(r)
+
+ def test_Selector(self):
+ """Test the Selector class"""
+
+ class MyNode:
+ def __init__(self, name):
+ self.name = name
+ self.suffix = os.path.splitext(name)[1]
+
+ def __str__(self):
+ return self.name
+
+ s = Selector({'a' : 'AAA', 'b' : 'BBB'})
+ assert s['a'] == 'AAA', s['a']
+ assert s['b'] == 'BBB', s['b']
+ exc_caught = None
+ try:
+ x = s['c']
+ except KeyError:
+ exc_caught = 1
+ assert exc_caught, "should have caught a KeyError"
+ s['c'] = 'CCC'
+ assert s['c'] == 'CCC', s['c']
+
+ class DummyEnv(UserDict):
+ def subst(self, key):
+ if key[0] == '$':
+ return self[key[1:]]
+ return key
+
+ env = DummyEnv()
+
+ s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
+ ret = s(env, [])
+ assert ret is None, ret
+ ret = s(env, [MyNode('foo.d')])
+ assert ret == 'DDD', ret
+ ret = s(env, [MyNode('bar.e')])
+ assert ret == 'EEE', ret
+ ret = s(env, [MyNode('bar.x')])
+ assert ret is None, ret
+ s[None] = 'XXX'
+ ret = s(env, [MyNode('bar.x')])
+ assert ret == 'XXX', ret
+
+ env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
+
+ s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
+ ret = s(env, [MyNode('foo.f')])
+ assert ret == 'FFF', ret
+ ret = s(env, [MyNode('bar.g')])
+ assert ret == 'GGG', ret
+
+ def test_adjustixes(self):
+ """Test the adjustixes() function"""
+ r = adjustixes('file', 'pre-', '-suf')
+ assert r == 'pre-file-suf', r
+ r = adjustixes('pre-file', 'pre-', '-suf')
+ assert r == 'pre-file-suf', r
+ r = adjustixes('file-suf', 'pre-', '-suf')
+ assert r == 'pre-file-suf', r
+ r = adjustixes('pre-file-suf', 'pre-', '-suf')
+ assert r == 'pre-file-suf', r
+ r = adjustixes('pre-file.xxx', 'pre-', '-suf')
+ assert r == 'pre-file.xxx', r
+ r = adjustixes('dir/file', 'pre-', '-suf')
+ assert r == os.path.join('dir', 'pre-file-suf'), r
+
+ def test_containsAny(self):
+ """Test the containsAny() function"""
+ assert containsAny('*.py', '*?[]')
+ assert not containsAny('file.txt', '*?[]')
+
+ def test_containsAll(self):
+ """Test the containsAll() function"""
+ assert containsAll('43221', '123')
+ assert not containsAll('134', '123')
+
+ def test_containsOnly(self):
+ """Test the containsOnly() function"""
+ assert containsOnly('.83', '0123456789.')
+ assert not containsOnly('43221', '123')
+
+ def test_LogicalLines(self):
+ """Test the LogicalLines class"""
+ fobj = StringIO.StringIO(r"""
+foo \
+bar \
+baz
+foo
+bling \
+bling \ bling
+bling
+""")
+
+ lines = LogicalLines(fobj).readlines()
+ assert lines == [
+ '\n',
+ 'foo bar baz\n',
+ 'foo\n',
+ 'bling bling \\ bling\n',
+ 'bling\n',
+ ], lines
+
+ def test_intern(self):
+ s1 = silent_intern("spam")
+ # Python 1.5 and 3.x do not have a unicode() built-in
+ if sys.version[0] == '2':
+ s2 = silent_intern(unicode("unicode spam"))
+ s3 = silent_intern(42)
+ s4 = silent_intern("spam")
+ assert id(s1) == id(s4)
+
+
+class MD5TestCase(unittest.TestCase):
+
+ def test_collect(self):
+ """Test collecting a list of signatures into a new signature value
+ """
+ s = map(MD5signature, ('111', '222', '333'))
+
+ assert '698d51a19d8a121ce581499d7b701668' == MD5collect(s[0:1])
+ assert '8980c988edc2c78cc43ccb718c06efd5' == MD5collect(s[0:2])
+ assert '53fd88c84ff8a285eb6e0a687e55b8c7' == MD5collect(s)
+
+ def test_MD5signature(self):
+ """Test generating a signature"""
+ s = MD5signature('111')
+ assert '698d51a19d8a121ce581499d7b701668' == s, s
+
+ s = MD5signature('222')
+ assert 'bcbe3365e6ac95ea2c0343a2395834dd' == s, s
+
+class NodeListTestCase(unittest.TestCase):
+ def test_simple_attributes(self):
+ """Test simple attributes of a NodeList class"""
+ class TestClass:
+ def __init__(self, name, child=None):
+ self.child = child
+ self.bar = name
+
+ t1 = TestClass('t1', TestClass('t1child'))
+ t2 = TestClass('t2', TestClass('t2child'))
+ t3 = TestClass('t3')
+
+ nl = NodeList([t1, t2, t3])
+ assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
+ assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
+ nl[0:2].child.bar
+
+ def test_callable_attributes(self):
+ """Test callable attributes of a NodeList class"""
+ class TestClass:
+ def __init__(self, name, child=None):
+ self.child = child
+ self.bar = name
+ def foo(self):
+ return self.bar + "foo"
+ def getself(self):
+ return self
+
+ t1 = TestClass('t1', TestClass('t1child'))
+ t2 = TestClass('t2', TestClass('t2child'))
+ t3 = TestClass('t3')
+
+ nl = NodeList([t1, t2, t3])
+ assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
+ assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
+ assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
+ assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
+ nl[0:2].child.foo()
+ assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
+ nl[0:2].child.bar
+
+ def test_null(self):
+ """Test a null NodeList"""
+ nl = NodeList([])
+ r = str(nl)
+ assert r == '', r
+ for node in nl:
+ raise Exception, "should not enter this loop"
+
+
+class flattenTestCase(unittest.TestCase):
+
+ def test_scalar(self):
+ """Test flattening a scalar"""
+ result = flatten('xyz')
+ assert result == ['xyz'], result
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ dictifyTestCase,
+ flattenTestCase,
+ MD5TestCase,
+ NodeListTestCase,
+ UtilTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/BoolVariable.py b/src/engine/SCons/Variables/BoolVariable.py
new file mode 100644
index 0000000..1209a4c
--- /dev/null
+++ b/src/engine/SCons/Variables/BoolVariable.py
@@ -0,0 +1,91 @@
+"""engine.SCons.Variables.BoolVariable
+
+This file defines the option type for SCons implementing true/false values.
+
+Usage example:
+
+ opts = Variables()
+ opts.Add(BoolVariable('embedded', 'build for an embedded system', 0))
+ ...
+ if env['embedded'] == 1:
+ ...
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/BoolVariable.py 4577 2009/12/27 19:44:43 scons"
+
+__all__ = ['BoolVariable',]
+
+import string
+
+import SCons.Errors
+
+__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' )
+__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none')
+
+
+def _text2bool(val):
+ """
+ Converts strings to True/False depending on the 'truth' expressed by
+ the string. If the string can't be converted, the original value
+ will be returned.
+
+ See '__true_strings' and '__false_strings' for values considered
+ 'true' or 'false respectivly.
+
+ This is usable as 'converter' for SCons' Variables.
+ """
+ lval = string.lower(val)
+ if lval in __true_strings: return True
+ if lval in __false_strings: return False
+ raise ValueError("Invalid value for boolean option: %s" % val)
+
+
+def _validator(key, val, env):
+ """
+ Validates the given value to be either '0' or '1'.
+
+ This is usable as 'validator' for SCons' Variables.
+ """
+ if not env[key] in (True, False):
+ raise SCons.Errors.UserError(
+ 'Invalid value for boolean option %s: %s' % (key, env[key]))
+
+
+def BoolVariable(key, help, default):
+ """
+ The input parameters describe a boolen option, thus they are
+ returned with the correct converter and validator appended. The
+ 'help' text will by appended by '(yes|no) to show the valid
+ valued. The result is usable for input to opts.Add().
+ """
+ return (key, '%s (yes|no)' % help, default,
+ _validator, _text2bool)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/BoolVariableTests.py b/src/engine/SCons/Variables/BoolVariableTests.py
new file mode 100644
index 0000000..c33d9d0
--- /dev/null
+++ b/src/engine/SCons/Variables/BoolVariableTests.py
@@ -0,0 +1,127 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/BoolVariableTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Variables
+
+class BoolVariableTestCase(unittest.TestCase):
+ def test_BoolVariable(self):
+ """Test BoolVariable creation"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
+
+ o = opts.options[0]
+ assert o.key == 'test', o.key
+ assert o.help == 'test option help (yes|no)', o.help
+ assert o.default == 0, o.default
+ assert o.validator is not None, o.validator
+ assert o.converter is not None, o.converter
+
+ def test_converter(self):
+ """Test the BoolVariable converter"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
+
+ o = opts.options[0]
+
+ true_values = [
+ 'y', 'Y',
+ 'yes', 'YES',
+ 't', 'T',
+ 'true', 'TRUE',
+ 'on', 'ON',
+ 'all', 'ALL',
+ '1',
+ ]
+ false_values = [
+ 'n', 'N',
+ 'no', 'NO',
+ 'f', 'F',
+ 'false', 'FALSE',
+ 'off', 'OFF',
+ 'none', 'NONE',
+ '0',
+ ]
+
+ for t in true_values:
+ x = o.converter(t)
+ assert x, "converter returned false for '%s'" % t
+
+ for f in false_values:
+ x = o.converter(f)
+ assert not x, "converter returned true for '%s'" % f
+
+ caught = None
+ try:
+ o.converter('x')
+ except ValueError:
+ caught = 1
+ assert caught, "did not catch expected ValueError"
+
+ def test_validator(self):
+ """Test the BoolVariable validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
+
+ o = opts.options[0]
+
+ env = {
+ 'T' : True,
+ 'F' : False,
+ 'N' : 'xyzzy',
+ }
+
+ o.validator('T', 0, env)
+
+ o.validator('F', 0, env)
+
+ caught = None
+ try:
+ o.validator('N', 0, env)
+ except SCons.Errors.UserError:
+ caught = 1
+ assert caught, "did not catch expected UserError for N"
+
+ caught = None
+ try:
+ o.validator('NOSUCHKEY', 0, env)
+ except KeyError:
+ caught = 1
+ assert caught, "did not catch expected KeyError for NOSUCHKEY"
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(BoolVariableTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/EnumVariable.py b/src/engine/SCons/Variables/EnumVariable.py
new file mode 100644
index 0000000..90ed48e
--- /dev/null
+++ b/src/engine/SCons/Variables/EnumVariable.py
@@ -0,0 +1,107 @@
+"""engine.SCons.Variables.EnumVariable
+
+This file defines the option type for SCons allowing only specified
+input-values.
+
+Usage example:
+
+ opts = Variables()
+ opts.Add(EnumVariable('debug', 'debug output and symbols', 'no',
+ allowed_values=('yes', 'no', 'full'),
+ map={}, ignorecase=2))
+ ...
+ if env['debug'] == 'full':
+ ...
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/EnumVariable.py 4577 2009/12/27 19:44:43 scons"
+
+__all__ = ['EnumVariable',]
+
+import string
+
+import SCons.Errors
+
+def _validator(key, val, env, vals):
+ if not val in vals:
+ raise SCons.Errors.UserError(
+ 'Invalid value for option %s: %s' % (key, val))
+
+
+def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0):
+ """
+ The input parameters describe a option with only certain values
+ allowed. They are returned with an appropriate converter and
+ validator appended. The result is usable for input to
+ Variables.Add().
+
+ 'key' and 'default' are the values to be passed on to Variables.Add().
+
+ 'help' will be appended by the allowed values automatically
+
+ 'allowed_values' is a list of strings, which are allowed as values
+ for this option.
+
+ The 'map'-dictionary may be used for converting the input value
+ into canonical values (eg. for aliases).
+
+ 'ignorecase' defines the behaviour of the validator:
+
+ If ignorecase == 0, the validator/converter are case-sensitive.
+ If ignorecase == 1, the validator/converter are case-insensitive.
+ If ignorecase == 2, the validator/converter is case-insensitive and
+ the converted value will always be lower-case.
+
+ The 'validator' tests whether the value is in the list of allowed
+ values. The 'converter' converts input values according to the
+ given 'map'-dictionary (unmapped input values are returned
+ unchanged).
+ """
+ help = '%s (%s)' % (help, string.join(allowed_values, '|'))
+ # define validator
+ if ignorecase >= 1:
+ validator = lambda key, val, env, vals=allowed_values: \
+ _validator(key, string.lower(val), env, vals)
+ else:
+ validator = lambda key, val, env, vals=allowed_values: \
+ _validator(key, val, env, vals)
+ # define converter
+ if ignorecase == 2:
+ converter = lambda val, map=map: \
+ string.lower(map.get(string.lower(val), val))
+ elif ignorecase == 1:
+ converter = lambda val, map=map: \
+ map.get(string.lower(val), val)
+ else:
+ converter = lambda val, map=map: \
+ map.get(val, val)
+ return (key, help, default, validator, converter)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/EnumVariableTests.py b/src/engine/SCons/Variables/EnumVariableTests.py
new file mode 100644
index 0000000..668487d
--- /dev/null
+++ b/src/engine/SCons/Variables/EnumVariableTests.py
@@ -0,0 +1,204 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/EnumVariableTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Variables
+
+class EnumVariableTestCase(unittest.TestCase):
+ def test_EnumVariable(self):
+ """Test EnumVariable creation"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {}))
+
+ o = opts.options[0]
+ assert o.key == 'test', o.key
+ assert o.help == 'test option help (one|two|three)', o.help
+ assert o.default == 0, o.default
+ assert o.validator is not None, o.validator
+ assert o.converter is not None, o.converter
+
+ def test_converter(self):
+ """Test the EnumVariable converter"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0,
+ ['one', 'two', 'three']))
+
+ o = opts.options[0]
+
+ for a in ['one', 'two', 'three', 'no_match']:
+ x = o.converter(a)
+ assert x == a, x
+
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'1' : 'one',
+ '2' : 'two',
+ '3' : 'three'}))
+
+ o = opts.options[0]
+
+ x = o.converter('one')
+ assert x == 'one', x
+ x = o.converter('1')
+ assert x == 'one', x
+
+ x = o.converter('two')
+ assert x == 'two', x
+ x = o.converter('2')
+ assert x == 'two', x
+
+ x = o.converter('three')
+ assert x == 'three', x
+ x = o.converter('3')
+ assert x == 'three', x
+
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.EnumVariable('test0', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'a' : 'one',
+ 'b' : 'two',
+ 'c' : 'three'},
+ ignorecase=0))
+ opts.Add(SCons.Variables.EnumVariable('test1', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'a' : 'one',
+ 'b' : 'two',
+ 'c' : 'three'},
+ ignorecase=1))
+ opts.Add(SCons.Variables.EnumVariable('test2', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'a' : 'one',
+ 'b' : 'two',
+ 'c' : 'three'},
+ ignorecase=2))
+
+ o0 = opts.options[0]
+ o1 = opts.options[1]
+ o2 = opts.options[2]
+
+ table = {
+ 'one' : ['one', 'one', 'one'],
+ 'One' : ['One', 'One', 'one'],
+ 'ONE' : ['ONE', 'ONE', 'one'],
+ 'two' : ['two', 'two', 'two'],
+ 'twO' : ['twO', 'twO', 'two'],
+ 'TWO' : ['TWO', 'TWO', 'two'],
+ 'three' : ['three', 'three', 'three'],
+ 'thRee' : ['thRee', 'thRee', 'three'],
+ 'THREE' : ['THREE', 'THREE', 'three'],
+ 'a' : ['one', 'one', 'one'],
+ 'A' : ['A', 'one', 'one'],
+ 'b' : ['two', 'two', 'two'],
+ 'B' : ['B', 'two', 'two'],
+ 'c' : ['three', 'three', 'three'],
+ 'C' : ['C', 'three', 'three'],
+ }
+
+ for k, l in table.items():
+ x = o0.converter(k)
+ assert x == l[0], "o0 got %s, expected %s" % (x, l[0])
+ x = o1.converter(k)
+ assert x == l[1], "o1 got %s, expected %s" % (x, l[1])
+ x = o2.converter(k)
+ assert x == l[2], "o2 got %s, expected %s" % (x, l[2])
+
+ def test_validator(self):
+ """Test the EnumVariable validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.EnumVariable('test0', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'a' : 'one',
+ 'b' : 'two',
+ 'c' : 'three'},
+ ignorecase=0))
+ opts.Add(SCons.Variables.EnumVariable('test1', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'a' : 'one',
+ 'b' : 'two',
+ 'c' : 'three'},
+ ignorecase=1))
+ opts.Add(SCons.Variables.EnumVariable('test2', 'test option help', 0,
+ ['one', 'two', 'three'],
+ {'a' : 'one',
+ 'b' : 'two',
+ 'c' : 'three'},
+ ignorecase=2))
+
+ o0 = opts.options[0]
+ o1 = opts.options[1]
+ o2 = opts.options[2]
+
+ def valid(o, v):
+ o.validator('X', v, {})
+
+ def invalid(o, v):
+ caught = None
+ try:
+ o.validator('X', v, {})
+ except SCons.Errors.UserError:
+ caught = 1
+ assert caught, "did not catch expected UserError for o = %s, v = %s" % (o.key, v)
+
+ table = {
+ 'one' : [ valid, valid, valid],
+ 'One' : [invalid, valid, valid],
+ 'ONE' : [invalid, valid, valid],
+ 'two' : [ valid, valid, valid],
+ 'twO' : [invalid, valid, valid],
+ 'TWO' : [invalid, valid, valid],
+ 'three' : [ valid, valid, valid],
+ 'thRee' : [invalid, valid, valid],
+ 'THREE' : [invalid, valid, valid],
+ 'a' : [invalid, invalid, invalid],
+ 'A' : [invalid, invalid, invalid],
+ 'b' : [invalid, invalid, invalid],
+ 'B' : [invalid, invalid, invalid],
+ 'c' : [invalid, invalid, invalid],
+ 'C' : [invalid, invalid, invalid],
+ 'no_v' : [invalid, invalid, invalid],
+ }
+
+ for v, l in table.items():
+ l[0](o0, v)
+ l[1](o1, v)
+ l[2](o2, v)
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(EnumVariableTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/ListVariable.py b/src/engine/SCons/Variables/ListVariable.py
new file mode 100644
index 0000000..0debd14
--- /dev/null
+++ b/src/engine/SCons/Variables/ListVariable.py
@@ -0,0 +1,139 @@
+"""engine.SCons.Variables.ListVariable
+
+This file defines the option type for SCons implementing 'lists'.
+
+A 'list' option may either be 'all', 'none' or a list of names
+separated by comma. After the option has been processed, the option
+value holds either the named list elements, all list elemens or no
+list elements at all.
+
+Usage example:
+
+ list_of_libs = Split('x11 gl qt ical')
+
+ opts = Variables()
+ opts.Add(ListVariable('shared',
+ 'libraries to build as shared libraries',
+ 'all',
+ elems = list_of_libs))
+ ...
+ for lib in list_of_libs:
+ if lib in env['shared']:
+ env.SharedObject(...)
+ else:
+ env.Object(...)
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/ListVariable.py 4577 2009/12/27 19:44:43 scons"
+
+# Know Bug: This should behave like a Set-Type, but does not really,
+# since elements can occur twice.
+
+__all__ = ['ListVariable',]
+
+import string
+import UserList
+
+import SCons.Util
+
+
+class _ListVariable(UserList.UserList):
+ def __init__(self, initlist=[], allowedElems=[]):
+ UserList.UserList.__init__(self, filter(None, initlist))
+ self.allowedElems = allowedElems[:]
+ self.allowedElems.sort()
+
+ def __cmp__(self, other):
+ raise NotImplementedError
+ def __eq__(self, other):
+ raise NotImplementedError
+ def __ge__(self, other):
+ raise NotImplementedError
+ def __gt__(self, other):
+ raise NotImplementedError
+ def __le__(self, other):
+ raise NotImplementedError
+ def __lt__(self, other):
+ raise NotImplementedError
+ def __str__(self):
+ if len(self) == 0:
+ return 'none'
+ self.data.sort()
+ if self.data == self.allowedElems:
+ return 'all'
+ else:
+ return string.join(self, ',')
+ def prepare_to_store(self):
+ return self.__str__()
+
+def _converter(val, allowedElems, mapdict):
+ """
+ """
+ if val == 'none':
+ val = []
+ elif val == 'all':
+ val = allowedElems
+ else:
+ val = filter(None, string.split(val, ','))
+ val = map(lambda v, m=mapdict: m.get(v, v), val)
+ notAllowed = filter(lambda v, aE=allowedElems: not v in aE, val)
+ if notAllowed:
+ raise ValueError("Invalid value(s) for option: %s" %
+ string.join(notAllowed, ','))
+ return _ListVariable(val, allowedElems)
+
+
+## def _validator(key, val, env):
+## """
+## """
+## # todo: write validater for pgk list
+## return 1
+
+
+def ListVariable(key, help, default, names, map={}):
+ """
+ The input parameters describe a 'package list' option, thus they
+ are returned with the correct converter and validater appended. The
+ result is usable for input to opts.Add() .
+
+ A 'package list' option may either be 'all', 'none' or a list of
+ package names (separated by space).
+ """
+ names_str = 'allowed names: %s' % string.join(names, ' ')
+ if SCons.Util.is_List(default):
+ default = string.join(default, ',')
+ help = string.join(
+ (help, '(all|none|comma-separated list of names)', names_str),
+ '\n ')
+ return (key, help, default,
+ None, #_validator,
+ lambda val, elems=names, m=map: _converter(val, elems, m))
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/ListVariableTests.py b/src/engine/SCons/Variables/ListVariableTests.py
new file mode 100644
index 0000000..353fbe6
--- /dev/null
+++ b/src/engine/SCons/Variables/ListVariableTests.py
@@ -0,0 +1,134 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/ListVariableTests.py 4577 2009/12/27 19:44:43 scons"
+
+import copy
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Variables
+
+class ListVariableTestCase(unittest.TestCase):
+ def test_ListVariable(self):
+ """Test ListVariable creation"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all',
+ ['one', 'two', 'three']))
+
+ o = opts.options[0]
+ assert o.key == 'test', o.key
+ assert o.help == 'test option help\n (all|none|comma-separated list of names)\n allowed names: one two three', repr(o.help)
+ assert o.default == 'all', o.default
+ assert o.validator is None, o.validator
+ assert not o.converter is None, o.converter
+
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.ListVariable('test2', 'test2 help',
+ ['one', 'three'],
+ ['one', 'two', 'three']))
+
+ o = opts.options[0]
+ assert o.default == 'one,three'
+
+ def test_converter(self):
+ """Test the ListVariable converter"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all',
+ ['one', 'two', 'three'],
+ {'ONE':'one', 'TWO':'two'}))
+
+ o = opts.options[0]
+
+ x = o.converter('all')
+ assert str(x) == 'all', x
+
+ x = o.converter('none')
+ assert str(x) == 'none', x
+
+ x = o.converter('one')
+ assert str(x) == 'one', x
+ x = o.converter('ONE')
+ assert str(x) == 'one', x
+
+ x = o.converter('two')
+ assert str(x) == 'two', x
+ x = o.converter('TWO')
+ assert str(x) == 'two', x
+
+ x = o.converter('three')
+ assert str(x) == 'three', x
+
+ x = o.converter('one,two')
+ assert str(x) == 'one,two', x
+ x = o.converter('two,one')
+ assert str(x) == 'one,two', x
+
+ x = o.converter('one,three')
+ assert str(x) == 'one,three', x
+ x = o.converter('three,one')
+ assert str(x) == 'one,three', x
+
+ x = o.converter('two,three')
+ assert str(x) == 'three,two', x
+ x = o.converter('three,two')
+ assert str(x) == 'three,two', x
+
+ x = o.converter('one,two,three')
+ assert str(x) == 'all', x
+
+ x = o.converter('three,two,one')
+ assert str(x) == 'all', x
+
+ x = o.converter('three,ONE,TWO')
+ assert str(x) == 'all', x
+
+ caught = None
+ try:
+ x = o.converter('no_match')
+ except ValueError:
+ caught = 1
+ assert caught, "did not catch expected ValueError"
+
+ def test_copy(self):
+ """Test copying a ListVariable like an Environment would"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all',
+ ['one', 'two', 'three']))
+
+ o = opts.options[0]
+
+ l = o.converter('all')
+ n = l.__class__(copy.copy(l))
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(ListVariableTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/PackageVariable.py b/src/engine/SCons/Variables/PackageVariable.py
new file mode 100644
index 0000000..e10ff0c
--- /dev/null
+++ b/src/engine/SCons/Variables/PackageVariable.py
@@ -0,0 +1,109 @@
+"""engine.SCons.Variables.PackageVariable
+
+This file defines the option type for SCons implementing 'package
+activation'.
+
+To be used whenever a 'package' may be enabled/disabled and the
+package path may be specified.
+
+Usage example:
+
+ Examples:
+ x11=no (disables X11 support)
+ x11=yes (will search for the package installation dir)
+ x11=/usr/local/X11 (will check this path for existance)
+
+ To replace autoconf's --with-xxx=yyy
+
+ opts = Variables()
+ opts.Add(PackageVariable('x11',
+ 'use X11 installed here (yes = search some places',
+ 'yes'))
+ ...
+ if env['x11'] == True:
+ dir = ... search X11 in some standard places ...
+ env['x11'] = dir
+ if env['x11']:
+ ... build with x11 ...
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/PackageVariable.py 4577 2009/12/27 19:44:43 scons"
+
+__all__ = ['PackageVariable',]
+
+import string
+
+import SCons.Errors
+
+__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search')
+__disable_strings = ('0', 'no', 'false', 'off', 'disable')
+
+def _converter(val):
+ """
+ """
+ lval = string.lower(val)
+ if lval in __enable_strings: return True
+ if lval in __disable_strings: return False
+ #raise ValueError("Invalid value for boolean option: %s" % val)
+ return val
+
+
+def _validator(key, val, env, searchfunc):
+ # NB: searchfunc is currenty undocumented and unsupported
+ """
+ """
+ # todo: write validator, check for path
+ import os
+ if env[key] is True:
+ if searchfunc:
+ env[key] = searchfunc(key, val)
+ elif env[key] and not os.path.exists(val):
+ raise SCons.Errors.UserError(
+ 'Path does not exist for option %s: %s' % (key, val))
+
+
+def PackageVariable(key, help, default, searchfunc=None):
+ # NB: searchfunc is currenty undocumented and unsupported
+ """
+ The input parameters describe a 'package list' option, thus they
+ are returned with the correct converter and validator appended. The
+ result is usable for input to opts.Add() .
+
+ A 'package list' option may either be 'all', 'none' or a list of
+ package names (seperated by space).
+ """
+ help = string.join(
+ (help, '( yes | no | /path/to/%s )' % key),
+ '\n ')
+ return (key, help, default,
+ lambda k, v, e, f=searchfunc: _validator(k,v,e,f),
+ _converter)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/PackageVariableTests.py b/src/engine/SCons/Variables/PackageVariableTests.py
new file mode 100644
index 0000000..70ea191
--- /dev/null
+++ b/src/engine/SCons/Variables/PackageVariableTests.py
@@ -0,0 +1,124 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/PackageVariableTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Variables
+
+import TestCmd
+
+class PackageVariableTestCase(unittest.TestCase):
+ def test_PackageVariable(self):
+ """Test PackageVariable creation"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path'))
+
+ o = opts.options[0]
+ assert o.key == 'test', o.key
+ assert o.help == 'test option help\n ( yes | no | /path/to/test )', repr(o.help)
+ assert o.default == '/default/path', o.default
+ assert o.validator is not None, o.validator
+ assert o.converter is not None, o.converter
+
+ def test_converter(self):
+ """Test the PackageVariable converter"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path'))
+
+ o = opts.options[0]
+
+ true_values = [
+ 'yes', 'YES',
+ 'true', 'TRUE',
+ 'on', 'ON',
+ 'enable', 'ENABLE',
+ 'search', 'SEARCH',
+ ]
+ false_values = [
+ 'no', 'NO',
+ 'false', 'FALSE',
+ 'off', 'OFF',
+ 'disable', 'DISABLE',
+ ]
+
+ for t in true_values:
+ x = o.converter(t)
+ assert x, "converter returned false for '%s'" % t
+
+ for f in false_values:
+ x = o.converter(f)
+ assert not x, "converter returned true for '%s'" % f
+
+ x = o.converter('/explicit/path')
+ assert x == '/explicit/path', x
+
+ # Make sure the converter returns True if we give it str(True) and
+ # False when we give it str(False). This assures consistent operation
+ # through a cycle of Variables.Save(<file>) -> Variables(<file>).
+ x = o.converter(str(True))
+ assert x == True, "converter returned a string when given str(True)"
+
+ x = o.converter(str(False))
+ assert x == False, "converter returned a string when given str(False)"
+
+ def test_validator(self):
+ """Test the PackageVariable validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path'))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.write('exists', 'exists\n')
+
+ o = opts.options[0]
+
+ env = {'F':False, 'T':True, 'X':'x'}
+
+ exists = test.workpath('exists')
+ does_not_exist = test.workpath('does_not_exist')
+
+ o.validator('F', '/path', env)
+ o.validator('T', '/path', env)
+ o.validator('X', exists, env)
+
+ caught = None
+ try:
+ o.validator('X', does_not_exist, env)
+ except SCons.Errors.UserError:
+ caught = 1
+ assert caught, "did not catch expected UserError"
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(PackageVariableTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/PathVariable.py b/src/engine/SCons/Variables/PathVariable.py
new file mode 100644
index 0000000..2ebbd8d
--- /dev/null
+++ b/src/engine/SCons/Variables/PathVariable.py
@@ -0,0 +1,147 @@
+"""SCons.Variables.PathVariable
+
+This file defines an option type for SCons implementing path settings.
+
+To be used whenever a a user-specified path override should be allowed.
+
+Arguments to PathVariable are:
+ option-name = name of this option on the command line (e.g. "prefix")
+ option-help = help string for option
+ option-dflt = default value for this option
+ validator = [optional] validator for option value. Predefined
+ validators are:
+
+ PathAccept -- accepts any path setting; no validation
+ PathIsDir -- path must be an existing directory
+ PathIsDirCreate -- path must be a dir; will create
+ PathIsFile -- path must be a file
+ PathExists -- path must exist (any type) [default]
+
+ The validator is a function that is called and which
+ should return True or False to indicate if the path
+ is valid. The arguments to the validator function
+ are: (key, val, env). The key is the name of the
+ option, the val is the path specified for the option,
+ and the env is the env to which the Otions have been
+ added.
+
+Usage example:
+
+ Examples:
+ prefix=/usr/local
+
+ opts = Variables()
+
+ opts = Variables()
+ opts.Add(PathVariable('qtdir',
+ 'where the root of Qt is installed',
+ qtdir, PathIsDir))
+ opts.Add(PathVariable('qt_includes',
+ 'where the Qt includes are installed',
+ '$qtdir/includes', PathIsDirCreate))
+ opts.Add(PathVariable('qt_libraries',
+ 'where the Qt library is installed',
+ '$qtdir/lib'))
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/PathVariable.py 4577 2009/12/27 19:44:43 scons"
+
+__all__ = ['PathVariable',]
+
+import os
+import os.path
+
+import SCons.Errors
+
+class _PathVariableClass:
+
+ def PathAccept(self, key, val, env):
+ """Accepts any path, no checking done."""
+ pass
+
+ def PathIsDir(self, key, val, env):
+ """Validator to check if Path is a directory."""
+ if not os.path.isdir(val):
+ if os.path.isfile(val):
+ m = 'Directory path for option %s is a file: %s'
+ else:
+ m = 'Directory path for option %s does not exist: %s'
+ raise SCons.Errors.UserError(m % (key, val))
+
+ def PathIsDirCreate(self, key, val, env):
+ """Validator to check if Path is a directory,
+ creating it if it does not exist."""
+ if os.path.isfile(val):
+ m = 'Path for option %s is a file, not a directory: %s'
+ raise SCons.Errors.UserError(m % (key, val))
+ if not os.path.isdir(val):
+ os.makedirs(val)
+
+ def PathIsFile(self, key, val, env):
+ """validator to check if Path is a file"""
+ if not os.path.isfile(val):
+ if os.path.isdir(val):
+ m = 'File path for option %s is a directory: %s'
+ else:
+ m = 'File path for option %s does not exist: %s'
+ raise SCons.Errors.UserError(m % (key, val))
+
+ def PathExists(self, key, val, env):
+ """validator to check if Path exists"""
+ if not os.path.exists(val):
+ m = 'Path for option %s does not exist: %s'
+ raise SCons.Errors.UserError(m % (key, val))
+
+ def __call__(self, key, help, default, validator=None):
+ # NB: searchfunc is currenty undocumented and unsupported
+ """
+ The input parameters describe a 'path list' option, thus they
+ are returned with the correct converter and validator appended. The
+ result is usable for input to opts.Add() .
+
+ The 'default' option specifies the default path to use if the
+ user does not specify an override with this option.
+
+ validator is a validator, see this file for examples
+ """
+ if validator is None:
+ validator = self.PathExists
+
+ if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key):
+ return (key, '%s ( /path/to/%s )' % (help, key[0]), default,
+ validator, None)
+ else:
+ return (key, '%s ( /path/to/%s )' % (help, key), default,
+ validator, None)
+
+PathVariable = _PathVariableClass()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/PathVariableTests.py b/src/engine/SCons/Variables/PathVariableTests.py
new file mode 100644
index 0000000..e9e35e7
--- /dev/null
+++ b/src/engine/SCons/Variables/PathVariableTests.py
@@ -0,0 +1,237 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/PathVariableTests.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Variables
+
+import TestCmd
+
+class PathVariableTestCase(unittest.TestCase):
+ def test_PathVariable(self):
+ """Test PathVariable creation"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path'))
+
+ o = opts.options[0]
+ assert o.key == 'test', o.key
+ assert o.help == 'test option help ( /path/to/test )', repr(o.help)
+ assert o.default == '/default/path', o.default
+ assert o.validator is not None, o.validator
+ assert o.converter is None, o.converter
+
+ def test_PathExists(self):
+ """Test the PathExists validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path',
+ SCons.Variables.PathVariable.PathExists))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.write('exists', 'exists\n')
+
+ o = opts.options[0]
+
+ o.validator('X', test.workpath('exists'), {})
+
+ dne = test.workpath('does_not_exist')
+ try:
+ o.validator('X', dne, {})
+ except SCons.Errors.UserError, e:
+ assert str(e) == 'Path for option X does not exist: %s' % dne, e
+ except:
+ raise "did not catch expected UserError"
+
+ def test_PathIsDir(self):
+ """Test the PathIsDir validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path',
+ SCons.Variables.PathVariable.PathIsDir))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.subdir('dir')
+ test.write('file', "file\n")
+
+ o = opts.options[0]
+
+ o.validator('X', test.workpath('dir'), {})
+
+ f = test.workpath('file')
+ try:
+ o.validator('X', f, {})
+ except SCons.Errors.UserError, e:
+ assert str(e) == 'Directory path for option X is a file: %s' % f, e
+ except:
+ raise "did not catch expected UserError"
+
+ dne = test.workpath('does_not_exist')
+ try:
+ o.validator('X', dne, {})
+ except SCons.Errors.UserError, e:
+ assert str(e) == 'Directory path for option X does not exist: %s' % dne, e
+ except:
+ raise "did not catch expected UserError"
+
+ def test_PathIsDirCreate(self):
+ """Test the PathIsDirCreate validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path',
+ SCons.Variables.PathVariable.PathIsDirCreate))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.write('file', "file\n")
+
+ o = opts.options[0]
+
+ d = test.workpath('dir')
+ o.validator('X', d, {})
+ assert os.path.isdir(d)
+
+ f = test.workpath('file')
+ try:
+ o.validator('X', f, {})
+ except SCons.Errors.UserError, e:
+ assert str(e) == 'Path for option X is a file, not a directory: %s' % f, e
+ except:
+ raise "did not catch expected UserError"
+
+ def test_PathIsFile(self):
+ """Test the PathIsFile validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path',
+ SCons.Variables.PathVariable.PathIsFile))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.subdir('dir')
+ test.write('file', "file\n")
+
+ o = opts.options[0]
+
+ o.validator('X', test.workpath('file'), {})
+
+ d = test.workpath('d')
+ try:
+ o.validator('X', d, {})
+ except SCons.Errors.UserError, e:
+ assert str(e) == 'File path for option X does not exist: %s' % d, e
+ except:
+ raise "did not catch expected UserError"
+
+ dne = test.workpath('does_not_exist')
+ try:
+ o.validator('X', dne, {})
+ except SCons.Errors.UserError, e:
+ assert str(e) == 'File path for option X does not exist: %s' % dne, e
+ except:
+ raise "did not catch expected UserError"
+
+ def test_PathAccept(self):
+ """Test the PathAccept validator"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path',
+ SCons.Variables.PathVariable.PathAccept))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.subdir('dir')
+ test.write('file', "file\n")
+
+ o = opts.options[0]
+
+ o.validator('X', test.workpath('file'), {})
+
+ d = test.workpath('d')
+ o.validator('X', d, {})
+
+ dne = test.workpath('does_not_exist')
+ o.validator('X', dne, {})
+
+ def test_validator(self):
+ """Test the PathVariable validator argument"""
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test',
+ 'test option help',
+ '/default/path'))
+
+ test = TestCmd.TestCmd(workdir='')
+ test.write('exists', 'exists\n')
+
+ o = opts.options[0]
+
+ o.validator('X', test.workpath('exists'), {})
+
+ dne = test.workpath('does_not_exist')
+ try:
+ o.validator('X', dne, {})
+ except SCons.Errors.UserError, e:
+ expect = 'Path for option X does not exist: %s' % dne
+ assert str(e) == expect, e
+ else:
+ raise "did not catch expected UserError"
+
+ def my_validator(key, val, env):
+ raise Exception, "my_validator() got called for %s, %s!" % (key, val)
+
+ opts = SCons.Variables.Variables()
+ opts.Add(SCons.Variables.PathVariable('test2',
+ 'more help',
+ '/default/path/again',
+ my_validator))
+
+ o = opts.options[0]
+
+ try:
+ o.validator('Y', 'value', {})
+ except Exception, e:
+ assert str(e) == 'my_validator() got called for Y, value!', e
+ else:
+ raise "did not catch expected exception from my_validator()"
+
+
+
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(PathVariableTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/VariablesTests.py b/src/engine/SCons/Variables/VariablesTests.py
new file mode 100644
index 0000000..b89404b
--- /dev/null
+++ b/src/engine/SCons/Variables/VariablesTests.py
@@ -0,0 +1,663 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/VariablesTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+import TestSCons
+
+import SCons.Variables
+import SCons.Subst
+import SCons.Warnings
+
+
+class Environment:
+ def __init__(self):
+ self.dict = {}
+ def subst(self, x):
+ return SCons.Subst.scons_subst(x, self, gvars=self.dict)
+ def __setitem__(self, key, value):
+ self.dict[key] = value
+ def __getitem__(self, key):
+ return self.dict[key]
+ def has_key(self, key):
+ return self.dict.has_key(key)
+
+
+def check(key, value, env):
+ assert int(value) == 6 * 9, "key %s = %s" % (key, repr(value))
+
+# Check saved option file by executing and comparing against
+# the expected dictionary
+def checkSave(file, expected):
+ gdict = {}
+ ldict = {}
+ exec open(file, 'rU').read() in gdict, ldict
+ assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
+
+class VariablesTestCase(unittest.TestCase):
+
+ def test_keys(self):
+ """Test the Variables.keys() method"""
+ opts = SCons.Variables.Variables()
+
+ opts.Add('VAR1')
+ opts.Add('VAR2',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+ keys = opts.keys()
+ assert keys == ['VAR1', 'VAR2'], keys
+
+ def test_Add(self):
+ """Test adding to a Variables object"""
+ opts = SCons.Variables.Variables()
+
+ opts.Add('VAR')
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ o = opts.options[0]
+ assert o.key == 'VAR'
+ assert o.help == ''
+ assert o.default is None
+ assert o.validator is None
+ assert o.converter is None
+
+ o = opts.options[1]
+ assert o.key == 'ANSWER'
+ assert o.help == 'THE answer to THE question'
+ assert o.default == "42"
+ o.validator(o.key, o.converter(o.default), {})
+
+ def test_it(var, opts=opts):
+ exc_caught = None
+ try:
+ opts.Add(var)
+ except SCons.Errors.UserError:
+ exc_caught = 1
+ assert exc_caught, "did not catch UserError for '%s'" % var
+ test_it('foo/bar')
+ test_it('foo-bar')
+ test_it('foo.bar')
+
+ def test_AddVariables(self):
+ """Test adding a list of options to a Variables object"""
+ opts = SCons.Variables.Variables()
+
+ opts.AddVariables(('VAR2',),
+ ('ANSWER2',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12))
+
+ o = opts.options[0]
+ assert o.key == 'VAR2', o.key
+ assert o.help == '', o.help
+ assert o.default is None, o.default
+ assert o.validator is None, o.validator
+ assert o.converter is None, o.converter
+
+ o = opts.options[1]
+ assert o.key == 'ANSWER2', o.key
+ assert o.help == 'THE answer to THE question', o.help
+ assert o.default == "42", o.default
+ o.validator(o.key, o.converter(o.default), {})
+
+ def test_Update(self):
+ """Test updating an Environment"""
+
+ # Test that a default value is validated correctly.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env)
+ assert env['ANSWER'] == 54
+
+ env = Environment()
+ opts.Update(env, {})
+ assert env['ANSWER'] == 54
+
+ # Test that a bad value from the file is used and
+ # validation fails correctly.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=54')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ exc_caught = None
+ try:
+ opts.Update(env)
+ except AssertionError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected assertion"
+
+ env = Environment()
+ exc_caught = None
+ try:
+ opts.Update(env, {})
+ except AssertionError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected assertion"
+
+ # Test that a good value from the file is used and validated.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=42')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "10",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env)
+ assert env['ANSWER'] == 54
+
+ env = Environment()
+ opts.Update(env, {})
+ assert env['ANSWER'] == 54
+
+ # Test that a bad value from an args dictionary passed to
+ # Update() is used and validation fails correctly.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=10')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "12",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ exc_caught = None
+ try:
+ opts.Update(env, {'ANSWER':'54'})
+ except AssertionError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected assertion"
+
+ # Test that a good value from an args dictionary
+ # passed to Update() is used and validated.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=10')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "12",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env, {'ANSWER':'42'})
+ assert env['ANSWER'] == 54
+
+ # Test against a former bug. If we supply a converter,
+ # but no default, the value should *not* appear in the
+ # Environment if no value is specified in the options file
+ # or args.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ help='THE answer to THE question',
+ converter=str)
+
+ env = Environment()
+ opts.Update(env, {})
+ assert not env.has_key('ANSWER')
+
+ # Test that a default value of None is all right.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ opts = SCons.Variables.Variables(file)
+
+ opts.Add('ANSWER',
+ "This is the answer",
+ None,
+ check)
+
+ env = Environment()
+ opts.Update(env, {})
+ assert not env.has_key('ANSWER')
+
+ def test_args(self):
+ """Test updating an Environment with arguments overridden"""
+
+ # Test that a bad (command-line) argument is used
+ # and the validation fails correctly.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=42')
+ opts = SCons.Variables.Variables(file, {'ANSWER':54})
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ exc_caught = None
+ try:
+ opts.Update(env)
+ except AssertionError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected assertion"
+
+ # Test that a good (command-line) argument is used and validated.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=54')
+ opts = SCons.Variables.Variables(file, {'ANSWER':42})
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "54",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env)
+ assert env['ANSWER'] == 54
+
+ # Test that a (command-line) argument is overridden by a dictionary
+ # supplied to Update() and the dictionary value is validated correctly.
+ test = TestSCons.TestSCons()
+ file = test.workpath('custom.py')
+ test.write('custom.py', 'ANSWER=54')
+ opts = SCons.Variables.Variables(file, {'ANSWER':54})
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "54",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env, {'ANSWER':42})
+ assert env['ANSWER'] == 54
+
+ def test_Save(self):
+ """Testing saving Variables"""
+
+ test = TestSCons.TestSCons()
+ cache_file = test.workpath('cached.options')
+ opts = SCons.Variables.Variables()
+
+ def bool_converter(val):
+ if val in [1, 'y']: val = 1
+ if val in [0, 'n']: val = 0
+ return val
+
+ # test saving out empty file
+ opts.Add('OPT_VAL',
+ 'An option to test',
+ 21,
+ None,
+ None)
+ opts.Add('OPT_VAL_2',
+ default='foo')
+ opts.Add('OPT_VAL_3',
+ default=1)
+ opts.Add('OPT_BOOL_0',
+ default='n',
+ converter=bool_converter)
+ opts.Add('OPT_BOOL_1',
+ default='y',
+ converter=bool_converter)
+ opts.Add('OPT_BOOL_2',
+ default=0,
+ converter=bool_converter)
+
+ env = Environment()
+ opts.Update(env, {'OPT_VAL_3' : 2})
+ assert env['OPT_VAL'] == 21, env['OPT_VAL']
+ assert env['OPT_VAL_2'] == 'foo', env['OPT_VAL_2']
+ assert env['OPT_VAL_3'] == 2, env['OPT_VAL_3']
+ assert env['OPT_BOOL_0'] == 0, env['OPT_BOOL_0']
+ assert env['OPT_BOOL_1'] == 1, env['OPT_BOOL_1']
+ assert env['OPT_BOOL_2'] == '0', env['OPT_BOOL_2']
+
+ env['OPT_VAL_2'] = 'bar'
+ env['OPT_BOOL_0'] = 0
+ env['OPT_BOOL_1'] = 1
+ env['OPT_BOOL_2'] = 2
+
+ opts.Save(cache_file, env)
+ checkSave(cache_file, { 'OPT_VAL_2' : 'bar',
+ 'OPT_VAL_3' : 2,
+ 'OPT_BOOL_2' : 2})
+
+ # Test against some old bugs
+ class Foo:
+ def __init__(self, x):
+ self.x = x
+ def __str__(self):
+ return self.x
+
+ test = TestSCons.TestSCons()
+ cache_file = test.workpath('cached.options')
+ opts = SCons.Variables.Variables()
+
+ opts.Add('THIS_USED_TO_BREAK',
+ 'An option to test',
+ "Default")
+
+ opts.Add('THIS_ALSO_BROKE',
+ 'An option to test',
+ "Default2")
+
+ opts.Add('THIS_SHOULD_WORK',
+ 'An option to test',
+ Foo('bar'))
+
+ env = Environment()
+ opts.Update(env, { 'THIS_USED_TO_BREAK' : "Single'Quotes'In'String",
+ 'THIS_ALSO_BROKE' : "\\Escape\nSequences\t",
+ 'THIS_SHOULD_WORK' : Foo('baz') })
+ opts.Save(cache_file, env)
+ checkSave(cache_file, { 'THIS_USED_TO_BREAK' : "Single'Quotes'In'String",
+ 'THIS_ALSO_BROKE' : "\\Escape\nSequences\t",
+ 'THIS_SHOULD_WORK' : 'baz' })
+
+ def test_GenerateHelpText(self):
+ """Test generating the default format help text"""
+ opts = SCons.Variables.Variables()
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ opts.Add('B',
+ 'b - alpha test',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ opts.Add('A',
+ 'a - alpha test',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env, {})
+
+ expect = """
+ANSWER: THE answer to THE question
+ default: 42
+ actual: 54
+
+B: b - alpha test
+ default: 42
+ actual: 54
+
+A: a - alpha test
+ default: 42
+ actual: 54
+"""
+
+ text = opts.GenerateHelpText(env)
+ assert text == expect, text
+
+ expectAlpha = """
+A: a - alpha test
+ default: 42
+ actual: 54
+
+ANSWER: THE answer to THE question
+ default: 42
+ actual: 54
+
+B: b - alpha test
+ default: 42
+ actual: 54
+"""
+ text = opts.GenerateHelpText(env, sort=cmp)
+ assert text == expectAlpha, text
+
+ def test_FormatVariableHelpText(self):
+ """Test generating custom format help text"""
+ opts = SCons.Variables.Variables()
+
+ def my_format(env, opt, help, default, actual, aliases):
+ return '%s %s %s %s %s\n' % (opt, default, actual, help, aliases)
+
+ opts.FormatVariableHelpText = my_format
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ opts.Add('B',
+ 'b - alpha test',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ opts.Add('A',
+ 'a - alpha test',
+ "42",
+ check,
+ lambda x: int(x) + 12)
+
+ env = Environment()
+ opts.Update(env, {})
+
+ expect = """\
+ANSWER 42 54 THE answer to THE question ['ANSWER']
+B 42 54 b - alpha test ['B']
+A 42 54 a - alpha test ['A']
+"""
+
+ text = opts.GenerateHelpText(env)
+ assert text == expect, text
+
+ expectAlpha = """\
+A 42 54 a - alpha test ['A']
+ANSWER 42 54 THE answer to THE question ['ANSWER']
+B 42 54 b - alpha test ['B']
+"""
+ text = opts.GenerateHelpText(env, sort=cmp)
+ assert text == expectAlpha, text
+
+ def test_Aliases(self):
+ """Test option aliases"""
+ # test alias as a tuple
+ opts = SCons.Variables.Variables()
+ opts.AddVariables(
+ (('ANSWER', 'ANSWERALIAS'),
+ 'THE answer to THE question',
+ "42"),
+ )
+
+ env = Environment()
+ opts.Update(env, {'ANSWER' : 'answer'})
+
+ assert env.has_key('ANSWER')
+
+ env = Environment()
+ opts.Update(env, {'ANSWERALIAS' : 'answer'})
+
+ assert env.has_key('ANSWER') and not env.has_key('ANSWERALIAS')
+
+ # test alias as a list
+ opts = SCons.Variables.Variables()
+ opts.AddVariables(
+ (['ANSWER', 'ANSWERALIAS'],
+ 'THE answer to THE question',
+ "42"),
+ )
+
+ env = Environment()
+ opts.Update(env, {'ANSWER' : 'answer'})
+
+ assert env.has_key('ANSWER')
+
+ env = Environment()
+ opts.Update(env, {'ANSWERALIAS' : 'answer'})
+
+ assert env.has_key('ANSWER') and not env.has_key('ANSWERALIAS')
+
+
+
+class UnknownVariablesTestCase(unittest.TestCase):
+
+ def test_unknown(self):
+ """Test the UnknownVariables() method"""
+ opts = SCons.Variables.Variables()
+
+ opts.Add('ANSWER',
+ 'THE answer to THE question',
+ "42")
+
+ args = {
+ 'ANSWER' : 'answer',
+ 'UNKNOWN' : 'unknown',
+ }
+
+ env = Environment()
+ opts.Update(env, args)
+
+ r = opts.UnknownVariables()
+ assert r == {'UNKNOWN' : 'unknown'}, r
+ assert env['ANSWER'] == 'answer', env['ANSWER']
+
+ def test_AddOptionUpdatesUnknown(self):
+ """Test updating of the 'unknown' dict"""
+ opts = SCons.Variables.Variables()
+
+ opts.Add('A',
+ 'A test variable',
+ "1")
+
+ args = {
+ 'A' : 'a',
+ 'ADDEDLATER' : 'notaddedyet',
+ }
+
+ env = Environment()
+ opts.Update(env,args)
+
+ r = opts.UnknownVariables()
+ assert r == {'ADDEDLATER' : 'notaddedyet'}, r
+ assert env['A'] == 'a', env['A']
+
+ opts.Add('ADDEDLATER',
+ 'An option not present initially',
+ "1")
+
+ args = {
+ 'A' : 'a',
+ 'ADDEDLATER' : 'added',
+ }
+
+ opts.Update(env, args)
+
+ r = opts.UnknownVariables()
+ assert len(r) == 0, r
+ assert env['ADDEDLATER'] == 'added', env['ADDEDLATER']
+
+ def test_AddOptionWithAliasUpdatesUnknown(self):
+ """Test updating of the 'unknown' dict (with aliases)"""
+ opts = SCons.Variables.Variables()
+
+ opts.Add('A',
+ 'A test variable',
+ "1")
+
+ args = {
+ 'A' : 'a',
+ 'ADDEDLATERALIAS' : 'notaddedyet',
+ }
+
+ env = Environment()
+ opts.Update(env,args)
+
+ r = opts.UnknownVariables()
+ assert r == {'ADDEDLATERALIAS' : 'notaddedyet'}, r
+ assert env['A'] == 'a', env['A']
+
+ opts.AddVariables(
+ (('ADDEDLATER', 'ADDEDLATERALIAS'),
+ 'An option not present initially',
+ "1"),
+ )
+
+ args['ADDEDLATERALIAS'] = 'added'
+
+ opts.Update(env, args)
+
+ r = opts.UnknownVariables()
+ assert len(r) == 0, r
+ assert env['ADDEDLATER'] == 'added', env['ADDEDLATER']
+
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [ VariablesTestCase,
+ UnknownVariablesTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Variables/__init__.py b/src/engine/SCons/Variables/__init__.py
new file mode 100644
index 0000000..7ab711a
--- /dev/null
+++ b/src/engine/SCons/Variables/__init__.py
@@ -0,0 +1,317 @@
+"""engine.SCons.Variables
+
+This file defines the Variables class that is used to add user-friendly
+customizable variables to an SCons build.
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Variables/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import string
+import sys
+
+import SCons.Environment
+import SCons.Errors
+import SCons.Util
+import SCons.Warnings
+
+from BoolVariable import BoolVariable # okay
+from EnumVariable import EnumVariable # okay
+from ListVariable import ListVariable # naja
+from PackageVariable import PackageVariable # naja
+from PathVariable import PathVariable # okay
+
+
+class Variables:
+ instance=None
+
+ """
+ Holds all the options, updates the environment with the variables,
+ and renders the help text.
+ """
+ def __init__(self, files=[], args={}, is_global=1):
+ """
+ files - [optional] List of option configuration files to load
+ (backward compatibility) If a single string is passed it is
+ automatically placed in a file list
+ """
+ self.options = []
+ self.args = args
+ if not SCons.Util.is_List(files):
+ if files:
+ files = [ files ]
+ else:
+ files = []
+ self.files = files
+ self.unknown = {}
+
+ # create the singleton instance
+ if is_global:
+ self=Variables.instance
+
+ if not Variables.instance:
+ Variables.instance=self
+
+ def _do_add(self, key, help="", default=None, validator=None, converter=None):
+ class Variable:
+ pass
+
+ option = Variable()
+
+ # if we get a list or a tuple, we take the first element as the
+ # option key and store the remaining in aliases.
+ if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key):
+ option.key = key[0]
+ option.aliases = key[1:]
+ else:
+ option.key = key
+ option.aliases = [ key ]
+ option.help = help
+ option.default = default
+ option.validator = validator
+ option.converter = converter
+
+ self.options.append(option)
+
+ # options might be added after the 'unknown' dict has been set up,
+ # so we remove the key and all its aliases from that dict
+ for alias in list(option.aliases) + [ option.key ]:
+ # TODO(1.5)
+ #if alias in self.unknown:
+ if alias in self.unknown.keys():
+ del self.unknown[alias]
+
+ def keys(self):
+ """
+ Returns the keywords for the options
+ """
+ return map(lambda o: o.key, self.options)
+
+ def Add(self, key, help="", default=None, validator=None, converter=None, **kw):
+ """
+ Add an option.
+
+ key - the name of the variable, or a list or tuple of arguments
+ help - optional help text for the options
+ default - optional default value
+ validator - optional function that is called to validate the option's value
+ Called with (key, value, environment)
+ converter - optional function that is called to convert the option's value before
+ putting it in the environment.
+ """
+
+ if SCons.Util.is_List(key) or type(key) == type(()):
+ apply(self._do_add, key)
+ return
+
+ if not SCons.Util.is_String(key) or \
+ not SCons.Environment.is_valid_construction_var(key):
+ raise SCons.Errors.UserError, "Illegal Variables.Add() key `%s'" % str(key)
+
+ self._do_add(key, help, default, validator, converter)
+
+ def AddVariables(self, *optlist):
+ """
+ Add a list of options.
+
+ Each list element is a tuple/list of arguments to be passed on
+ to the underlying method for adding options.
+
+ Example:
+ opt.AddVariables(
+ ('debug', '', 0),
+ ('CC', 'The C compiler'),
+ ('VALIDATE', 'An option for testing validation', 'notset',
+ validator, None),
+ )
+ """
+ for o in optlist:
+ apply(self._do_add, o)
+
+
+ def Update(self, env, args=None):
+ """
+ Update an environment with the option variables.
+
+ env - the environment to update.
+ """
+
+ values = {}
+
+ # first set the defaults:
+ for option in self.options:
+ if not option.default is None:
+ values[option.key] = option.default
+
+ # next set the value specified in the options file
+ for filename in self.files:
+ if os.path.exists(filename):
+ dir = os.path.split(os.path.abspath(filename))[0]
+ if dir:
+ sys.path.insert(0, dir)
+ try:
+ values['__name__'] = filename
+ exec open(filename, 'rU').read() in {}, values
+ finally:
+ if dir:
+ del sys.path[0]
+ del values['__name__']
+
+ # set the values specified on the command line
+ if args is None:
+ args = self.args
+
+ for arg, value in args.items():
+ added = False
+ for option in self.options:
+ if arg in list(option.aliases) + [ option.key ]:
+ values[option.key] = value
+ added = True
+ if not added:
+ self.unknown[arg] = value
+
+ # put the variables in the environment:
+ # (don't copy over variables that are not declared as options)
+ for option in self.options:
+ try:
+ env[option.key] = values[option.key]
+ except KeyError:
+ pass
+
+ # Call the convert functions:
+ for option in self.options:
+ if option.converter and values.has_key(option.key):
+ value = env.subst('${%s}'%option.key)
+ try:
+ try:
+ env[option.key] = option.converter(value)
+ except TypeError:
+ env[option.key] = option.converter(value, env)
+ except ValueError, x:
+ raise SCons.Errors.UserError, 'Error converting option: %s\n%s'%(option.key, x)
+
+
+ # Finally validate the values:
+ for option in self.options:
+ if option.validator and values.has_key(option.key):
+ option.validator(option.key, env.subst('${%s}'%option.key), env)
+
+ def UnknownVariables(self):
+ """
+ Returns any options in the specified arguments lists that
+ were not known, declared options in this object.
+ """
+ return self.unknown
+
+ def Save(self, filename, env):
+ """
+ Saves all the options in the given file. This file can
+ then be used to load the options next run. This can be used
+ to create an option cache file.
+
+ filename - Name of the file to save into
+ env - the environment get the option values from
+ """
+
+ # Create the file and write out the header
+ try:
+ fh = open(filename, 'w')
+
+ try:
+ # Make an assignment in the file for each option
+ # within the environment that was assigned a value
+ # other than the default.
+ for option in self.options:
+ try:
+ value = env[option.key]
+ try:
+ prepare = value.prepare_to_store
+ except AttributeError:
+ try:
+ eval(repr(value))
+ except KeyboardInterrupt:
+ raise
+ except:
+ # Convert stuff that has a repr() that
+ # cannot be evaluated into a string
+ value = SCons.Util.to_String(value)
+ else:
+ value = prepare()
+
+ defaultVal = env.subst(SCons.Util.to_String(option.default))
+ if option.converter:
+ defaultVal = option.converter(defaultVal)
+
+ if str(env.subst('${%s}' % option.key)) != str(defaultVal):
+ fh.write('%s = %s\n' % (option.key, repr(value)))
+ except KeyError:
+ pass
+ finally:
+ fh.close()
+
+ except IOError, x:
+ raise SCons.Errors.UserError, 'Error writing options to file: %s\n%s' % (filename, x)
+
+ def GenerateHelpText(self, env, sort=None):
+ """
+ Generate the help text for the options.
+
+ env - an environment that is used to get the current values
+ of the options.
+ """
+
+ if sort:
+ options = self.options[:]
+ options.sort(lambda x,y,func=sort: func(x.key,y.key))
+ else:
+ options = self.options
+
+ def format(opt, self=self, env=env):
+ if env.has_key(opt.key):
+ actual = env.subst('${%s}' % opt.key)
+ else:
+ actual = None
+ return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases)
+ lines = filter(None, map(format, options))
+
+ return string.join(lines, '')
+
+ format = '\n%s: %s\n default: %s\n actual: %s\n'
+ format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n'
+
+ def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]):
+ # Don't display the key name itself as an alias.
+ aliases = filter(lambda a, k=key: a != k, aliases)
+ if len(aliases)==0:
+ return self.format % (key, help, default, actual)
+ else:
+ return self.format_ % (key, help, default, actual, aliases)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py
new file mode 100644
index 0000000..8f290f7
--- /dev/null
+++ b/src/engine/SCons/Warnings.py
@@ -0,0 +1,228 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""SCons.Warnings
+
+This file implements the warnings framework for SCons.
+
+"""
+
+__revision__ = "src/engine/SCons/Warnings.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+import sys
+
+import SCons.Errors
+
+class Warning(SCons.Errors.UserError):
+ pass
+
+class MandatoryWarning(Warning):
+ pass
+
+
+
+class FutureDeprecatedWarning(Warning):
+ pass
+
+class DeprecatedWarning(Warning):
+ pass
+
+class MandatoryDeprecatedWarning(MandatoryWarning):
+ pass
+
+
+
+# NOTE: If you add a new warning class, add it to the man page, too!
+
+class CacheWriteErrorWarning(Warning):
+ pass
+
+class CorruptSConsignWarning(Warning):
+ pass
+
+class DependencyWarning(Warning):
+ pass
+
+class DeprecatedCopyWarning(DeprecatedWarning):
+ pass
+
+class DeprecatedOptionsWarning(DeprecatedWarning):
+ pass
+
+class DeprecatedSourceSignaturesWarning(DeprecatedWarning):
+ pass
+
+class DeprecatedTargetSignaturesWarning(DeprecatedWarning):
+ pass
+
+class DuplicateEnvironmentWarning(Warning):
+ pass
+
+class FutureReservedVariableWarning(Warning):
+ pass
+
+class LinkWarning(Warning):
+ pass
+
+class MisleadingKeywordsWarning(Warning):
+ pass
+
+class MissingSConscriptWarning(Warning):
+ pass
+
+class NoMD5ModuleWarning(Warning):
+ pass
+
+class NoMetaclassSupportWarning(Warning):
+ pass
+
+class NoObjectCountWarning(Warning):
+ pass
+
+class NoParallelSupportWarning(Warning):
+ pass
+
+class PythonVersionWarning(DeprecatedWarning):
+ pass
+
+class ReservedVariableWarning(Warning):
+ pass
+
+class StackSizeWarning(Warning):
+ pass
+
+class TaskmasterNeedsExecuteWarning(FutureDeprecatedWarning):
+ pass
+
+class VisualCMissingWarning(Warning):
+ pass
+
+# Used when MSVC_VERSION and MSVS_VERSION do not point to the
+# same version (MSVS_VERSION is deprecated)
+class VisualVersionMismatch(Warning):
+ pass
+
+class VisualStudioMissingWarning(Warning):
+ pass
+
+class FortranCxxMixWarning(LinkWarning):
+ pass
+
+_warningAsException = 0
+
+# The below is a list of 2-tuples. The first element is a class object.
+# The second element is true if that class is enabled, false if it is disabled.
+_enabled = []
+
+_warningOut = None
+
+def suppressWarningClass(clazz):
+ """Suppresses all warnings that are of type clazz or
+ derived from clazz."""
+ _enabled.insert(0, (clazz, 0))
+
+def enableWarningClass(clazz):
+ """Suppresses all warnings that are of type clazz or
+ derived from clazz."""
+ _enabled.insert(0, (clazz, 1))
+
+def warningAsException(flag=1):
+ """Turn warnings into exceptions. Returns the old value of the flag."""
+ global _warningAsException
+ old = _warningAsException
+ _warningAsException = flag
+ return old
+
+def warn(clazz, *args):
+ global _enabled, _warningAsException, _warningOut
+
+ warning = clazz(args)
+ for clazz, flag in _enabled:
+ if isinstance(warning, clazz):
+ if flag:
+ if _warningAsException:
+ raise warning
+
+ if _warningOut:
+ _warningOut(warning)
+ break
+
+def process_warn_strings(arguments):
+ """Process string specifications of enabling/disabling warnings,
+ as passed to the --warn option or the SetOption('warn') function.
+
+
+ An argument to this option should be of the form <warning-class>
+ or no-<warning-class>. The warning class is munged in order
+ to get an actual class name from the classes above, which we
+ need to pass to the {enable,disable}WarningClass() functions.
+ The supplied <warning-class> is split on hyphens, each element
+ is capitalized, then smushed back together. Then the string
+ "Warning" is appended to get the class name.
+
+ For example, 'deprecated' will enable the DeprecatedWarning
+ class. 'no-dependency' will disable the .DependencyWarning
+ class.
+
+ As a special case, --warn=all and --warn=no-all will enable or
+ disable (respectively) the base Warning class of all warnings.
+
+ """
+
+ def _capitalize(s):
+ if s[:5] == "scons":
+ return "SCons" + s[5:]
+ else:
+ return string.capitalize(s)
+
+ for arg in arguments:
+
+ elems = string.split(string.lower(arg), '-')
+ enable = 1
+ if elems[0] == 'no':
+ enable = 0
+ del elems[0]
+
+ if len(elems) == 1 and elems[0] == 'all':
+ class_name = "Warning"
+ else:
+ class_name = string.join(map(_capitalize, elems), '') + "Warning"
+ try:
+ clazz = globals()[class_name]
+ except KeyError:
+ sys.stderr.write("No warning type: '%s'\n" % arg)
+ else:
+ if enable:
+ enableWarningClass(clazz)
+ elif issubclass(clazz, MandatoryDeprecatedWarning):
+ fmt = "Can not disable mandataory warning: '%s'\n"
+ sys.stderr.write(fmt % arg)
+ else:
+ suppressWarningClass(clazz)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/WarningsTests.py b/src/engine/SCons/WarningsTests.py
new file mode 100644
index 0000000..a4a7b58
--- /dev/null
+++ b/src/engine/SCons/WarningsTests.py
@@ -0,0 +1,135 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/WarningsTests.py 4577 2009/12/27 19:44:43 scons"
+
+import sys
+import unittest
+import SCons.Warnings
+
+class TestOutput:
+ def __call__(self, x):
+ args = x[0]
+ if len(args) == 1:
+ args = args[0]
+ self.out = str(args)
+
+class WarningsTestCase(unittest.TestCase):
+ def test_Warning(self):
+ """Test warn function."""
+
+ # Reset global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = 0
+
+ to = TestOutput()
+ SCons.Warnings._warningOut=to
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "Foo", 1)
+ assert to.out == "('Foo', 1)", to.out
+
+ def test_WarningAsExc(self):
+ """Test warnings as exceptions."""
+
+ # Reset global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = 0
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ old = SCons.Warnings.warningAsException()
+ assert old == 0, old
+ exc_caught = 0
+ try:
+ SCons.Warnings.warn(SCons.Warnings.Warning, "Foo")
+ except:
+ exc_caught = 1
+ assert exc_caught == 1
+
+ old = SCons.Warnings.warningAsException(old)
+ assert old == 1, old
+ exc_caught = 0
+ try:
+ SCons.Warnings.warn(SCons.Warnings.Warning, "Foo")
+ except:
+ exc_caught = 1
+ assert exc_caught == 0
+
+ def test_Disable(self):
+ """Test disabling/enabling warnings."""
+
+ # Reset global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = 0
+
+ to = TestOutput()
+ SCons.Warnings._warningOut=to
+ to.out = None
+
+ # No warnings by default
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out is None, to.out
+
+ SCons.Warnings.warn(SCons.Warnings.MandatoryWarning,
+ "Foo")
+ assert to.out is None, to.out
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+
+ to.out = None
+ SCons.Warnings.suppressWarningClass(SCons.Warnings.DeprecatedWarning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out is None, to.out
+
+ # Dependency warnings should still be enabled though
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+
+ # Try reenabling all warnings...
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(WarningsTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/__init__.py b/src/engine/SCons/__init__.py
new file mode 100644
index 0000000..dd6115a
--- /dev/null
+++ b/src/engine/SCons/__init__.py
@@ -0,0 +1,49 @@
+"""SCons
+
+The main package for the SCons software construction utility.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+__version__ = "1.2.0.d20091224"
+
+__build__ = "r4577[MODIFIED]"
+
+__buildsys__ = "scons-dev"
+
+__date__ = "2009/12/27 19:44:43"
+
+__developer__ = "scons"
+
+# make sure compatibility is always in place
+import SCons.compat
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py
new file mode 100644
index 0000000..6eb9340
--- /dev/null
+++ b/src/engine/SCons/compat/__init__.py
@@ -0,0 +1,302 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = """
+SCons compatibility package for old Python versions
+
+This subpackage holds modules that provide backwards-compatible
+implementations of various things that we'd like to use in SCons but which
+only show up in later versions of Python than the early, old version(s)
+we still support.
+
+Other code will not generally reference things in this package through
+the SCons.compat namespace. The modules included here add things to
+the __builtin__ namespace or the global module list so that the rest
+of our code can use the objects and names imported here regardless of
+Python version.
+
+Simply enough, things that go in the __builtin__ name space come from
+our builtins module.
+
+The rest of the things here will be in individual compatibility modules
+that are either: 1) suitably modified copies of the future modules that
+we want to use; or 2) backwards compatible re-implementations of the
+specific portions of a future module's API that we want to use.
+
+GENERAL WARNINGS: Implementations of functions in the SCons.compat
+modules are *NOT* guaranteed to be fully compliant with these functions in
+later versions of Python. We are only concerned with adding functionality
+that we actually use in SCons, so be wary if you lift this code for
+other uses. (That said, making these more nearly the same as later,
+official versions is still a desirable goal, we just don't need to be
+obsessive about it.)
+
+We name the compatibility modules with an initial '_scons_' (for example,
+_scons_subprocess.py is our compatibility module for subprocess) so
+that we can still try to import the real module name and fall back to
+our compatibility module if we get an ImportError. The import_as()
+function defined below loads the module as the "real" name (without the
+'_scons'), after which all of the "import {module}" statements in the
+rest of our code will find our pre-loaded compatibility module.
+"""
+
+__revision__ = "src/engine/SCons/compat/__init__.py 4577 2009/12/27 19:44:43 scons"
+
+def import_as(module, name):
+ """
+ Imports the specified module (from our local directory) as the
+ specified name.
+ """
+ import imp
+ import os.path
+ dir = os.path.split(__file__)[0]
+ file, filename, suffix_mode_type = imp.find_module(module, [dir])
+ imp.load_module(name, file, filename, suffix_mode_type)
+
+import builtins
+
+try:
+ import hashlib
+except ImportError:
+ # Pre-2.5 Python has no hashlib module.
+ try:
+ import_as('_scons_hashlib', 'hashlib')
+ except ImportError:
+ # If we failed importing our compatibility module, it probably
+ # means this version of Python has no md5 module. Don't do
+ # anything and let the higher layer discover this fact, so it
+ # can fall back to using timestamp.
+ pass
+
+try:
+ set
+except NameError:
+ # Pre-2.4 Python has no native set type
+ try:
+ # Python 2.2 and 2.3 can use the copy of the 2.[45] sets module
+ # that we grabbed.
+ import_as('_scons_sets', 'sets')
+ except (ImportError, SyntaxError):
+ # Python 1.5 (ImportError, no __future_ module) and 2.1
+ # (SyntaxError, no generators in __future__) will blow up
+ # trying to import the 2.[45] sets module, so back off to a
+ # custom sets module that can be discarded easily when we
+ # stop supporting those versions.
+ import_as('_scons_sets15', 'sets')
+ import __builtin__
+ import sets
+ __builtin__.set = sets.Set
+
+import fnmatch
+try:
+ fnmatch.filter
+except AttributeError:
+ # Pre-2.2 Python has no fnmatch.filter() function.
+ def filter(names, pat):
+ """Return the subset of the list NAMES that match PAT"""
+ import os,posixpath
+ result=[]
+ pat = os.path.normcase(pat)
+ if not fnmatch._cache.has_key(pat):
+ import re
+ res = fnmatch.translate(pat)
+ fnmatch._cache[pat] = re.compile(res)
+ match = fnmatch._cache[pat].match
+ if os.path is posixpath:
+ # normcase on posix is NOP. Optimize it away from the loop.
+ for name in names:
+ if match(name):
+ result.append(name)
+ else:
+ for name in names:
+ if match(os.path.normcase(name)):
+ result.append(name)
+ return result
+ fnmatch.filter = filter
+ del filter
+
+try:
+ import itertools
+except ImportError:
+ # Pre-2.3 Python has no itertools module.
+ import_as('_scons_itertools', 'itertools')
+
+# If we need the compatibility version of textwrap, it must be imported
+# before optparse, which uses it.
+try:
+ import textwrap
+except ImportError:
+ # Pre-2.3 Python has no textwrap module.
+ import_as('_scons_textwrap', 'textwrap')
+
+try:
+ import optparse
+except ImportError:
+ # Pre-2.3 Python has no optparse module.
+ import_as('_scons_optparse', 'optparse')
+
+import os
+try:
+ os.devnull
+except AttributeError:
+ # Pre-2.4 Python has no os.devnull attribute
+ import sys
+ _names = sys.builtin_module_names
+ if 'posix' in _names:
+ os.devnull = '/dev/null'
+ elif 'nt' in _names:
+ os.devnull = 'nul'
+ os.path.devnull = os.devnull
+try:
+ os.path.lexists
+except AttributeError:
+ # Pre-2.4 Python has no os.path.lexists function
+ def lexists(path):
+ return os.path.exists(path) or os.path.islink(path)
+ os.path.lexists = lexists
+
+
+try:
+ import platform
+except ImportError:
+ # Pre-2.3 Python has no platform module.
+ import_as('_scons_platform', 'platform')
+
+
+import shlex
+try:
+ shlex.split
+except AttributeError:
+ # Pre-2.3 Python has no shlex.split() function.
+ #
+ # The full white-space splitting semantics of shlex.split() are
+ # complicated to reproduce by hand, so just use a compatibility
+ # version of the shlex module cribbed from Python 2.5 with some
+ # minor modifications for older Python versions.
+ del shlex
+ import_as('_scons_shlex', 'shlex')
+
+
+import shutil
+try:
+ shutil.move
+except AttributeError:
+ # Pre-2.3 Python has no shutil.move() function.
+ #
+ # Cribbed from Python 2.5.
+ import os
+
+ def move(src, dst):
+ """Recursively move a file or directory to another location.
+
+ If the destination is on our current filesystem, then simply use
+ rename. Otherwise, copy src to the dst and then remove src.
+ A lot more could be done here... A look at a mv.c shows a lot of
+ the issues this implementation glosses over.
+
+ """
+ try:
+ os.rename(src, dst)
+ except OSError:
+ if os.path.isdir(src):
+ if shutil.destinsrc(src, dst):
+ raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
+ shutil.copytree(src, dst, symlinks=True)
+ shutil.rmtree(src)
+ else:
+ shutil.copy2(src,dst)
+ os.unlink(src)
+ shutil.move = move
+ del move
+
+ def destinsrc(src, dst):
+ src = os.path.abspath(src)
+ return os.path.abspath(dst)[:len(src)] == src
+ shutil.destinsrc = destinsrc
+ del destinsrc
+
+
+try:
+ import subprocess
+except ImportError:
+ # Pre-2.4 Python has no subprocess module.
+ import_as('_scons_subprocess', 'subprocess')
+
+import sys
+try:
+ sys.version_info
+except AttributeError:
+ # Pre-1.6 Python has no sys.version_info
+ import string
+ version_string = string.split(sys.version)[0]
+ version_ints = map(int, string.split(version_string, '.'))
+ sys.version_info = tuple(version_ints + ['final', 0])
+
+try:
+ import UserString
+except ImportError:
+ # Pre-1.6 Python has no UserString module.
+ import_as('_scons_UserString', 'UserString')
+
+import tempfile
+try:
+ tempfile.mkstemp
+except AttributeError:
+ # Pre-2.3 Python has no tempfile.mkstemp function, so try to simulate it.
+ # adapted from the mkstemp implementation in python 3.
+ import os
+ import errno
+ def mkstemp(*args, **kw):
+ text = False
+ # TODO (1.5)
+ #if 'text' in kw :
+ if 'text' in kw.keys() :
+ text = kw['text']
+ del kw['text']
+ elif len( args ) == 4 :
+ text = args[3]
+ args = args[:3]
+ flags = os.O_RDWR | os.O_CREAT | os.O_EXCL
+ if not text and hasattr( os, 'O_BINARY' ) :
+ flags = flags | os.O_BINARY
+ while True:
+ try :
+ name = apply(tempfile.mktemp, args, kw)
+ fd = os.open( name, flags, 0600 )
+ return (fd, os.path.abspath(name))
+ except OSError, e:
+ if e.errno == errno.EEXIST:
+ continue
+ raise
+
+ tempfile.mkstemp = mkstemp
+ del mkstemp
+
+
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_UserString.py b/src/engine/SCons/compat/_scons_UserString.py
new file mode 100644
index 0000000..932c216
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_UserString.py
@@ -0,0 +1,98 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/compat/_scons_UserString.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+A user-defined wrapper around string objects
+
+This class is "borrowed" from the Python 2.2 UserString and modified
+slightly for use with SCons. It is *NOT* guaranteed to be fully compliant
+with the standard UserString class from all later versions of Python.
+In particular, it does not necessarily contain all of the methods found
+in later versions.
+"""
+
+import types
+
+StringType = types.StringType
+
+if hasattr(types, 'UnicodeType'):
+ UnicodeType = types.UnicodeType
+ def is_String(obj):
+ return type(obj) in (StringType, UnicodeType)
+else:
+ def is_String(obj):
+ return type(obj) is StringType
+
+class UserString:
+ def __init__(self, seq):
+ if is_String(seq):
+ self.data = seq
+ elif isinstance(seq, UserString):
+ self.data = seq.data[:]
+ else:
+ self.data = str(seq)
+ def __str__(self): return str(self.data)
+ def __repr__(self): return repr(self.data)
+ def __int__(self): return int(self.data)
+ def __long__(self): return long(self.data)
+ def __float__(self): return float(self.data)
+ def __complex__(self): return complex(self.data)
+ def __hash__(self): return hash(self.data)
+
+ def __cmp__(self, string):
+ if isinstance(string, UserString):
+ return cmp(self.data, string.data)
+ else:
+ return cmp(self.data, string)
+ def __contains__(self, char):
+ return char in self.data
+
+ def __len__(self): return len(self.data)
+ def __getitem__(self, index): return self.__class__(self.data[index])
+ def __getslice__(self, start, end):
+ start = max(start, 0); end = max(end, 0)
+ return self.__class__(self.data[start:end])
+
+ def __add__(self, other):
+ if isinstance(other, UserString):
+ return self.__class__(self.data + other.data)
+ elif is_String(other):
+ return self.__class__(self.data + other)
+ else:
+ return self.__class__(self.data + str(other))
+ def __radd__(self, other):
+ if is_String(other):
+ return self.__class__(other + self.data)
+ else:
+ return self.__class__(str(other) + self.data)
+ def __mul__(self, n):
+ return self.__class__(self.data*n)
+ __rmul__ = __mul__
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_hashlib.py b/src/engine/SCons/compat/_scons_hashlib.py
new file mode 100644
index 0000000..bb4c69c
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_hashlib.py
@@ -0,0 +1,91 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = """
+hashlib backwards-compatibility module for older (pre-2.5) Python versions
+
+This does not not NOT (repeat, *NOT*) provide complete hashlib
+functionality. It only wraps the portions of MD5 functionality used
+by SCons, in an interface that looks like hashlib (or enough for our
+purposes, anyway). In fact, this module will raise an ImportError if
+the underlying md5 module isn't available.
+"""
+
+__revision__ = "src/engine/SCons/compat/_scons_hashlib.py 4577 2009/12/27 19:44:43 scons"
+
+import md5
+import string
+
+class md5obj:
+
+ md5_module = md5
+
+ def __init__(self, name, string=''):
+ if not name in ('MD5', 'md5'):
+ raise ValueError, "unsupported hash type"
+ self.name = 'md5'
+ self.m = self.md5_module.md5()
+
+ def __repr__(self):
+ return '<%s HASH object @ %#x>' % (self.name, id(self))
+
+ def copy(self):
+ import copy
+ result = copy.copy(self)
+ result.m = self.m.copy()
+ return result
+
+ def digest(self):
+ return self.m.digest()
+
+ def update(self, arg):
+ return self.m.update(arg)
+
+ if hasattr(md5.md5(), 'hexdigest'):
+
+ def hexdigest(self):
+ return self.m.hexdigest()
+
+ else:
+
+ # Objects created by the underlying md5 module have no native
+ # hexdigest() method (*cough* 1.5.2 *cough*), so provide an
+ # equivalent lifted from elsewhere.
+ def hexdigest(self):
+ h = string.hexdigits
+ r = ''
+ for c in self.digest():
+ i = ord(c)
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+ return r
+
+new = md5obj
+
+def md5(string=''):
+ return md5obj('md5', string)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_itertools.py b/src/engine/SCons/compat/_scons_itertools.py
new file mode 100644
index 0000000..f2e93c6
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_itertools.py
@@ -0,0 +1,124 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/compat/_scons_itertools.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+Implementations of itertools functions for Python versions that don't
+have iterators.
+
+These implement the functions by creating the entire list, not returning
+it element-by-element as the real itertools functions do. This means
+that early Python versions won't get the performance benefit of using
+the itertools, but we can still use them so the later Python versions
+do get the advantages of using iterators.
+
+Because we return the entire list, we intentionally do not implement the
+itertools functions that "return" infinitely-long lists: the count(),
+cycle() and repeat() functions. Other functions below have remained
+unimplemented simply because they aren't being used (yet) and it wasn't
+obvious how to do it. Or, conversely, we only implemented those functions
+that *were* easy to implement (mostly because the Python documentation
+contained examples of equivalent code).
+
+Note that these do not have independent unit tests, so it's possible
+that there are bugs.
+"""
+
+def chain(*iterables):
+ result = []
+ for x in iterables:
+ result.extend(list(x))
+ return result
+
+def count(n=0):
+ # returns infinite length, should not be supported
+ raise NotImplementedError
+
+def cycle(iterable):
+ # returns infinite length, should not be supported
+ raise NotImplementedError
+
+def dropwhile(predicate, iterable):
+ result = []
+ for x in iterable:
+ if not predicate(x):
+ result.append(x)
+ break
+ result.extend(iterable)
+ return result
+
+def groupby(iterable, *args):
+ raise NotImplementedError
+
+def ifilter(predicate, iterable):
+ result = []
+ if predicate is None:
+ predicate = bool
+ for x in iterable:
+ if predicate(x):
+ result.append(x)
+ return result
+
+def ifilterfalse(predicate, iterable):
+ result = []
+ if predicate is None:
+ predicate = bool
+ for x in iterable:
+ if not predicate(x):
+ result.append(x)
+ return result
+
+def imap(function, *iterables):
+ return apply(map, (function,) + tuple(iterables))
+
+def islice(*args, **kw):
+ raise NotImplementedError
+
+def izip(*iterables):
+ return apply(zip, iterables)
+
+def repeat(*args, **kw):
+ # returns infinite length, should not be supported
+ raise NotImplementedError
+
+def starmap(*args, **kw):
+ raise NotImplementedError
+
+def takewhile(predicate, iterable):
+ result = []
+ for x in iterable:
+ if predicate(x):
+ result.append(x)
+ else:
+ break
+ return result
+
+def tee(*args, **kw):
+ raise NotImplementedError
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_optparse.py b/src/engine/SCons/compat/_scons_optparse.py
new file mode 100644
index 0000000..219adba
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_optparse.py
@@ -0,0 +1,1725 @@
+"""optparse - a powerful, extensible, and easy-to-use option parser.
+
+By Greg Ward <gward@python.net>
+
+Originally distributed as Optik; see http://optik.sourceforge.net/ .
+
+If you have problems with this module, please do not file bugs,
+patches, or feature requests with Python; instead, use Optik's
+SourceForge project page:
+ http://sourceforge.net/projects/optik
+
+For support, use the optik-users@lists.sourceforge.net mailing list
+(http://lists.sourceforge.net/lists/listinfo/optik-users).
+"""
+
+# Python developers: please do not make changes to this file, since
+# it is automatically generated from the Optik source code.
+
+__version__ = "1.5.3"
+
+__all__ = ['Option',
+ 'SUPPRESS_HELP',
+ 'SUPPRESS_USAGE',
+ 'Values',
+ 'OptionContainer',
+ 'OptionGroup',
+ 'OptionParser',
+ 'HelpFormatter',
+ 'IndentedHelpFormatter',
+ 'TitledHelpFormatter',
+ 'OptParseError',
+ 'OptionError',
+ 'OptionConflictError',
+ 'OptionValueError',
+ 'BadOptionError']
+
+__copyright__ = """
+Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved.
+Copyright (c) 2002-2006 Python Software Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import string
+import sys, os
+import types
+import textwrap
+
+def _repr(self):
+ return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self)
+
+
+try:
+ sys.getdefaultencoding
+except AttributeError:
+ def fake_getdefaultencoding():
+ return None
+ sys.getdefaultencoding = fake_getdefaultencoding
+
+try:
+ ''.encode
+except AttributeError:
+ def encode_wrapper(s, encoding, replacement):
+ return s
+else:
+ def encode_wrapper(s, encoding, replacement):
+ return s.encode(encoding, replacement)
+
+
+# This file was generated from:
+# Id: option_parser.py 527 2006-07-23 15:21:30Z greg
+# Id: option.py 522 2006-06-11 16:22:03Z gward
+# Id: help.py 527 2006-07-23 15:21:30Z greg
+# Id: errors.py 509 2006-04-20 00:58:24Z gward
+
+try:
+ from gettext import gettext
+except ImportError:
+ def gettext(message):
+ return message
+_ = gettext
+
+
+class OptParseError (Exception):
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+
+class OptionError (OptParseError):
+ """
+ Raised if an Option instance is created with invalid or
+ inconsistent arguments.
+ """
+
+ def __init__(self, msg, option):
+ self.msg = msg
+ self.option_id = str(option)
+
+ def __str__(self):
+ if self.option_id:
+ return "option %s: %s" % (self.option_id, self.msg)
+ else:
+ return self.msg
+
+class OptionConflictError (OptionError):
+ """
+ Raised if conflicting options are added to an OptionParser.
+ """
+
+class OptionValueError (OptParseError):
+ """
+ Raised if an invalid option value is encountered on the command
+ line.
+ """
+
+class BadOptionError (OptParseError):
+ """
+ Raised if an invalid option is seen on the command line.
+ """
+ def __init__(self, opt_str):
+ self.opt_str = opt_str
+
+ def __str__(self):
+ return _("no such option: %s") % self.opt_str
+
+class AmbiguousOptionError (BadOptionError):
+ """
+ Raised if an ambiguous option is seen on the command line.
+ """
+ def __init__(self, opt_str, possibilities):
+ BadOptionError.__init__(self, opt_str)
+ self.possibilities = possibilities
+
+ def __str__(self):
+ return (_("ambiguous option: %s (%s?)")
+ % (self.opt_str, string.join(self.possibilities, ", ")))
+
+
+class HelpFormatter:
+
+ """
+ Abstract base class for formatting option help. OptionParser
+ instances should use one of the HelpFormatter subclasses for
+ formatting help; by default IndentedHelpFormatter is used.
+
+ Instance attributes:
+ parser : OptionParser
+ the controlling OptionParser instance
+ indent_increment : int
+ the number of columns to indent per nesting level
+ max_help_position : int
+ the maximum starting column for option help text
+ help_position : int
+ the calculated starting column for option help text;
+ initially the same as the maximum
+ width : int
+ total number of columns for output (pass None to constructor for
+ this value to be taken from the $COLUMNS environment variable)
+ level : int
+ current indentation level
+ current_indent : int
+ current indentation level (in columns)
+ help_width : int
+ number of columns available for option help text (calculated)
+ default_tag : str
+ text to replace with each option's default value, "%default"
+ by default. Set to false value to disable default value expansion.
+ option_strings : { Option : str }
+ maps Option instances to the snippet of help text explaining
+ the syntax of that option, e.g. "-h, --help" or
+ "-fFILE, --file=FILE"
+ _short_opt_fmt : str
+ format string controlling how short options with values are
+ printed in help text. Must be either "%s%s" ("-fFILE") or
+ "%s %s" ("-f FILE"), because those are the two syntaxes that
+ Optik supports.
+ _long_opt_fmt : str
+ similar but for long options; must be either "%s %s" ("--file FILE")
+ or "%s=%s" ("--file=FILE").
+ """
+
+ NO_DEFAULT_VALUE = "none"
+
+ def __init__(self,
+ indent_increment,
+ max_help_position,
+ width,
+ short_first):
+ self.parser = None
+ self.indent_increment = indent_increment
+ self.help_position = self.max_help_position = max_help_position
+ if width is None:
+ try:
+ width = int(os.environ['COLUMNS'])
+ except (KeyError, ValueError):
+ width = 80
+ width = width - 2
+ self.width = width
+ self.current_indent = 0
+ self.level = 0
+ self.help_width = None # computed later
+ self.short_first = short_first
+ self.default_tag = "%default"
+ self.option_strings = {}
+ self._short_opt_fmt = "%s %s"
+ self._long_opt_fmt = "%s=%s"
+
+ def set_parser(self, parser):
+ self.parser = parser
+
+ def set_short_opt_delimiter(self, delim):
+ if delim not in ("", " "):
+ raise ValueError(
+ "invalid metavar delimiter for short options: %r" % delim)
+ self._short_opt_fmt = "%s" + delim + "%s"
+
+ def set_long_opt_delimiter(self, delim):
+ if delim not in ("=", " "):
+ raise ValueError(
+ "invalid metavar delimiter for long options: %r" % delim)
+ self._long_opt_fmt = "%s" + delim + "%s"
+
+ def indent(self):
+ self.current_indent = self.current_indent + self.indent_increment
+ self.level = self.level + 1
+
+ def dedent(self):
+ self.current_indent = self.current_indent - self.indent_increment
+ assert self.current_indent >= 0, "Indent decreased below 0."
+ self.level = self.level - 1
+
+ def format_usage(self, usage):
+ raise NotImplementedError, "subclasses must implement"
+
+ def format_heading(self, heading):
+ raise NotImplementedError, "subclasses must implement"
+
+ def _format_text(self, text):
+ """
+ Format a paragraph of free-form text for inclusion in the
+ help output at the current indentation level.
+ """
+ text_width = self.width - self.current_indent
+ indent = " "*self.current_indent
+ return textwrap.fill(text,
+ text_width,
+ initial_indent=indent,
+ subsequent_indent=indent)
+
+ def format_description(self, description):
+ if description:
+ return self._format_text(description) + "\n"
+ else:
+ return ""
+
+ def format_epilog(self, epilog):
+ if epilog:
+ return "\n" + self._format_text(epilog) + "\n"
+ else:
+ return ""
+
+
+ def expand_default(self, option):
+ if self.parser is None or not self.default_tag:
+ return option.help
+
+ default_value = self.parser.defaults.get(option.dest)
+ if default_value is NO_DEFAULT or default_value is None:
+ default_value = self.NO_DEFAULT_VALUE
+
+ return string.replace(option.help, self.default_tag, str(default_value))
+
+ def format_option(self, option):
+ # The help for each option consists of two parts:
+ # * the opt strings and metavars
+ # eg. ("-x", or "-fFILENAME, --file=FILENAME")
+ # * the user-supplied help string
+ # eg. ("turn on expert mode", "read data from FILENAME")
+ #
+ # If possible, we write both of these on the same line:
+ # -x turn on expert mode
+ #
+ # But if the opt string list is too long, we put the help
+ # string on a second line, indented to the same column it would
+ # start in if it fit on the first line.
+ # -fFILENAME, --file=FILENAME
+ # read data from FILENAME
+ result = []
+ opts = self.option_strings[option]
+ opt_width = self.help_position - self.current_indent - 2
+ if len(opts) > opt_width:
+ opts = "%*s%s\n" % (self.current_indent, "", opts)
+ indent_first = self.help_position
+ else: # start help on same line as opts
+ opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
+ indent_first = 0
+ result.append(opts)
+ if option.help:
+ help_text = self.expand_default(option)
+ help_lines = textwrap.wrap(help_text, self.help_width)
+ result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
+ for line in help_lines[1:]:
+ result.append("%*s%s\n" % (self.help_position, "", line))
+ elif opts[-1] != "\n":
+ result.append("\n")
+ return string.join(result, "")
+
+ def store_option_strings(self, parser):
+ self.indent()
+ max_len = 0
+ for opt in parser.option_list:
+ strings = self.format_option_strings(opt)
+ self.option_strings[opt] = strings
+ max_len = max(max_len, len(strings) + self.current_indent)
+ self.indent()
+ for group in parser.option_groups:
+ for opt in group.option_list:
+ strings = self.format_option_strings(opt)
+ self.option_strings[opt] = strings
+ max_len = max(max_len, len(strings) + self.current_indent)
+ self.dedent()
+ self.dedent()
+ self.help_position = min(max_len + 2, self.max_help_position)
+ self.help_width = self.width - self.help_position
+
+ def format_option_strings(self, option):
+ """Return a comma-separated list of option strings & metavariables."""
+ if option.takes_value():
+ metavar = option.metavar or string.upper(option.dest)
+ short_opts = []
+ for sopt in option._short_opts:
+ short_opts.append(self._short_opt_fmt % (sopt, metavar))
+ long_opts = []
+ for lopt in option._long_opts:
+ long_opts.append(self._long_opt_fmt % (lopt, metavar))
+ else:
+ short_opts = option._short_opts
+ long_opts = option._long_opts
+
+ if self.short_first:
+ opts = short_opts + long_opts
+ else:
+ opts = long_opts + short_opts
+
+ return string.join(opts, ", ")
+
+class IndentedHelpFormatter (HelpFormatter):
+ """Format help with indented section bodies.
+ """
+
+ def __init__(self,
+ indent_increment=2,
+ max_help_position=24,
+ width=None,
+ short_first=1):
+ HelpFormatter.__init__(
+ self, indent_increment, max_help_position, width, short_first)
+
+ def format_usage(self, usage):
+ return _("Usage: %s\n") % usage
+
+ def format_heading(self, heading):
+ return "%*s%s:\n" % (self.current_indent, "", heading)
+
+
+class TitledHelpFormatter (HelpFormatter):
+ """Format help with underlined section headers.
+ """
+
+ def __init__(self,
+ indent_increment=0,
+ max_help_position=24,
+ width=None,
+ short_first=0):
+ HelpFormatter.__init__ (
+ self, indent_increment, max_help_position, width, short_first)
+
+ def format_usage(self, usage):
+ return "%s %s\n" % (self.format_heading(_("Usage")), usage)
+
+ def format_heading(self, heading):
+ return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading))
+
+
+def _parse_num(val, type):
+ if string.lower(val[:2]) == "0x": # hexadecimal
+ radix = 16
+ elif string.lower(val[:2]) == "0b": # binary
+ radix = 2
+ val = val[2:] or "0" # have to remove "0b" prefix
+ elif val[:1] == "0": # octal
+ radix = 8
+ else: # decimal
+ radix = 10
+
+ return type(val, radix)
+
+def _parse_int(val):
+ return _parse_num(val, int)
+
+def _parse_long(val):
+ return _parse_num(val, long)
+
+try:
+ int('0', 10)
+except TypeError:
+ # Python 1.5.2 doesn't allow a radix value to be passed to int().
+ _parse_int = int
+
+try:
+ long('0', 10)
+except TypeError:
+ # Python 1.5.2 doesn't allow a radix value to be passed to long().
+ _parse_long = long
+
+_builtin_cvt = { "int" : (_parse_int, _("integer")),
+ "long" : (_parse_long, _("long integer")),
+ "float" : (float, _("floating-point")),
+ "complex" : (complex, _("complex")) }
+
+def check_builtin(option, opt, value):
+ (cvt, what) = _builtin_cvt[option.type]
+ try:
+ return cvt(value)
+ except ValueError:
+ raise OptionValueError(
+ _("option %s: invalid %s value: %r") % (opt, what, value))
+
+def check_choice(option, opt, value):
+ if value in option.choices:
+ return value
+ else:
+ choices = string.join(map(repr, option.choices), ", ")
+ raise OptionValueError(
+ _("option %s: invalid choice: %r (choose from %s)")
+ % (opt, value, choices))
+
+# Not supplying a default is different from a default of None,
+# so we need an explicit "not supplied" value.
+NO_DEFAULT = ("NO", "DEFAULT")
+
+
+class Option:
+ """
+ Instance attributes:
+ _short_opts : [string]
+ _long_opts : [string]
+
+ action : string
+ type : string
+ dest : string
+ default : any
+ nargs : int
+ const : any
+ choices : [string]
+ callback : function
+ callback_args : (any*)
+ callback_kwargs : { string : any }
+ help : string
+ metavar : string
+ """
+
+ # The list of instance attributes that may be set through
+ # keyword args to the constructor.
+ ATTRS = ['action',
+ 'type',
+ 'dest',
+ 'default',
+ 'nargs',
+ 'const',
+ 'choices',
+ 'callback',
+ 'callback_args',
+ 'callback_kwargs',
+ 'help',
+ 'metavar']
+
+ # The set of actions allowed by option parsers. Explicitly listed
+ # here so the constructor can validate its arguments.
+ ACTIONS = ("store",
+ "store_const",
+ "store_true",
+ "store_false",
+ "append",
+ "append_const",
+ "count",
+ "callback",
+ "help",
+ "version")
+
+ # The set of actions that involve storing a value somewhere;
+ # also listed just for constructor argument validation. (If
+ # the action is one of these, there must be a destination.)
+ STORE_ACTIONS = ("store",
+ "store_const",
+ "store_true",
+ "store_false",
+ "append",
+ "append_const",
+ "count")
+
+ # The set of actions for which it makes sense to supply a value
+ # type, ie. which may consume an argument from the command line.
+ TYPED_ACTIONS = ("store",
+ "append",
+ "callback")
+
+ # The set of actions which *require* a value type, ie. that
+ # always consume an argument from the command line.
+ ALWAYS_TYPED_ACTIONS = ("store",
+ "append")
+
+ # The set of actions which take a 'const' attribute.
+ CONST_ACTIONS = ("store_const",
+ "append_const")
+
+ # The set of known types for option parsers. Again, listed here for
+ # constructor argument validation.
+ TYPES = ("string", "int", "long", "float", "complex", "choice")
+
+ # Dictionary of argument checking functions, which convert and
+ # validate option arguments according to the option type.
+ #
+ # Signature of checking functions is:
+ # check(option : Option, opt : string, value : string) -> any
+ # where
+ # option is the Option instance calling the checker
+ # opt is the actual option seen on the command-line
+ # (eg. "-a", "--file")
+ # value is the option argument seen on the command-line
+ #
+ # The return value should be in the appropriate Python type
+ # for option.type -- eg. an integer if option.type == "int".
+ #
+ # If no checker is defined for a type, arguments will be
+ # unchecked and remain strings.
+ TYPE_CHECKER = { "int" : check_builtin,
+ "long" : check_builtin,
+ "float" : check_builtin,
+ "complex": check_builtin,
+ "choice" : check_choice,
+ }
+
+
+ # CHECK_METHODS is a list of unbound method objects; they are called
+ # by the constructor, in order, after all attributes are
+ # initialized. The list is created and filled in later, after all
+ # the methods are actually defined. (I just put it here because I
+ # like to define and document all class attributes in the same
+ # place.) Subclasses that add another _check_*() method should
+ # define their own CHECK_METHODS list that adds their check method
+ # to those from this class.
+ CHECK_METHODS = None
+
+
+ # -- Constructor/initialization methods ----------------------------
+
+ def __init__(self, *opts, **attrs):
+ # Set _short_opts, _long_opts attrs from 'opts' tuple.
+ # Have to be set now, in case no option strings are supplied.
+ self._short_opts = []
+ self._long_opts = []
+ opts = self._check_opt_strings(opts)
+ self._set_opt_strings(opts)
+
+ # Set all other attrs (action, type, etc.) from 'attrs' dict
+ self._set_attrs(attrs)
+
+ # Check all the attributes we just set. There are lots of
+ # complicated interdependencies, but luckily they can be farmed
+ # out to the _check_*() methods listed in CHECK_METHODS -- which
+ # could be handy for subclasses! The one thing these all share
+ # is that they raise OptionError if they discover a problem.
+ for checker in self.CHECK_METHODS:
+ checker(self)
+
+ def _check_opt_strings(self, opts):
+ # Filter out None because early versions of Optik had exactly
+ # one short option and one long option, either of which
+ # could be None.
+ opts = filter(None, opts)
+ if not opts:
+ raise TypeError("at least one option string must be supplied")
+ return opts
+
+ def _set_opt_strings(self, opts):
+ for opt in opts:
+ if len(opt) < 2:
+ raise OptionError(
+ "invalid option string %r: "
+ "must be at least two characters long" % opt, self)
+ elif len(opt) == 2:
+ if not (opt[0] == "-" and opt[1] != "-"):
+ raise OptionError(
+ "invalid short option string %r: "
+ "must be of the form -x, (x any non-dash char)" % opt,
+ self)
+ self._short_opts.append(opt)
+ else:
+ if not (opt[0:2] == "--" and opt[2] != "-"):
+ raise OptionError(
+ "invalid long option string %r: "
+ "must start with --, followed by non-dash" % opt,
+ self)
+ self._long_opts.append(opt)
+
+ def _set_attrs(self, attrs):
+ for attr in self.ATTRS:
+ if attrs.has_key(attr):
+ setattr(self, attr, attrs[attr])
+ del attrs[attr]
+ else:
+ if attr == 'default':
+ setattr(self, attr, NO_DEFAULT)
+ else:
+ setattr(self, attr, None)
+ if attrs:
+ attrs = attrs.keys()
+ attrs.sort()
+ raise OptionError(
+ "invalid keyword arguments: %s" % string.join(attrs, ", "),
+ self)
+
+
+ # -- Constructor validation methods --------------------------------
+
+ def _check_action(self):
+ if self.action is None:
+ self.action = "store"
+ elif self.action not in self.ACTIONS:
+ raise OptionError("invalid action: %r" % self.action, self)
+
+ def _check_type(self):
+ if self.type is None:
+ if self.action in self.ALWAYS_TYPED_ACTIONS:
+ if self.choices is not None:
+ # The "choices" attribute implies "choice" type.
+ self.type = "choice"
+ else:
+ # No type given? "string" is the most sensible default.
+ self.type = "string"
+ else:
+ # Allow type objects or builtin type conversion functions
+ # (int, str, etc.) as an alternative to their names. (The
+ # complicated check of __builtin__ is only necessary for
+ # Python 2.1 and earlier, and is short-circuited by the
+ # first check on modern Pythons.)
+ import __builtin__
+ if ( type(self.type) is types.TypeType or
+ (hasattr(self.type, "__name__") and
+ getattr(__builtin__, self.type.__name__, None) is self.type) ):
+ self.type = self.type.__name__
+
+ if self.type == "str":
+ self.type = "string"
+
+ if self.type not in self.TYPES:
+ raise OptionError("invalid option type: %r" % self.type, self)
+ if self.action not in self.TYPED_ACTIONS:
+ raise OptionError(
+ "must not supply a type for action %r" % self.action, self)
+
+ def _check_choice(self):
+ if self.type == "choice":
+ if self.choices is None:
+ raise OptionError(
+ "must supply a list of choices for type 'choice'", self)
+ elif type(self.choices) not in (types.TupleType, types.ListType):
+ raise OptionError(
+ "choices must be a list of strings ('%s' supplied)"
+ % string.split(str(type(self.choices)), "'")[1], self)
+ elif self.choices is not None:
+ raise OptionError(
+ "must not supply choices for type %r" % self.type, self)
+
+ def _check_dest(self):
+ # No destination given, and we need one for this action. The
+ # self.type check is for callbacks that take a value.
+ takes_value = (self.action in self.STORE_ACTIONS or
+ self.type is not None)
+ if self.dest is None and takes_value:
+
+ # Glean a destination from the first long option string,
+ # or from the first short option string if no long options.
+ if self._long_opts:
+ # eg. "--foo-bar" -> "foo_bar"
+ self.dest = string.replace(self._long_opts[0][2:], '-', '_')
+ else:
+ self.dest = self._short_opts[0][1]
+
+ def _check_const(self):
+ if self.action not in self.CONST_ACTIONS and self.const is not None:
+ raise OptionError(
+ "'const' must not be supplied for action %r" % self.action,
+ self)
+
+ def _check_nargs(self):
+ if self.action in self.TYPED_ACTIONS:
+ if self.nargs is None:
+ self.nargs = 1
+ elif self.nargs is not None:
+ raise OptionError(
+ "'nargs' must not be supplied for action %r" % self.action,
+ self)
+
+ def _check_callback(self):
+ if self.action == "callback":
+ if not callable(self.callback):
+ raise OptionError(
+ "callback not callable: %r" % self.callback, self)
+ if (self.callback_args is not None and
+ type(self.callback_args) is not types.TupleType):
+ raise OptionError(
+ "callback_args, if supplied, must be a tuple: not %r"
+ % self.callback_args, self)
+ if (self.callback_kwargs is not None and
+ type(self.callback_kwargs) is not types.DictType):
+ raise OptionError(
+ "callback_kwargs, if supplied, must be a dict: not %r"
+ % self.callback_kwargs, self)
+ else:
+ if self.callback is not None:
+ raise OptionError(
+ "callback supplied (%r) for non-callback option"
+ % self.callback, self)
+ if self.callback_args is not None:
+ raise OptionError(
+ "callback_args supplied for non-callback option", self)
+ if self.callback_kwargs is not None:
+ raise OptionError(
+ "callback_kwargs supplied for non-callback option", self)
+
+
+ CHECK_METHODS = [_check_action,
+ _check_type,
+ _check_choice,
+ _check_dest,
+ _check_const,
+ _check_nargs,
+ _check_callback]
+
+
+ # -- Miscellaneous methods -----------------------------------------
+
+ def __str__(self):
+ return string.join(self._short_opts + self._long_opts, "/")
+
+ __repr__ = _repr
+
+ def takes_value(self):
+ return self.type is not None
+
+ def get_opt_string(self):
+ if self._long_opts:
+ return self._long_opts[0]
+ else:
+ return self._short_opts[0]
+
+
+ # -- Processing methods --------------------------------------------
+
+ def check_value(self, opt, value):
+ checker = self.TYPE_CHECKER.get(self.type)
+ if checker is None:
+ return value
+ else:
+ return checker(self, opt, value)
+
+ def convert_value(self, opt, value):
+ if value is not None:
+ if self.nargs == 1:
+ return self.check_value(opt, value)
+ else:
+ return tuple(map(lambda v, o=opt, s=self: s.check_value(o, v), value))
+
+ def process(self, opt, value, values, parser):
+
+ # First, convert the value(s) to the right type. Howl if any
+ # value(s) are bogus.
+ value = self.convert_value(opt, value)
+
+ # And then take whatever action is expected of us.
+ # This is a separate method to make life easier for
+ # subclasses to add new actions.
+ return self.take_action(
+ self.action, self.dest, opt, value, values, parser)
+
+ def take_action(self, action, dest, opt, value, values, parser):
+ if action == "store":
+ setattr(values, dest, value)
+ elif action == "store_const":
+ setattr(values, dest, self.const)
+ elif action == "store_true":
+ setattr(values, dest, True)
+ elif action == "store_false":
+ setattr(values, dest, False)
+ elif action == "append":
+ values.ensure_value(dest, []).append(value)
+ elif action == "append_const":
+ values.ensure_value(dest, []).append(self.const)
+ elif action == "count":
+ setattr(values, dest, values.ensure_value(dest, 0) + 1)
+ elif action == "callback":
+ args = self.callback_args or ()
+ kwargs = self.callback_kwargs or {}
+ apply(self.callback, (self, opt, value, parser,) + args, kwargs)
+ elif action == "help":
+ parser.print_help()
+ parser.exit()
+ elif action == "version":
+ parser.print_version()
+ parser.exit()
+ else:
+ raise RuntimeError, "unknown action %r" % self.action
+
+ return 1
+
+# class Option
+
+
+SUPPRESS_HELP = "SUPPRESS"+"HELP"
+SUPPRESS_USAGE = "SUPPRESS"+"USAGE"
+
+# For compatibility with Python 2.2
+try:
+ True, False
+except NameError:
+ (True, False) = (1, 0)
+
+try:
+ types.UnicodeType
+except AttributeError:
+ def isbasestring(x):
+ return isinstance(x, types.StringType)
+else:
+ def isbasestring(x):
+ return isinstance(x, types.StringType) or isinstance(x, types.UnicodeType)
+
+class Values:
+
+ def __init__(self, defaults=None):
+ if defaults:
+ for (attr, val) in defaults.items():
+ setattr(self, attr, val)
+
+ def __str__(self):
+ return str(self.__dict__)
+
+ __repr__ = _repr
+
+ def __cmp__(self, other):
+ if isinstance(other, Values):
+ return cmp(self.__dict__, other.__dict__)
+ elif isinstance(other, types.DictType):
+ return cmp(self.__dict__, other)
+ else:
+ return -1
+
+ def _update_careful(self, dict):
+ """
+ Update the option values from an arbitrary dictionary, but only
+ use keys from dict that already have a corresponding attribute
+ in self. Any keys in dict without a corresponding attribute
+ are silently ignored.
+ """
+ for attr in dir(self):
+ if dict.has_key(attr):
+ dval = dict[attr]
+ if dval is not None:
+ setattr(self, attr, dval)
+
+ def _update_loose(self, dict):
+ """
+ Update the option values from an arbitrary dictionary,
+ using all keys from the dictionary regardless of whether
+ they have a corresponding attribute in self or not.
+ """
+ self.__dict__.update(dict)
+
+ def _update(self, dict, mode):
+ if mode == "careful":
+ self._update_careful(dict)
+ elif mode == "loose":
+ self._update_loose(dict)
+ else:
+ raise ValueError, "invalid update mode: %r" % mode
+
+ def read_module(self, modname, mode="careful"):
+ __import__(modname)
+ mod = sys.modules[modname]
+ self._update(vars(mod), mode)
+
+ def read_file(self, filename, mode="careful"):
+ vars = {}
+ exec open(filename, 'rU').read() in vars
+ self._update(vars, mode)
+
+ def ensure_value(self, attr, value):
+ if not hasattr(self, attr) or getattr(self, attr) is None:
+ setattr(self, attr, value)
+ return getattr(self, attr)
+
+
+class OptionContainer:
+
+ """
+ Abstract base class.
+
+ Class attributes:
+ standard_option_list : [Option]
+ list of standard options that will be accepted by all instances
+ of this parser class (intended to be overridden by subclasses).
+
+ Instance attributes:
+ option_list : [Option]
+ the list of Option objects contained by this OptionContainer
+ _short_opt : { string : Option }
+ dictionary mapping short option strings, eg. "-f" or "-X",
+ to the Option instances that implement them. If an Option
+ has multiple short option strings, it will appears in this
+ dictionary multiple times. [1]
+ _long_opt : { string : Option }
+ dictionary mapping long option strings, eg. "--file" or
+ "--exclude", to the Option instances that implement them.
+ Again, a given Option can occur multiple times in this
+ dictionary. [1]
+ defaults : { string : any }
+ dictionary mapping option destination names to default
+ values for each destination [1]
+
+ [1] These mappings are common to (shared by) all components of the
+ controlling OptionParser, where they are initially created.
+
+ """
+
+ def __init__(self, option_class, conflict_handler, description):
+ # Initialize the option list and related data structures.
+ # This method must be provided by subclasses, and it must
+ # initialize at least the following instance attributes:
+ # option_list, _short_opt, _long_opt, defaults.
+ self._create_option_list()
+
+ self.option_class = option_class
+ self.set_conflict_handler(conflict_handler)
+ self.set_description(description)
+
+ def _create_option_mappings(self):
+ # For use by OptionParser constructor -- create the master
+ # option mappings used by this OptionParser and all
+ # OptionGroups that it owns.
+ self._short_opt = {} # single letter -> Option instance
+ self._long_opt = {} # long option -> Option instance
+ self.defaults = {} # maps option dest -> default value
+
+
+ def _share_option_mappings(self, parser):
+ # For use by OptionGroup constructor -- use shared option
+ # mappings from the OptionParser that owns this OptionGroup.
+ self._short_opt = parser._short_opt
+ self._long_opt = parser._long_opt
+ self.defaults = parser.defaults
+
+ def set_conflict_handler(self, handler):
+ if handler not in ("error", "resolve"):
+ raise ValueError, "invalid conflict_resolution value %r" % handler
+ self.conflict_handler = handler
+
+ def set_description(self, description):
+ self.description = description
+
+ def get_description(self):
+ return self.description
+
+
+ def destroy(self):
+ """see OptionParser.destroy()."""
+ del self._short_opt
+ del self._long_opt
+ del self.defaults
+
+
+ # -- Option-adding methods -----------------------------------------
+
+ def _check_conflict(self, option):
+ conflict_opts = []
+ for opt in option._short_opts:
+ if self._short_opt.has_key(opt):
+ conflict_opts.append((opt, self._short_opt[opt]))
+ for opt in option._long_opts:
+ if self._long_opt.has_key(opt):
+ conflict_opts.append((opt, self._long_opt[opt]))
+
+ if conflict_opts:
+ handler = self.conflict_handler
+ if handler == "error":
+ raise OptionConflictError(
+ "conflicting option string(s): %s"
+ % string.join(map(lambda co: co[0], conflict_opts), ", "),
+ option)
+ elif handler == "resolve":
+ for (opt, c_option) in conflict_opts:
+ if opt[:2] == "--":
+ c_option._long_opts.remove(opt)
+ del self._long_opt[opt]
+ else:
+ c_option._short_opts.remove(opt)
+ del self._short_opt[opt]
+ if not (c_option._short_opts or c_option._long_opts):
+ c_option.container.option_list.remove(c_option)
+
+ def add_option(self, *args, **kwargs):
+ """add_option(Option)
+ add_option(opt_str, ..., kwarg=val, ...)
+ """
+ if type(args[0]) is types.StringType:
+ option = apply(self.option_class, args, kwargs)
+ elif len(args) == 1 and not kwargs:
+ option = args[0]
+ if not isinstance(option, Option):
+ raise TypeError, "not an Option instance: %r" % option
+ else:
+ raise TypeError, "invalid arguments"
+
+ self._check_conflict(option)
+
+ self.option_list.append(option)
+ option.container = self
+ for opt in option._short_opts:
+ self._short_opt[opt] = option
+ for opt in option._long_opts:
+ self._long_opt[opt] = option
+
+ if option.dest is not None: # option has a dest, we need a default
+ if option.default is not NO_DEFAULT:
+ self.defaults[option.dest] = option.default
+ elif not self.defaults.has_key(option.dest):
+ self.defaults[option.dest] = None
+
+ return option
+
+ def add_options(self, option_list):
+ for option in option_list:
+ self.add_option(option)
+
+ # -- Option query/removal methods ----------------------------------
+
+ def get_option(self, opt_str):
+ return (self._short_opt.get(opt_str) or
+ self._long_opt.get(opt_str))
+
+ def has_option(self, opt_str):
+ return (self._short_opt.has_key(opt_str) or
+ self._long_opt.has_key(opt_str))
+
+ def remove_option(self, opt_str):
+ option = self._short_opt.get(opt_str)
+ if option is None:
+ option = self._long_opt.get(opt_str)
+ if option is None:
+ raise ValueError("no such option %r" % opt_str)
+
+ for opt in option._short_opts:
+ del self._short_opt[opt]
+ for opt in option._long_opts:
+ del self._long_opt[opt]
+ option.container.option_list.remove(option)
+
+
+ # -- Help-formatting methods ---------------------------------------
+
+ def format_option_help(self, formatter):
+ if not self.option_list:
+ return ""
+ result = []
+ for option in self.option_list:
+ if not option.help is SUPPRESS_HELP:
+ result.append(formatter.format_option(option))
+ return string.join(result, "")
+
+ def format_description(self, formatter):
+ return formatter.format_description(self.get_description())
+
+ def format_help(self, formatter):
+ result = []
+ if self.description:
+ result.append(self.format_description(formatter))
+ if self.option_list:
+ result.append(self.format_option_help(formatter))
+ return string.join(result, "\n")
+
+
+class OptionGroup (OptionContainer):
+
+ def __init__(self, parser, title, description=None):
+ self.parser = parser
+ OptionContainer.__init__(
+ self, parser.option_class, parser.conflict_handler, description)
+ self.title = title
+
+ def _create_option_list(self):
+ self.option_list = []
+ self._share_option_mappings(self.parser)
+
+ def set_title(self, title):
+ self.title = title
+
+ def destroy(self):
+ """see OptionParser.destroy()."""
+ OptionContainer.destroy(self)
+ del self.option_list
+
+ # -- Help-formatting methods ---------------------------------------
+
+ def format_help(self, formatter):
+ result = formatter.format_heading(self.title)
+ formatter.indent()
+ result = result + OptionContainer.format_help(self, formatter)
+ formatter.dedent()
+ return result
+
+
+class OptionParser (OptionContainer):
+
+ """
+ Class attributes:
+ standard_option_list : [Option]
+ list of standard options that will be accepted by all instances
+ of this parser class (intended to be overridden by subclasses).
+
+ Instance attributes:
+ usage : string
+ a usage string for your program. Before it is displayed
+ to the user, "%prog" will be expanded to the name of
+ your program (self.prog or os.path.basename(sys.argv[0])).
+ prog : string
+ the name of the current program (to override
+ os.path.basename(sys.argv[0])).
+ epilog : string
+ paragraph of help text to print after option help
+
+ option_groups : [OptionGroup]
+ list of option groups in this parser (option groups are
+ irrelevant for parsing the command-line, but very useful
+ for generating help)
+
+ allow_interspersed_args : bool = true
+ if true, positional arguments may be interspersed with options.
+ Assuming -a and -b each take a single argument, the command-line
+ -ablah foo bar -bboo baz
+ will be interpreted the same as
+ -ablah -bboo -- foo bar baz
+ If this flag were false, that command line would be interpreted as
+ -ablah -- foo bar -bboo baz
+ -- ie. we stop processing options as soon as we see the first
+ non-option argument. (This is the tradition followed by
+ Python's getopt module, Perl's Getopt::Std, and other argument-
+ parsing libraries, but it is generally annoying to users.)
+
+ process_default_values : bool = true
+ if true, option default values are processed similarly to option
+ values from the command line: that is, they are passed to the
+ type-checking function for the option's type (as long as the
+ default value is a string). (This really only matters if you
+ have defined custom types; see SF bug #955889.) Set it to false
+ to restore the behaviour of Optik 1.4.1 and earlier.
+
+ rargs : [string]
+ the argument list currently being parsed. Only set when
+ parse_args() is active, and continually trimmed down as
+ we consume arguments. Mainly there for the benefit of
+ callback options.
+ largs : [string]
+ the list of leftover arguments that we have skipped while
+ parsing options. If allow_interspersed_args is false, this
+ list is always empty.
+ values : Values
+ the set of option values currently being accumulated. Only
+ set when parse_args() is active. Also mainly for callbacks.
+
+ Because of the 'rargs', 'largs', and 'values' attributes,
+ OptionParser is not thread-safe. If, for some perverse reason, you
+ need to parse command-line arguments simultaneously in different
+ threads, use different OptionParser instances.
+
+ """
+
+ standard_option_list = []
+
+ def __init__(self,
+ usage=None,
+ option_list=None,
+ option_class=Option,
+ version=None,
+ conflict_handler="error",
+ description=None,
+ formatter=None,
+ add_help_option=True,
+ prog=None,
+ epilog=None):
+ OptionContainer.__init__(
+ self, option_class, conflict_handler, description)
+ self.set_usage(usage)
+ self.prog = prog
+ self.version = version
+ self.allow_interspersed_args = True
+ self.process_default_values = True
+ if formatter is None:
+ formatter = IndentedHelpFormatter()
+ self.formatter = formatter
+ self.formatter.set_parser(self)
+ self.epilog = epilog
+
+ # Populate the option list; initial sources are the
+ # standard_option_list class attribute, the 'option_list'
+ # argument, and (if applicable) the _add_version_option() and
+ # _add_help_option() methods.
+ self._populate_option_list(option_list,
+ add_help=add_help_option)
+
+ self._init_parsing_state()
+
+
+ def destroy(self):
+ """
+ Declare that you are done with this OptionParser. This cleans up
+ reference cycles so the OptionParser (and all objects referenced by
+ it) can be garbage-collected promptly. After calling destroy(), the
+ OptionParser is unusable.
+ """
+ OptionContainer.destroy(self)
+ for group in self.option_groups:
+ group.destroy()
+ del self.option_list
+ del self.option_groups
+ del self.formatter
+
+
+ # -- Private methods -----------------------------------------------
+ # (used by our or OptionContainer's constructor)
+
+ def _create_option_list(self):
+ self.option_list = []
+ self.option_groups = []
+ self._create_option_mappings()
+
+ def _add_help_option(self):
+ self.add_option("-h", "--help",
+ action="help",
+ help=_("show this help message and exit"))
+
+ def _add_version_option(self):
+ self.add_option("--version",
+ action="version",
+ help=_("show program's version number and exit"))
+
+ def _populate_option_list(self, option_list, add_help=True):
+ if self.standard_option_list:
+ self.add_options(self.standard_option_list)
+ if option_list:
+ self.add_options(option_list)
+ if self.version:
+ self._add_version_option()
+ if add_help:
+ self._add_help_option()
+
+ def _init_parsing_state(self):
+ # These are set in parse_args() for the convenience of callbacks.
+ self.rargs = None
+ self.largs = None
+ self.values = None
+
+
+ # -- Simple modifier methods ---------------------------------------
+
+ def set_usage(self, usage):
+ if usage is None:
+ self.usage = _("%prog [options]")
+ elif usage is SUPPRESS_USAGE:
+ self.usage = None
+ # For backwards compatibility with Optik 1.3 and earlier.
+ elif string.lower(usage)[:7] == "usage: ":
+ self.usage = usage[7:]
+ else:
+ self.usage = usage
+
+ def enable_interspersed_args(self):
+ self.allow_interspersed_args = True
+
+ def disable_interspersed_args(self):
+ self.allow_interspersed_args = False
+
+ def set_process_default_values(self, process):
+ self.process_default_values = process
+
+ def set_default(self, dest, value):
+ self.defaults[dest] = value
+
+ def set_defaults(self, **kwargs):
+ self.defaults.update(kwargs)
+
+ def _get_all_options(self):
+ options = self.option_list[:]
+ for group in self.option_groups:
+ options.extend(group.option_list)
+ return options
+
+ def get_default_values(self):
+ if not self.process_default_values:
+ # Old, pre-Optik 1.5 behaviour.
+ return Values(self.defaults)
+
+ defaults = self.defaults.copy()
+ for option in self._get_all_options():
+ default = defaults.get(option.dest)
+ if isbasestring(default):
+ opt_str = option.get_opt_string()
+ defaults[option.dest] = option.check_value(opt_str, default)
+
+ return Values(defaults)
+
+
+ # -- OptionGroup methods -------------------------------------------
+
+ def add_option_group(self, *args, **kwargs):
+ # XXX lots of overlap with OptionContainer.add_option()
+ if type(args[0]) is types.StringType:
+ group = apply(OptionGroup, (self,) + args, kwargs)
+ elif len(args) == 1 and not kwargs:
+ group = args[0]
+ if not isinstance(group, OptionGroup):
+ raise TypeError, "not an OptionGroup instance: %r" % group
+ if group.parser is not self:
+ raise ValueError, "invalid OptionGroup (wrong parser)"
+ else:
+ raise TypeError, "invalid arguments"
+
+ self.option_groups.append(group)
+ return group
+
+ def get_option_group(self, opt_str):
+ option = (self._short_opt.get(opt_str) or
+ self._long_opt.get(opt_str))
+ if option and option.container is not self:
+ return option.container
+ return None
+
+
+ # -- Option-parsing methods ----------------------------------------
+
+ def _get_args(self, args):
+ if args is None:
+ return sys.argv[1:]
+ else:
+ return args[:] # don't modify caller's list
+
+ def parse_args(self, args=None, values=None):
+ """
+ parse_args(args : [string] = sys.argv[1:],
+ values : Values = None)
+ -> (values : Values, args : [string])
+
+ Parse the command-line options found in 'args' (default:
+ sys.argv[1:]). Any errors result in a call to 'error()', which
+ by default prints the usage message to stderr and calls
+ sys.exit() with an error message. On success returns a pair
+ (values, args) where 'values' is an Values instance (with all
+ your option values) and 'args' is the list of arguments left
+ over after parsing options.
+ """
+ rargs = self._get_args(args)
+ if values is None:
+ values = self.get_default_values()
+
+ # Store the halves of the argument list as attributes for the
+ # convenience of callbacks:
+ # rargs
+ # the rest of the command-line (the "r" stands for
+ # "remaining" or "right-hand")
+ # largs
+ # the leftover arguments -- ie. what's left after removing
+ # options and their arguments (the "l" stands for "leftover"
+ # or "left-hand")
+ self.rargs = rargs
+ self.largs = largs = []
+ self.values = values
+
+ try:
+ stop = self._process_args(largs, rargs, values)
+ except (BadOptionError, OptionValueError), err:
+ self.error(str(err))
+
+ args = largs + rargs
+ return self.check_values(values, args)
+
+ def check_values(self, values, args):
+ """
+ check_values(values : Values, args : [string])
+ -> (values : Values, args : [string])
+
+ Check that the supplied option values and leftover arguments are
+ valid. Returns the option values and leftover arguments
+ (possibly adjusted, possibly completely new -- whatever you
+ like). Default implementation just returns the passed-in
+ values; subclasses may override as desired.
+ """
+ return (values, args)
+
+ def _process_args(self, largs, rargs, values):
+ """_process_args(largs : [string],
+ rargs : [string],
+ values : Values)
+
+ Process command-line arguments and populate 'values', consuming
+ options and arguments from 'rargs'. If 'allow_interspersed_args' is
+ false, stop at the first non-option argument. If true, accumulate any
+ interspersed non-option arguments in 'largs'.
+ """
+ while rargs:
+ arg = rargs[0]
+ # We handle bare "--" explicitly, and bare "-" is handled by the
+ # standard arg handler since the short arg case ensures that the
+ # len of the opt string is greater than 1.
+ if arg == "--":
+ del rargs[0]
+ return
+ elif arg[0:2] == "--":
+ # process a single long option (possibly with value(s))
+ self._process_long_opt(rargs, values)
+ elif arg[:1] == "-" and len(arg) > 1:
+ # process a cluster of short options (possibly with
+ # value(s) for the last one only)
+ self._process_short_opts(rargs, values)
+ elif self.allow_interspersed_args:
+ largs.append(arg)
+ del rargs[0]
+ else:
+ return # stop now, leave this arg in rargs
+
+ # Say this is the original argument list:
+ # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
+ # ^
+ # (we are about to process arg(i)).
+ #
+ # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
+ # [arg0, ..., arg(i-1)] (any options and their arguments will have
+ # been removed from largs).
+ #
+ # The while loop will usually consume 1 or more arguments per pass.
+ # If it consumes 1 (eg. arg is an option that takes no arguments),
+ # then after _process_arg() is done the situation is:
+ #
+ # largs = subset of [arg0, ..., arg(i)]
+ # rargs = [arg(i+1), ..., arg(N-1)]
+ #
+ # If allow_interspersed_args is false, largs will always be
+ # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
+ # not a very interesting subset!
+
+ def _match_long_opt(self, opt):
+ """_match_long_opt(opt : string) -> string
+
+ Determine which long option string 'opt' matches, ie. which one
+ it is an unambiguous abbrevation for. Raises BadOptionError if
+ 'opt' doesn't unambiguously match any long option string.
+ """
+ return _match_abbrev(opt, self._long_opt)
+
+ def _process_long_opt(self, rargs, values):
+ arg = rargs.pop(0)
+
+ # Value explicitly attached to arg? Pretend it's the next
+ # argument.
+ if "=" in arg:
+ (opt, next_arg) = string.split(arg, "=", 1)
+ rargs.insert(0, next_arg)
+ had_explicit_value = True
+ else:
+ opt = arg
+ had_explicit_value = False
+
+ opt = self._match_long_opt(opt)
+ option = self._long_opt[opt]
+ if option.takes_value():
+ nargs = option.nargs
+ if len(rargs) < nargs:
+ if nargs == 1:
+ self.error(_("%s option requires an argument") % opt)
+ else:
+ self.error(_("%s option requires %d arguments")
+ % (opt, nargs))
+ elif nargs == 1:
+ value = rargs.pop(0)
+ else:
+ value = tuple(rargs[0:nargs])
+ del rargs[0:nargs]
+
+ elif had_explicit_value:
+ self.error(_("%s option does not take a value") % opt)
+
+ else:
+ value = None
+
+ option.process(opt, value, values, self)
+
+ def _process_short_opts(self, rargs, values):
+ arg = rargs.pop(0)
+ stop = False
+ i = 1
+ for ch in arg[1:]:
+ opt = "-" + ch
+ option = self._short_opt.get(opt)
+ i = i + 1 # we have consumed a character
+
+ if not option:
+ raise BadOptionError(opt)
+ if option.takes_value():
+ # Any characters left in arg? Pretend they're the
+ # next arg, and stop consuming characters of arg.
+ if i < len(arg):
+ rargs.insert(0, arg[i:])
+ stop = True
+
+ nargs = option.nargs
+ if len(rargs) < nargs:
+ if nargs == 1:
+ self.error(_("%s option requires an argument") % opt)
+ else:
+ self.error(_("%s option requires %d arguments")
+ % (opt, nargs))
+ elif nargs == 1:
+ value = rargs.pop(0)
+ else:
+ value = tuple(rargs[0:nargs])
+ del rargs[0:nargs]
+
+ else: # option doesn't take a value
+ value = None
+
+ option.process(opt, value, values, self)
+
+ if stop:
+ break
+
+
+ # -- Feedback methods ----------------------------------------------
+
+ def get_prog_name(self):
+ if self.prog is None:
+ return os.path.basename(sys.argv[0])
+ else:
+ return self.prog
+
+ def expand_prog_name(self, s):
+ return string.replace(s, "%prog", self.get_prog_name())
+
+ def get_description(self):
+ return self.expand_prog_name(self.description)
+
+ def exit(self, status=0, msg=None):
+ if msg:
+ sys.stderr.write(msg)
+ sys.exit(status)
+
+ def error(self, msg):
+ """error(msg : string)
+
+ Print a usage message incorporating 'msg' to stderr and exit.
+ If you override this in a subclass, it should not return -- it
+ should either exit or raise an exception.
+ """
+ self.print_usage(sys.stderr)
+ self.exit(2, "%s: error: %s\n" % (self.get_prog_name(), msg))
+
+ def get_usage(self):
+ if self.usage:
+ return self.formatter.format_usage(
+ self.expand_prog_name(self.usage))
+ else:
+ return ""
+
+ def print_usage(self, file=None):
+ """print_usage(file : file = stdout)
+
+ Print the usage message for the current program (self.usage) to
+ 'file' (default stdout). Any occurence of the string "%prog" in
+ self.usage is replaced with the name of the current program
+ (basename of sys.argv[0]). Does nothing if self.usage is empty
+ or not defined.
+ """
+ if self.usage:
+ file.write(self.get_usage() + '\n')
+
+ def get_version(self):
+ if self.version:
+ return self.expand_prog_name(self.version)
+ else:
+ return ""
+
+ def print_version(self, file=None):
+ """print_version(file : file = stdout)
+
+ Print the version message for this program (self.version) to
+ 'file' (default stdout). As with print_usage(), any occurence
+ of "%prog" in self.version is replaced by the current program's
+ name. Does nothing if self.version is empty or undefined.
+ """
+ if self.version:
+ file.write(self.get_version() + '\n')
+
+ def format_option_help(self, formatter=None):
+ if formatter is None:
+ formatter = self.formatter
+ formatter.store_option_strings(self)
+ result = []
+ result.append(formatter.format_heading(_("Options")))
+ formatter.indent()
+ if self.option_list:
+ result.append(OptionContainer.format_option_help(self, formatter))
+ result.append("\n")
+ for group in self.option_groups:
+ result.append(group.format_help(formatter))
+ result.append("\n")
+ formatter.dedent()
+ # Drop the last "\n", or the header if no options or option groups:
+ return string.join(result[:-1], "")
+
+ def format_epilog(self, formatter):
+ return formatter.format_epilog(self.epilog)
+
+ def format_help(self, formatter=None):
+ if formatter is None:
+ formatter = self.formatter
+ result = []
+ if self.usage:
+ result.append(self.get_usage() + "\n")
+ if self.description:
+ result.append(self.format_description(formatter) + "\n")
+ result.append(self.format_option_help(formatter))
+ result.append(self.format_epilog(formatter))
+ return string.join(result, "")
+
+ # used by test suite
+ def _get_encoding(self, file):
+ encoding = getattr(file, "encoding", None)
+ if not encoding:
+ encoding = sys.getdefaultencoding()
+ return encoding
+
+ def print_help(self, file=None):
+ """print_help(file : file = stdout)
+
+ Print an extended help message, listing all options and any
+ help text provided with them, to 'file' (default stdout).
+ """
+ if file is None:
+ file = sys.stdout
+ encoding = self._get_encoding(file)
+ file.write(encode_wrapper(self.format_help(), encoding, "replace"))
+
+# class OptionParser
+
+
+def _match_abbrev(s, wordmap):
+ """_match_abbrev(s : string, wordmap : {string : Option}) -> string
+
+ Return the string key in 'wordmap' for which 's' is an unambiguous
+ abbreviation. If 's' is found to be ambiguous or doesn't match any of
+ 'words', raise BadOptionError.
+ """
+ # Is there an exact match?
+ if wordmap.has_key(s):
+ return s
+ else:
+ # Isolate all words with s as a prefix.
+ possibilities = filter(lambda w, s=s: w[:len(s)] == s, wordmap.keys())
+ # No exact match, so there had better be just one possibility.
+ if len(possibilities) == 1:
+ return possibilities[0]
+ elif not possibilities:
+ raise BadOptionError(s)
+ else:
+ # More than one possible completion: ambiguous prefix.
+ possibilities.sort()
+ raise AmbiguousOptionError(s, possibilities)
+
+
+# Some day, there might be many Option classes. As of Optik 1.3, the
+# preferred way to instantiate Options is indirectly, via make_option(),
+# which will become a factory function when there are many Option
+# classes.
+make_option = Option
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_platform.py b/src/engine/SCons/compat/_scons_platform.py
new file mode 100644
index 0000000..8b675ea
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_platform.py
@@ -0,0 +1,237 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = """
+platform backwards-compatibility module for older (pre-2.3) Python versions
+
+This does not not NOT (repeat, *NOT*) provide complete platform
+functionality. It only wraps the portions of platform functionality used
+by SCons.
+"""
+
+__revision__ = "src/engine/SCons/compat/_scons_platform.py 4577 2009/12/27 19:44:43 scons"
+
+### Portable uname() interface
+
+_uname_cache = None
+
+def uname():
+
+ """ Fairly portable uname interface. Returns a tuple
+ of strings (system,node,release,version,machine,processor)
+ identifying the underlying platform.
+
+ Note that unlike the os.uname function this also returns
+ possible processor information as an additional tuple entry.
+
+ Entries which cannot be determined are set to ''.
+
+ """
+ global _uname_cache
+ no_os_uname = 0
+
+ if _uname_cache is not None:
+ return _uname_cache
+
+ processor = ''
+
+ # Get some infos from the builtin os.uname API...
+ try:
+ system,node,release,version,machine = os.uname()
+ except AttributeError:
+ no_os_uname = 1
+
+ if no_os_uname or not filter(None, (system, node, release, version, machine)):
+ # Hmm, no there is either no uname or uname has returned
+ #'unknowns'... we'll have to poke around the system then.
+ if no_os_uname:
+ system = sys.platform
+ release = ''
+ version = ''
+ node = _node()
+ machine = ''
+
+ use_syscmd_ver = 01
+
+ # Try win32_ver() on win32 platforms
+ if system == 'win32':
+ release,version,csd,ptype = win32_ver()
+ if release and version:
+ use_syscmd_ver = 0
+ # Try to use the PROCESSOR_* environment variables
+ # available on Win XP and later; see
+ # http://support.microsoft.com/kb/888731 and
+ # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
+ if not machine:
+ machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
+ if not processor:
+ processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
+
+ # Try the 'ver' system command available on some
+ # platforms
+ if use_syscmd_ver:
+ system,release,version = _syscmd_ver(system)
+ # Normalize system to what win32_ver() normally returns
+ # (_syscmd_ver() tends to return the vendor name as well)
+ if system == 'Microsoft Windows':
+ system = 'Windows'
+ elif system == 'Microsoft' and release == 'Windows':
+ # Under Windows Vista and Windows Server 2008,
+ # Microsoft changed the output of the ver command. The
+ # release is no longer printed. This causes the
+ # system and release to be misidentified.
+ system = 'Windows'
+ if '6.0' == version[:3]:
+ release = 'Vista'
+ else:
+ release = ''
+
+ # In case we still don't know anything useful, we'll try to
+ # help ourselves
+ if system in ('win32','win16'):
+ if not version:
+ if system == 'win32':
+ version = '32bit'
+ else:
+ version = '16bit'
+ system = 'Windows'
+
+ elif system[:4] == 'java':
+ release,vendor,vminfo,osinfo = java_ver()
+ system = 'Java'
+ version = string.join(vminfo,', ')
+ if not version:
+ version = vendor
+
+ elif os.name == 'mac':
+ release,(version,stage,nonrel),machine = mac_ver()
+ system = 'MacOS'
+
+ # System specific extensions
+ if system == 'OpenVMS':
+ # OpenVMS seems to have release and version mixed up
+ if not release or release == '0':
+ release = version
+ version = ''
+ # Get processor information
+ try:
+ import vms_lib
+ except ImportError:
+ pass
+ else:
+ csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
+ if (cpu_number >= 128):
+ processor = 'Alpha'
+ else:
+ processor = 'VAX'
+ if not processor:
+ # Get processor information from the uname system command
+ processor = _syscmd_uname('-p','')
+
+ #If any unknowns still exist, replace them with ''s, which are more portable
+ if system == 'unknown':
+ system = ''
+ if node == 'unknown':
+ node = ''
+ if release == 'unknown':
+ release = ''
+ if version == 'unknown':
+ version = ''
+ if machine == 'unknown':
+ machine = ''
+ if processor == 'unknown':
+ processor = ''
+
+ # normalize name
+ if system == 'Microsoft' and release == 'Windows':
+ system = 'Windows'
+ release = 'Vista'
+
+ _uname_cache = system,node,release,version,machine,processor
+ return _uname_cache
+
+### Direct interfaces to some of the uname() return values
+
+def system():
+
+ """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
+
+ An empty string is returned if the value cannot be determined.
+
+ """
+ return uname()[0]
+
+def node():
+
+ """ Returns the computer's network name (which may not be fully
+ qualified)
+
+ An empty string is returned if the value cannot be determined.
+
+ """
+ return uname()[1]
+
+def release():
+
+ """ Returns the system's release, e.g. '2.2.0' or 'NT'
+
+ An empty string is returned if the value cannot be determined.
+
+ """
+ return uname()[2]
+
+def version():
+
+ """ Returns the system's release version, e.g. '#3 on degas'
+
+ An empty string is returned if the value cannot be determined.
+
+ """
+ return uname()[3]
+
+def machine():
+
+ """ Returns the machine type, e.g. 'i386'
+
+ An empty string is returned if the value cannot be determined.
+
+ """
+ return uname()[4]
+
+def processor():
+
+ """ Returns the (true) processor name, e.g. 'amdk6'
+
+ An empty string is returned if the value cannot be
+ determined. Note that many platforms do not provide this
+ information or simply return the same value as for machine(),
+ e.g. NetBSD does this.
+
+ """
+ return uname()[5]
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_sets.py b/src/engine/SCons/compat/_scons_sets.py
new file mode 100644
index 0000000..12dbead
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_sets.py
@@ -0,0 +1,583 @@
+"""Classes to represent arbitrary sets (including sets of sets).
+
+This module implements sets using dictionaries whose values are
+ignored. The usual operations (union, intersection, deletion, etc.)
+are provided as both methods and operators.
+
+Important: sets are not sequences! While they support 'x in s',
+'len(s)', and 'for x in s', none of those operations are unique for
+sequences; for example, mappings support all three as well. The
+characteristic operation for sequences is subscripting with small
+integers: s[i], for i in range(len(s)). Sets don't support
+subscripting at all. Also, sequences allow multiple occurrences and
+their elements have a definite order; sets on the other hand don't
+record multiple occurrences and don't remember the order of element
+insertion (which is why they don't support s[i]).
+
+The following classes are provided:
+
+BaseSet -- All the operations common to both mutable and immutable
+ sets. This is an abstract class, not meant to be directly
+ instantiated.
+
+Set -- Mutable sets, subclass of BaseSet; not hashable.
+
+ImmutableSet -- Immutable sets, subclass of BaseSet; hashable.
+ An iterable argument is mandatory to create an ImmutableSet.
+
+_TemporarilyImmutableSet -- A wrapper around a Set, hashable,
+ giving the same hash value as the immutable set equivalent
+ would have. Do not use this class directly.
+
+Only hashable objects can be added to a Set. In particular, you cannot
+really add a Set as an element to another Set; if you try, what is
+actually added is an ImmutableSet built from it (it compares equal to
+the one you tried adding).
+
+When you ask if `x in y' where x is a Set and y is a Set or
+ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and
+what's tested is actually `z in y'.
+
+"""
+
+# Code history:
+#
+# - Greg V. Wilson wrote the first version, using a different approach
+# to the mutable/immutable problem, and inheriting from dict.
+#
+# - Alex Martelli modified Greg's version to implement the current
+# Set/ImmutableSet approach, and make the data an attribute.
+#
+# - Guido van Rossum rewrote much of the code, made some API changes,
+# and cleaned up the docstrings.
+#
+# - Raymond Hettinger added a number of speedups and other
+# improvements.
+
+from __future__ import generators
+try:
+ from itertools import ifilter, ifilterfalse
+except ImportError:
+ # Code to make the module run under Py2.2
+ def ifilter(predicate, iterable):
+ if predicate is None:
+ def predicate(x):
+ return x
+ for x in iterable:
+ if predicate(x):
+ yield x
+ def ifilterfalse(predicate, iterable):
+ if predicate is None:
+ def predicate(x):
+ return x
+ for x in iterable:
+ if not predicate(x):
+ yield x
+ try:
+ True, False
+ except NameError:
+ True, False = (0==0, 0!=0)
+
+__all__ = ['BaseSet', 'Set', 'ImmutableSet']
+
+class BaseSet(object):
+ """Common base class for mutable and immutable sets."""
+
+ __slots__ = ['_data']
+
+ # Constructor
+
+ def __init__(self):
+ """This is an abstract class."""
+ # Don't call this from a concrete subclass!
+ if self.__class__ is BaseSet:
+ raise TypeError, ("BaseSet is an abstract class. "
+ "Use Set or ImmutableSet.")
+
+ # Standard protocols: __len__, __repr__, __str__, __iter__
+
+ def __len__(self):
+ """Return the number of elements of a set."""
+ return len(self._data)
+
+ def __repr__(self):
+ """Return string representation of a set.
+
+ This looks like 'Set([<list of elements>])'.
+ """
+ return self._repr()
+
+ # __str__ is the same as __repr__
+ __str__ = __repr__
+
+ def _repr(self, sorted=False):
+ elements = self._data.keys()
+ if sorted:
+ elements.sort()
+ return '%s(%r)' % (self.__class__.__name__, elements)
+
+ def __iter__(self):
+ """Return an iterator over the elements or a set.
+
+ This is the keys iterator for the underlying dict.
+ """
+ return self._data.iterkeys()
+
+ # Three-way comparison is not supported. However, because __eq__ is
+ # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and
+ # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this
+ # case).
+
+ def __cmp__(self, other):
+ raise TypeError, "can't compare sets using cmp()"
+
+ # Equality comparisons using the underlying dicts. Mixed-type comparisons
+ # are allowed here, where Set == z for non-Set z always returns False,
+ # and Set != z always True. This allows expressions like "x in y" to
+ # give the expected result when y is a sequence of mixed types, not
+ # raising a pointless TypeError just because y contains a Set, or x is
+ # a Set and y contain's a non-set ("in" invokes only __eq__).
+ # Subtle: it would be nicer if __eq__ and __ne__ could return
+ # NotImplemented instead of True or False. Then the other comparand
+ # would get a chance to determine the result, and if the other comparand
+ # also returned NotImplemented then it would fall back to object address
+ # comparison (which would always return False for __eq__ and always
+ # True for __ne__). However, that doesn't work, because this type
+ # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented,
+ # Python tries __cmp__ next, and the __cmp__ here then raises TypeError.
+
+ def __eq__(self, other):
+ if isinstance(other, BaseSet):
+ return self._data == other._data
+ else:
+ return False
+
+ def __ne__(self, other):
+ if isinstance(other, BaseSet):
+ return self._data != other._data
+ else:
+ return True
+
+ # Copying operations
+
+ def copy(self):
+ """Return a shallow copy of a set."""
+ result = self.__class__()
+ result._data.update(self._data)
+ return result
+
+ __copy__ = copy # For the copy module
+
+ def __deepcopy__(self, memo):
+ """Return a deep copy of a set; used by copy module."""
+ # This pre-creates the result and inserts it in the memo
+ # early, in case the deep copy recurses into another reference
+ # to this same set. A set can't be an element of itself, but
+ # it can certainly contain an object that has a reference to
+ # itself.
+ from copy import deepcopy
+ result = self.__class__()
+ memo[id(self)] = result
+ data = result._data
+ value = True
+ for elt in self:
+ data[deepcopy(elt, memo)] = value
+ return result
+
+ # Standard set operations: union, intersection, both differences.
+ # Each has an operator version (e.g. __or__, invoked with |) and a
+ # method version (e.g. union).
+ # Subtle: Each pair requires distinct code so that the outcome is
+ # correct when the type of other isn't suitable. For example, if
+ # we did "union = __or__" instead, then Set().union(3) would return
+ # NotImplemented instead of raising TypeError (albeit that *why* it
+ # raises TypeError as-is is also a bit subtle).
+
+ def __or__(self, other):
+ """Return the union of two sets as a new set.
+
+ (I.e. all elements that are in either set.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.union(other)
+
+ def union(self, other):
+ """Return the union of two sets as a new set.
+
+ (I.e. all elements that are in either set.)
+ """
+ result = self.__class__(self)
+ result._update(other)
+ return result
+
+ def __and__(self, other):
+ """Return the intersection of two sets as a new set.
+
+ (I.e. all elements that are in both sets.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.intersection(other)
+
+ def intersection(self, other):
+ """Return the intersection of two sets as a new set.
+
+ (I.e. all elements that are in both sets.)
+ """
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if len(self) <= len(other):
+ little, big = self, other
+ else:
+ little, big = other, self
+ common = ifilter(big._data.has_key, little)
+ return self.__class__(common)
+
+ def __xor__(self, other):
+ """Return the symmetric difference of two sets as a new set.
+
+ (I.e. all elements that are in exactly one of the sets.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.symmetric_difference(other)
+
+ def symmetric_difference(self, other):
+ """Return the symmetric difference of two sets as a new set.
+
+ (I.e. all elements that are in exactly one of the sets.)
+ """
+ result = self.__class__()
+ data = result._data
+ value = True
+ selfdata = self._data
+ try:
+ otherdata = other._data
+ except AttributeError:
+ otherdata = Set(other)._data
+ for elt in ifilterfalse(otherdata.has_key, selfdata):
+ data[elt] = value
+ for elt in ifilterfalse(selfdata.has_key, otherdata):
+ data[elt] = value
+ return result
+
+ def __sub__(self, other):
+ """Return the difference of two sets as a new Set.
+
+ (I.e. all elements that are in this set and not in the other.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.difference(other)
+
+ def difference(self, other):
+ """Return the difference of two sets as a new Set.
+
+ (I.e. all elements that are in this set and not in the other.)
+ """
+ result = self.__class__()
+ data = result._data
+ try:
+ otherdata = other._data
+ except AttributeError:
+ otherdata = Set(other)._data
+ value = True
+ for elt in ifilterfalse(otherdata.has_key, self):
+ data[elt] = value
+ return result
+
+ # Membership test
+
+ def __contains__(self, element):
+ """Report whether an element is a member of a set.
+
+ (Called in response to the expression `element in self'.)
+ """
+ try:
+ return element in self._data
+ except TypeError:
+ transform = getattr(element, "__as_temporarily_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ return transform() in self._data
+
+ # Subset and superset test
+
+ def issubset(self, other):
+ """Report whether another set contains this set."""
+ self._binary_sanity_check(other)
+ if len(self) > len(other): # Fast check for obvious cases
+ return False
+ for elt in ifilterfalse(other._data.has_key, self):
+ return False
+ return True
+
+ def issuperset(self, other):
+ """Report whether this set contains another set."""
+ self._binary_sanity_check(other)
+ if len(self) < len(other): # Fast check for obvious cases
+ return False
+ for elt in ifilterfalse(self._data.has_key, other):
+ return False
+ return True
+
+ # Inequality comparisons using the is-subset relation.
+ __le__ = issubset
+ __ge__ = issuperset
+
+ def __lt__(self, other):
+ self._binary_sanity_check(other)
+ return len(self) < len(other) and self.issubset(other)
+
+ def __gt__(self, other):
+ self._binary_sanity_check(other)
+ return len(self) > len(other) and self.issuperset(other)
+
+ # Assorted helpers
+
+ def _binary_sanity_check(self, other):
+ # Check that the other argument to a binary operation is also
+ # a set, raising a TypeError otherwise.
+ if not isinstance(other, BaseSet):
+ raise TypeError, "Binary operation only permitted between sets"
+
+ def _compute_hash(self):
+ # Calculate hash code for a set by xor'ing the hash codes of
+ # the elements. This ensures that the hash code does not depend
+ # on the order in which elements are added to the set. This is
+ # not called __hash__ because a BaseSet should not be hashable;
+ # only an ImmutableSet is hashable.
+ result = 0
+ for elt in self:
+ result ^= hash(elt)
+ return result
+
+ def _update(self, iterable):
+ # The main loop for update() and the subclass __init__() methods.
+ data = self._data
+
+ # Use the fast update() method when a dictionary is available.
+ if isinstance(iterable, BaseSet):
+ data.update(iterable._data)
+ return
+
+ value = True
+
+ if type(iterable) in (list, tuple, xrange):
+ # Optimized: we know that __iter__() and next() can't
+ # raise TypeError, so we can move 'try:' out of the loop.
+ it = iter(iterable)
+ while True:
+ try:
+ for element in it:
+ data[element] = value
+ return
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ data[transform()] = value
+ else:
+ # Safe: only catch TypeError where intended
+ for element in iterable:
+ try:
+ data[element] = value
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ data[transform()] = value
+
+
+class ImmutableSet(BaseSet):
+ """Immutable set class."""
+
+ __slots__ = ['_hashcode']
+
+ # BaseSet + hashing
+
+ def __init__(self, iterable=None):
+ """Construct an immutable set from an optional iterable."""
+ self._hashcode = None
+ self._data = {}
+ if iterable is not None:
+ self._update(iterable)
+
+ def __hash__(self):
+ if self._hashcode is None:
+ self._hashcode = self._compute_hash()
+ return self._hashcode
+
+ def __getstate__(self):
+ return self._data, self._hashcode
+
+ def __setstate__(self, state):
+ self._data, self._hashcode = state
+
+class Set(BaseSet):
+ """ Mutable set class."""
+
+ __slots__ = []
+
+ # BaseSet + operations requiring mutability; no hashing
+
+ def __init__(self, iterable=None):
+ """Construct a set from an optional iterable."""
+ self._data = {}
+ if iterable is not None:
+ self._update(iterable)
+
+ def __getstate__(self):
+ # getstate's results are ignored if it is not
+ return self._data,
+
+ def __setstate__(self, data):
+ self._data, = data
+
+ def __hash__(self):
+ """A Set cannot be hashed."""
+ # We inherit object.__hash__, so we must deny this explicitly
+ raise TypeError, "Can't hash a Set, only an ImmutableSet."
+
+ # In-place union, intersection, differences.
+ # Subtle: The xyz_update() functions deliberately return None,
+ # as do all mutating operations on built-in container types.
+ # The __xyz__ spellings have to return self, though.
+
+ def __ior__(self, other):
+ """Update a set with the union of itself and another."""
+ self._binary_sanity_check(other)
+ self._data.update(other._data)
+ return self
+
+ def union_update(self, other):
+ """Update a set with the union of itself and another."""
+ self._update(other)
+
+ def __iand__(self, other):
+ """Update a set with the intersection of itself and another."""
+ self._binary_sanity_check(other)
+ self._data = (self & other)._data
+ return self
+
+ def intersection_update(self, other):
+ """Update a set with the intersection of itself and another."""
+ if isinstance(other, BaseSet):
+ self &= other
+ else:
+ self._data = (self.intersection(other))._data
+
+ def __ixor__(self, other):
+ """Update a set with the symmetric difference of itself and another."""
+ self._binary_sanity_check(other)
+ self.symmetric_difference_update(other)
+ return self
+
+ def symmetric_difference_update(self, other):
+ """Update a set with the symmetric difference of itself and another."""
+ data = self._data
+ value = True
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if self is other:
+ self.clear()
+ for elt in other:
+ if elt in data:
+ del data[elt]
+ else:
+ data[elt] = value
+
+ def __isub__(self, other):
+ """Remove all elements of another set from this set."""
+ self._binary_sanity_check(other)
+ self.difference_update(other)
+ return self
+
+ def difference_update(self, other):
+ """Remove all elements of another set from this set."""
+ data = self._data
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if self is other:
+ self.clear()
+ for elt in ifilter(data.has_key, other):
+ del data[elt]
+
+ # Python dict-like mass mutations: update, clear
+
+ def update(self, iterable):
+ """Add all values from an iterable (such as a list or file)."""
+ self._update(iterable)
+
+ def clear(self):
+ """Remove all elements from this set."""
+ self._data.clear()
+
+ # Single-element mutations: add, remove, discard
+
+ def add(self, element):
+ """Add an element to a set.
+
+ This has no effect if the element is already present.
+ """
+ try:
+ self._data[element] = True
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ self._data[transform()] = True
+
+ def remove(self, element):
+ """Remove an element from a set; it must be a member.
+
+ If the element is not a member, raise a KeyError.
+ """
+ try:
+ del self._data[element]
+ except TypeError:
+ transform = getattr(element, "__as_temporarily_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ del self._data[transform()]
+
+ def discard(self, element):
+ """Remove an element from a set if it is a member.
+
+ If the element is not a member, do nothing.
+ """
+ try:
+ self.remove(element)
+ except KeyError:
+ pass
+
+ def pop(self):
+ """Remove and return an arbitrary set element."""
+ return self._data.popitem()[0]
+
+ def __as_immutable__(self):
+ # Return a copy of self as an immutable set
+ return ImmutableSet(self)
+
+ def __as_temporarily_immutable__(self):
+ # Return self wrapped in a temporarily immutable set
+ return _TemporarilyImmutableSet(self)
+
+
+class _TemporarilyImmutableSet(BaseSet):
+ # Wrap a mutable set as if it was temporarily immutable.
+ # This only supplies hashing and equality comparisons.
+
+ def __init__(self, set):
+ self._set = set
+ self._data = set._data # Needed by ImmutableSet.__eq__()
+
+ def __hash__(self):
+ return self._set._compute_hash()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_sets15.py b/src/engine/SCons/compat/_scons_sets15.py
new file mode 100644
index 0000000..bafa009
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_sets15.py
@@ -0,0 +1,176 @@
+#
+# A Set class that works all the way back to Python 1.5. From:
+#
+# Python Cookbook: Yet another Set class for Python
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/106469
+# Goncalo Rodriques
+#
+# This is a pure Pythonic implementation of a set class. The syntax
+# and methods implemented are, for the most part, borrowed from
+# PEP 218 by Greg Wilson.
+#
+# Note that this class violates the formal definition of a set() by adding
+# a __getitem__() method so we can iterate over a set's elements under
+# Python 1.5 and 2.1, which don't support __iter__() and iterator types.
+#
+
+import string
+
+class Set:
+ """The set class. It can contain mutable objects."""
+
+ def __init__(self, seq = None):
+ """The constructor. It can take any object giving an iterator as an optional
+ argument to populate the new set."""
+ self.elems = []
+ if seq:
+ for elem in seq:
+ if elem not in self.elems:
+ hash(elem)
+ self.elems.append(elem)
+
+ def __str__(self):
+ return "set([%s])" % string.join(map(str, self.elems), ", ")
+
+
+ def copy(self):
+ """Shallow copy of a set object."""
+ return Set(self.elems)
+
+ def __contains__(self, elem):
+ return elem in self.elems
+
+ def __len__(self):
+ return len(self.elems)
+
+ def __getitem__(self, index):
+ # Added so that Python 1.5 can iterate over the elements.
+ # The cookbook recipe's author didn't like this because there
+ # really isn't any order in a set object, but this is necessary
+ # to make the class work well enough for our purposes.
+ return self.elems[index]
+
+ def items(self):
+ """Returns a list of the elements in the set."""
+ return self.elems
+
+ def add(self, elem):
+ """Add one element to the set."""
+ if elem not in self.elems:
+ hash(elem)
+ self.elems.append(elem)
+
+ def remove(self, elem):
+ """Remove an element from the set. Return an error if elem is not in the set."""
+ try:
+ self.elems.remove(elem)
+ except ValueError:
+ raise LookupError, "Object %s is not a member of the set." % str(elem)
+
+ def discard(self, elem):
+ """Remove an element from the set. Do nothing if elem is not in the set."""
+ try:
+ self.elems.remove(elem)
+ except ValueError:
+ pass
+
+ def sort(self, func=cmp):
+ self.elems.sort(func)
+
+ #Define an iterator for a set.
+ def __iter__(self):
+ return iter(self.elems)
+
+ #The basic binary operations with sets.
+ def __or__(self, other):
+ """Union of two sets."""
+ ret = self.copy()
+ for elem in other.elems:
+ if elem not in ret:
+ ret.elems.append(elem)
+ return ret
+
+ def __sub__(self, other):
+ """Difference of two sets."""
+ ret = self.copy()
+ for elem in other.elems:
+ ret.discard(elem)
+ return ret
+
+ def __and__(self, other):
+ """Intersection of two sets."""
+ ret = Set()
+ for elem in self.elems:
+ if elem in other.elems:
+ ret.elems.append(elem)
+ return ret
+
+ def __add__(self, other):
+ """Symmetric difference of two sets."""
+ ret = Set()
+ temp = other.copy()
+ for elem in self.elems:
+ if elem in temp.elems:
+ temp.elems.remove(elem)
+ else:
+ ret.elems.append(elem)
+ #Add remaining elements.
+ for elem in temp.elems:
+ ret.elems.append(elem)
+ return ret
+
+ def __mul__(self, other):
+ """Cartesian product of two sets."""
+ ret = Set()
+ for elemself in self.elems:
+ x = map(lambda other, s=elemself: (s, other), other.elems)
+ ret.elems.extend(x)
+ return ret
+
+ #Some of the binary comparisons.
+ def __lt__(self, other):
+ """Returns 1 if the lhs set is contained but not equal to the rhs set."""
+ if len(self.elems) < len(other.elems):
+ temp = other.copy()
+ for elem in self.elems:
+ if elem in temp.elems:
+ temp.remove(elem)
+ else:
+ return 0
+ return len(temp.elems) == 0
+ else:
+ return 0
+
+ def __le__(self, other):
+ """Returns 1 if the lhs set is contained in the rhs set."""
+ if len(self.elems) <= len(other.elems):
+ ret = 1
+ for elem in self.elems:
+ if elem not in other.elems:
+ ret = 0
+ break
+ return ret
+ else:
+ return 0
+
+ def __eq__(self, other):
+ """Returns 1 if the sets are equal."""
+ if len(self.elems) != len(other.elems):
+ return 0
+ else:
+ return len(self - other) == 0
+
+ def __cmp__(self, other):
+ """Returns 1 if the sets are equal."""
+ if self.__lt__(other):
+ return -1
+ elif other.__lt__(self):
+ return 1
+ else:
+ return 0
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_shlex.py b/src/engine/SCons/compat/_scons_shlex.py
new file mode 100644
index 0000000..9e30a01
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_shlex.py
@@ -0,0 +1,325 @@
+# -*- coding: iso-8859-1 -*-
+"""A lexical analyzer class for simple shell-like syntaxes."""
+
+# Module and documentation by Eric S. Raymond, 21 Dec 1998
+# Input stacking and error message cleanup added by ESR, March 2000
+# push_source() and pop_source() made explicit by ESR, January 2001.
+# Posix compliance, split(), string arguments, and
+# iterator interface by Gustavo Niemeyer, April 2003.
+
+import os.path
+import sys
+#from collections import deque
+
+class deque:
+ def __init__(self):
+ self.data = []
+ def __len__(self):
+ return len(self.data)
+ def appendleft(self, item):
+ self.data.insert(0, item)
+ def popleft(self):
+ return self.data.pop(0)
+
+try:
+ basestring
+except NameError:
+ import types
+ def is_basestring(s):
+ return type(s) is types.StringType
+else:
+ def is_basestring(s):
+ return isinstance(s, basestring)
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+__all__ = ["shlex", "split"]
+
+class shlex:
+ "A lexical analyzer class for simple shell-like syntaxes."
+ def __init__(self, instream=None, infile=None, posix=False):
+ if is_basestring(instream):
+ instream = StringIO(instream)
+ if instream is not None:
+ self.instream = instream
+ self.infile = infile
+ else:
+ self.instream = sys.stdin
+ self.infile = None
+ self.posix = posix
+ if posix:
+ self.eof = None
+ else:
+ self.eof = ''
+ self.commenters = '#'
+ self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
+ if self.posix:
+ self.wordchars = self.wordchars + ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
+ 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
+ self.whitespace = ' \t\r\n'
+ self.whitespace_split = False
+ self.quotes = '\'"'
+ self.escape = '\\'
+ self.escapedquotes = '"'
+ self.state = ' '
+ self.pushback = deque()
+ self.lineno = 1
+ self.debug = 0
+ self.token = ''
+ self.filestack = deque()
+ self.source = None
+ if self.debug:
+ print 'shlex: reading from %s, line %d' \
+ % (self.instream, self.lineno)
+
+ def push_token(self, tok):
+ "Push a token onto the stack popped by the get_token method"
+ if self.debug >= 1:
+ print "shlex: pushing token " + repr(tok)
+ self.pushback.appendleft(tok)
+
+ def push_source(self, newstream, newfile=None):
+ "Push an input source onto the lexer's input source stack."
+ if is_basestring(newstream):
+ newstream = StringIO(newstream)
+ self.filestack.appendleft((self.infile, self.instream, self.lineno))
+ self.infile = newfile
+ self.instream = newstream
+ self.lineno = 1
+ if self.debug:
+ if newfile is not None:
+ print 'shlex: pushing to file %s' % (self.infile,)
+ else:
+ print 'shlex: pushing to stream %s' % (self.instream,)
+
+ def pop_source(self):
+ "Pop the input source stack."
+ self.instream.close()
+ (self.infile, self.instream, self.lineno) = self.filestack.popleft()
+ if self.debug:
+ print 'shlex: popping to %s, line %d' \
+ % (self.instream, self.lineno)
+ self.state = ' '
+
+ def get_token(self):
+ "Get a token from the input stream (or from stack if it's nonempty)"
+ if self.pushback:
+ tok = self.pushback.popleft()
+ if self.debug >= 1:
+ print "shlex: popping token " + repr(tok)
+ return tok
+ # No pushback. Get a token.
+ raw = self.read_token()
+ # Handle inclusions
+ if self.source is not None:
+ while raw == self.source:
+ spec = self.sourcehook(self.read_token())
+ if spec:
+ (newfile, newstream) = spec
+ self.push_source(newstream, newfile)
+ raw = self.get_token()
+ # Maybe we got EOF instead?
+ while raw == self.eof:
+ if not self.filestack:
+ return self.eof
+ else:
+ self.pop_source()
+ raw = self.get_token()
+ # Neither inclusion nor EOF
+ if self.debug >= 1:
+ if raw != self.eof:
+ print "shlex: token=" + repr(raw)
+ else:
+ print "shlex: token=EOF"
+ return raw
+
+ def read_token(self):
+ quoted = False
+ escapedstate = ' '
+ while True:
+ nextchar = self.instream.read(1)
+ if nextchar == '\n':
+ self.lineno = self.lineno + 1
+ if self.debug >= 3:
+ print "shlex: in state", repr(self.state), \
+ "I see character:", repr(nextchar)
+ if self.state is None:
+ self.token = '' # past end of file
+ break
+ elif self.state == ' ':
+ if not nextchar:
+ self.state = None # end of file
+ break
+ elif nextchar in self.whitespace:
+ if self.debug >= 2:
+ print "shlex: I see whitespace in whitespace state"
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif nextchar in self.commenters:
+ self.instream.readline()
+ self.lineno = self.lineno + 1
+ elif self.posix and nextchar in self.escape:
+ escapedstate = 'a'
+ self.state = nextchar
+ elif nextchar in self.wordchars:
+ self.token = nextchar
+ self.state = 'a'
+ elif nextchar in self.quotes:
+ if not self.posix:
+ self.token = nextchar
+ self.state = nextchar
+ elif self.whitespace_split:
+ self.token = nextchar
+ self.state = 'a'
+ else:
+ self.token = nextchar
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif self.state in self.quotes:
+ quoted = True
+ if not nextchar: # end of file
+ if self.debug >= 2:
+ print "shlex: I see EOF in quotes state"
+ # XXX what error should be raised here?
+ raise ValueError, "No closing quotation"
+ if nextchar == self.state:
+ if not self.posix:
+ self.token = self.token + nextchar
+ self.state = ' '
+ break
+ else:
+ self.state = 'a'
+ elif self.posix and nextchar in self.escape and \
+ self.state in self.escapedquotes:
+ escapedstate = self.state
+ self.state = nextchar
+ else:
+ self.token = self.token + nextchar
+ elif self.state in self.escape:
+ if not nextchar: # end of file
+ if self.debug >= 2:
+ print "shlex: I see EOF in escape state"
+ # XXX what error should be raised here?
+ raise ValueError, "No escaped character"
+ # In posix shells, only the quote itself or the escape
+ # character may be escaped within quotes.
+ if escapedstate in self.quotes and \
+ nextchar != self.state and nextchar != escapedstate:
+ self.token = self.token + self.state
+ self.token = self.token + nextchar
+ self.state = escapedstate
+ elif self.state == 'a':
+ if not nextchar:
+ self.state = None # end of file
+ break
+ elif nextchar in self.whitespace:
+ if self.debug >= 2:
+ print "shlex: I see whitespace in word state"
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif nextchar in self.commenters:
+ self.instream.readline()
+ self.lineno = self.lineno + 1
+ if self.posix:
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif self.posix and nextchar in self.quotes:
+ self.state = nextchar
+ elif self.posix and nextchar in self.escape:
+ escapedstate = 'a'
+ self.state = nextchar
+ elif nextchar in self.wordchars or nextchar in self.quotes \
+ or self.whitespace_split:
+ self.token = self.token + nextchar
+ else:
+ self.pushback.appendleft(nextchar)
+ if self.debug >= 2:
+ print "shlex: I see punctuation in word state"
+ self.state = ' '
+ if self.token:
+ break # emit current token
+ else:
+ continue
+ result = self.token
+ self.token = ''
+ if self.posix and not quoted and result == '':
+ result = None
+ if self.debug > 1:
+ if result:
+ print "shlex: raw token=" + repr(result)
+ else:
+ print "shlex: raw token=EOF"
+ return result
+
+ def sourcehook(self, newfile):
+ "Hook called on a filename to be sourced."
+ if newfile[0] == '"':
+ newfile = newfile[1:-1]
+ # This implements cpp-like semantics for relative-path inclusion.
+ if is_basestring(self.infile) and not os.path.isabs(newfile):
+ newfile = os.path.join(os.path.dirname(self.infile), newfile)
+ return (newfile, open(newfile, "r"))
+
+ def error_leader(self, infile=None, lineno=None):
+ "Emit a C-compiler-like, Emacs-friendly error-message leader."
+ if infile is None:
+ infile = self.infile
+ if lineno is None:
+ lineno = self.lineno
+ return "\"%s\", line %d: " % (infile, lineno)
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ token = self.get_token()
+ if token == self.eof:
+ raise StopIteration
+ return token
+
+def split(s, comments=False):
+ lex = shlex(s, posix=True)
+ lex.whitespace_split = True
+ if not comments:
+ lex.commenters = ''
+ #return list(lex)
+ result = []
+ while True:
+ token = lex.get_token()
+ if token == lex.eof:
+ break
+ result.append(token)
+ return result
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ lexer = shlex()
+ else:
+ file = sys.argv[1]
+ lexer = shlex(open(file), file)
+ while 1:
+ tt = lexer.get_token()
+ if tt:
+ print "Token: " + repr(tt)
+ else:
+ break
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_subprocess.py b/src/engine/SCons/compat/_scons_subprocess.py
new file mode 100644
index 0000000..4968825
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_subprocess.py
@@ -0,0 +1,1296 @@
+# subprocess - Subprocesses with accessible I/O streams
+#
+# For more information about this module, see PEP 324.
+#
+# This module should remain compatible with Python 2.2, see PEP 291.
+#
+# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
+#
+# Licensed to PSF under a Contributor Agreement.
+# See http://www.python.org/2.4/license for licensing details.
+
+r"""subprocess - Subprocesses with accessible I/O streams
+
+This module allows you to spawn processes, connect to their
+input/output/error pipes, and obtain their return codes. This module
+intends to replace several other, older modules and functions, like:
+
+os.system
+os.spawn*
+os.popen*
+popen2.*
+commands.*
+
+Information about how the subprocess module can be used to replace these
+modules and functions can be found below.
+
+
+
+Using the subprocess module
+===========================
+This module defines one class called Popen:
+
+class Popen(args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0):
+
+
+Arguments are:
+
+args should be a string, or a sequence of program arguments. The
+program to execute is normally the first item in the args sequence or
+string, but can be explicitly set by using the executable argument.
+
+On UNIX, with shell=False (default): In this case, the Popen class
+uses os.execvp() to execute the child program. args should normally
+be a sequence. A string will be treated as a sequence with the string
+as the only item (the program to execute).
+
+On UNIX, with shell=True: If args is a string, it specifies the
+command string to execute through the shell. If args is a sequence,
+the first item specifies the command string, and any additional items
+will be treated as additional shell arguments.
+
+On Windows: the Popen class uses CreateProcess() to execute the child
+program, which operates on strings. If args is a sequence, it will be
+converted to a string using the list2cmdline method. Please note that
+not all MS Windows applications interpret the command line the same
+way: The list2cmdline is designed for applications using the same
+rules as the MS C runtime.
+
+bufsize, if given, has the same meaning as the corresponding argument
+to the built-in open() function: 0 means unbuffered, 1 means line
+buffered, any other positive value means use a buffer of
+(approximately) that size. A negative bufsize means to use the system
+default, which usually means fully buffered. The default value for
+bufsize is 0 (unbuffered).
+
+stdin, stdout and stderr specify the executed programs' standard
+input, standard output and standard error file handles, respectively.
+Valid values are PIPE, an existing file descriptor (a positive
+integer), an existing file object, and None. PIPE indicates that a
+new pipe to the child should be created. With None, no redirection
+will occur; the child's file handles will be inherited from the
+parent. Additionally, stderr can be STDOUT, which indicates that the
+stderr data from the applications should be captured into the same
+file handle as for stdout.
+
+If preexec_fn is set to a callable object, this object will be called
+in the child process just before the child is executed.
+
+If close_fds is true, all file descriptors except 0, 1 and 2 will be
+closed before the child process is executed.
+
+if shell is true, the specified command will be executed through the
+shell.
+
+If cwd is not None, the current directory will be changed to cwd
+before the child is executed.
+
+If env is not None, it defines the environment variables for the new
+process.
+
+If universal_newlines is true, the file objects stdout and stderr are
+opened as a text files, but lines may be terminated by any of '\n',
+the Unix end-of-line convention, '\r', the Macintosh convention or
+'\r\n', the Windows convention. All of these external representations
+are seen as '\n' by the Python program. Note: This feature is only
+available if Python is built with universal newline support (the
+default). Also, the newlines attribute of the file objects stdout,
+stdin and stderr are not updated by the communicate() method.
+
+The startupinfo and creationflags, if given, will be passed to the
+underlying CreateProcess() function. They can specify things such as
+appearance of the main window and priority for the new process.
+(Windows only)
+
+
+This module also defines two shortcut functions:
+
+call(*popenargs, **kwargs):
+ Run command with arguments. Wait for command to complete, then
+ return the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ retcode = call(["ls", "-l"])
+
+check_call(*popenargs, **kwargs):
+ Run command with arguments. Wait for command to complete. If the
+ exit code was zero then return, otherwise raise
+ CalledProcessError. The CalledProcessError object will have the
+ return code in the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ check_call(["ls", "-l"])
+
+Exceptions
+----------
+Exceptions raised in the child process, before the new program has
+started to execute, will be re-raised in the parent. Additionally,
+the exception object will have one extra attribute called
+'child_traceback', which is a string containing traceback information
+from the childs point of view.
+
+The most common exception raised is OSError. This occurs, for
+example, when trying to execute a non-existent file. Applications
+should prepare for OSErrors.
+
+A ValueError will be raised if Popen is called with invalid arguments.
+
+check_call() will raise CalledProcessError, if the called process
+returns a non-zero return code.
+
+
+Security
+--------
+Unlike some other popen functions, this implementation will never call
+/bin/sh implicitly. This means that all characters, including shell
+metacharacters, can safely be passed to child processes.
+
+
+Popen objects
+=============
+Instances of the Popen class have the following methods:
+
+poll()
+ Check if child process has terminated. Returns returncode
+ attribute.
+
+wait()
+ Wait for child process to terminate. Returns returncode attribute.
+
+communicate(input=None)
+ Interact with process: Send data to stdin. Read data from stdout
+ and stderr, until end-of-file is reached. Wait for process to
+ terminate. The optional stdin argument should be a string to be
+ sent to the child process, or None, if no data should be sent to
+ the child.
+
+ communicate() returns a tuple (stdout, stderr).
+
+ Note: The data read is buffered in memory, so do not use this
+ method if the data size is large or unlimited.
+
+The following attributes are also available:
+
+stdin
+ If the stdin argument is PIPE, this attribute is a file object
+ that provides input to the child process. Otherwise, it is None.
+
+stdout
+ If the stdout argument is PIPE, this attribute is a file object
+ that provides output from the child process. Otherwise, it is
+ None.
+
+stderr
+ If the stderr argument is PIPE, this attribute is file object that
+ provides error output from the child process. Otherwise, it is
+ None.
+
+pid
+ The process ID of the child process.
+
+returncode
+ The child return code. A None value indicates that the process
+ hasn't terminated yet. A negative value -N indicates that the
+ child was terminated by signal N (UNIX only).
+
+
+Replacing older functions with the subprocess module
+====================================================
+In this section, "a ==> b" means that b can be used as a replacement
+for a.
+
+Note: All functions in this section fail (more or less) silently if
+the executed program cannot be found; this module raises an OSError
+exception.
+
+In the following examples, we assume that the subprocess module is
+imported with "from subprocess import *".
+
+
+Replacing /bin/sh shell backquote
+---------------------------------
+output=`mycmd myarg`
+==>
+output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
+
+
+Replacing shell pipe line
+-------------------------
+output=`dmesg | grep hda`
+==>
+p1 = Popen(["dmesg"], stdout=PIPE)
+p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+output = p2.communicate()[0]
+
+
+Replacing os.system()
+---------------------
+sts = os.system("mycmd" + " myarg")
+==>
+p = Popen("mycmd" + " myarg", shell=True)
+pid, sts = os.waitpid(p.pid, 0)
+
+Note:
+
+* Calling the program through the shell is usually not required.
+
+* It's easier to look at the returncode attribute than the
+ exitstatus.
+
+A more real-world example would look like this:
+
+try:
+ retcode = call("mycmd" + " myarg", shell=True)
+ if retcode < 0:
+ print >>sys.stderr, "Child was terminated by signal", -retcode
+ else:
+ print >>sys.stderr, "Child returned", retcode
+except OSError, e:
+ print >>sys.stderr, "Execution failed:", e
+
+
+Replacing os.spawn*
+-------------------
+P_NOWAIT example:
+
+pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
+==>
+pid = Popen(["/bin/mycmd", "myarg"]).pid
+
+
+P_WAIT example:
+
+retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
+==>
+retcode = call(["/bin/mycmd", "myarg"])
+
+
+Vector example:
+
+os.spawnvp(os.P_NOWAIT, path, args)
+==>
+Popen([path] + args[1:])
+
+
+Environment example:
+
+os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
+==>
+Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
+
+
+Replacing os.popen*
+-------------------
+pipe = os.popen(cmd, mode='r', bufsize)
+==>
+pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
+
+pipe = os.popen(cmd, mode='w', bufsize)
+==>
+pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
+
+
+(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdin, child_stdout) = (p.stdin, p.stdout)
+
+
+(child_stdin,
+ child_stdout,
+ child_stderr) = os.popen3(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+(child_stdin,
+ child_stdout,
+ child_stderr) = (p.stdin, p.stdout, p.stderr)
+
+
+(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
+(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
+
+
+Replacing popen2.*
+------------------
+Note: If the cmd argument to popen2 functions is a string, the command
+is executed through /bin/sh. If it is a list, the command is directly
+executed.
+
+(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
+==>
+p = Popen(["somestring"], shell=True, bufsize=bufsize
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdout, child_stdin) = (p.stdout, p.stdin)
+
+
+(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
+==>
+p = Popen(["mycmd", "myarg"], bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdout, child_stdin) = (p.stdout, p.stdin)
+
+The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen,
+except that:
+
+* subprocess.Popen raises an exception if the execution fails
+* the capturestderr argument is replaced with the stderr argument.
+* stdin=PIPE and stdout=PIPE must be specified.
+* popen2 closes all filedescriptors by default, but you have to specify
+ close_fds=True with subprocess.Popen.
+
+
+"""
+
+import sys
+mswindows = (sys.platform == "win32")
+
+import os
+import string
+import types
+import traceback
+
+# Exception classes used by this module.
+class CalledProcessError(Exception):
+ """This exception is raised when a process run by check_call() returns
+ a non-zero exit status. The exit status will be stored in the
+ returncode attribute."""
+ def __init__(self, returncode, cmd):
+ self.returncode = returncode
+ self.cmd = cmd
+ def __str__(self):
+ return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
+
+if mswindows:
+ try:
+ import threading
+ except ImportError:
+ # SCons: the threading module is only used by the communicate()
+ # method, which we don't actually use, so don't worry if we
+ # can't import it.
+ pass
+ import msvcrt
+ if 0: # <-- change this to use pywin32 instead of the _subprocess driver
+ import pywintypes
+ from win32api import GetStdHandle, STD_INPUT_HANDLE, \
+ STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
+ from win32api import GetCurrentProcess, DuplicateHandle, \
+ GetModuleFileName, GetVersion
+ from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
+ from win32pipe import CreatePipe
+ from win32process import CreateProcess, STARTUPINFO, \
+ GetExitCodeProcess, STARTF_USESTDHANDLES, \
+ STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
+ from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
+ else:
+ # SCons: don't die on Python versions that don't have _subprocess.
+ try:
+ from _subprocess import *
+ except ImportError:
+ pass
+ class STARTUPINFO:
+ dwFlags = 0
+ hStdInput = None
+ hStdOutput = None
+ hStdError = None
+ wShowWindow = 0
+ class pywintypes:
+ error = IOError
+else:
+ import select
+ import errno
+ import fcntl
+ import pickle
+
+ try:
+ fcntl.F_GETFD
+ except AttributeError:
+ fcntl.F_GETFD = 1
+
+ try:
+ fcntl.F_SETFD
+ except AttributeError:
+ fcntl.F_SETFD = 2
+
+__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"]
+
+try:
+ MAXFD = os.sysconf("SC_OPEN_MAX")
+except KeyboardInterrupt:
+ raise # SCons: don't swallow keyboard interrupts
+except:
+ MAXFD = 256
+
+# True/False does not exist on 2.2.0
+try:
+ False
+except NameError:
+ False = 0
+ True = 1
+
+try:
+ isinstance(1, int)
+except TypeError:
+ def is_int(obj):
+ return type(obj) == type(1)
+ def is_int_or_long(obj):
+ return type(obj) in (type(1), type(1L))
+else:
+ def is_int(obj):
+ return isinstance(obj, int)
+ def is_int_or_long(obj):
+ return isinstance(obj, (int, long))
+
+try:
+ types.StringTypes
+except AttributeError:
+ try:
+ types.StringTypes = (types.StringType, types.UnicodeType)
+ except AttributeError:
+ types.StringTypes = (types.StringType,)
+ def is_string(obj):
+ return type(obj) in types.StringTypes
+else:
+ def is_string(obj):
+ return isinstance(obj, types.StringTypes)
+
+_active = []
+
+def _cleanup():
+ for inst in _active[:]:
+ if inst.poll(_deadstate=sys.maxint) >= 0:
+ try:
+ _active.remove(inst)
+ except ValueError:
+ # This can happen if two threads create a new Popen instance.
+ # It's harmless that it was already removed, so ignore.
+ pass
+
+PIPE = -1
+STDOUT = -2
+
+
+def call(*popenargs, **kwargs):
+ """Run command with arguments. Wait for command to complete, then
+ return the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ retcode = call(["ls", "-l"])
+ """
+ return apply(Popen, popenargs, kwargs).wait()
+
+
+def check_call(*popenargs, **kwargs):
+ """Run command with arguments. Wait for command to complete. If
+ the exit code was zero then return, otherwise raise
+ CalledProcessError. The CalledProcessError object will have the
+ return code in the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ check_call(["ls", "-l"])
+ """
+ retcode = apply(call, popenargs, kwargs)
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ if retcode:
+ raise CalledProcessError(retcode, cmd)
+ return retcode
+
+
+def list2cmdline(seq):
+ """
+ Translate a sequence of arguments into a command line
+ string, using the same rules as the MS C runtime:
+
+ 1) Arguments are delimited by white space, which is either a
+ space or a tab.
+
+ 2) A string surrounded by double quotation marks is
+ interpreted as a single argument, regardless of white space
+ contained within. A quoted string can be embedded in an
+ argument.
+
+ 3) A double quotation mark preceded by a backslash is
+ interpreted as a literal double quotation mark.
+
+ 4) Backslashes are interpreted literally, unless they
+ immediately precede a double quotation mark.
+
+ 5) If backslashes immediately precede a double quotation mark,
+ every pair of backslashes is interpreted as a literal
+ backslash. If the number of backslashes is odd, the last
+ backslash escapes the next double quotation mark as
+ described in rule 3.
+ """
+
+ # See
+ # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
+ result = []
+ needquote = False
+ for arg in seq:
+ bs_buf = []
+
+ # Add a space to separate this argument from the others
+ if result:
+ result.append(' ')
+
+ needquote = (" " in arg) or ("\t" in arg)
+ if needquote:
+ result.append('"')
+
+ for c in arg:
+ if c == '\\':
+ # Don't know if we need to double yet.
+ bs_buf.append(c)
+ elif c == '"':
+ # Double backspaces.
+ result.append('\\' * len(bs_buf)*2)
+ bs_buf = []
+ result.append('\\"')
+ else:
+ # Normal char
+ if bs_buf:
+ result.extend(bs_buf)
+ bs_buf = []
+ result.append(c)
+
+ # Add remaining backspaces, if any.
+ if bs_buf:
+ result.extend(bs_buf)
+
+ if needquote:
+ result.extend(bs_buf)
+ result.append('"')
+
+ return string.join(result, '')
+
+
+try:
+ object
+except NameError:
+ class object:
+ pass
+
+class Popen(object):
+ def __init__(self, args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0):
+ """Create new Popen instance."""
+ _cleanup()
+
+ self._child_created = False
+ if not is_int_or_long(bufsize):
+ raise TypeError("bufsize must be an integer")
+
+ if mswindows:
+ if preexec_fn is not None:
+ raise ValueError("preexec_fn is not supported on Windows "
+ "platforms")
+ if close_fds:
+ raise ValueError("close_fds is not supported on Windows "
+ "platforms")
+ else:
+ # POSIX
+ if startupinfo is not None:
+ raise ValueError("startupinfo is only supported on Windows "
+ "platforms")
+ if creationflags != 0:
+ raise ValueError("creationflags is only supported on Windows "
+ "platforms")
+
+ self.stdin = None
+ self.stdout = None
+ self.stderr = None
+ self.pid = None
+ self.returncode = None
+ self.universal_newlines = universal_newlines
+
+ # Input and output objects. The general principle is like
+ # this:
+ #
+ # Parent Child
+ # ------ -----
+ # p2cwrite ---stdin---> p2cread
+ # c2pread <--stdout--- c2pwrite
+ # errread <--stderr--- errwrite
+ #
+ # On POSIX, the child objects are file descriptors. On
+ # Windows, these are Windows file handles. The parent objects
+ # are file descriptors on both platforms. The parent objects
+ # are None when not using PIPEs. The child objects are None
+ # when not redirecting.
+
+ (p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite) = self._get_handles(stdin, stdout, stderr)
+
+ self._execute_child(args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+
+ if p2cwrite:
+ self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
+ if c2pread:
+ if universal_newlines:
+ self.stdout = os.fdopen(c2pread, 'rU', bufsize)
+ else:
+ self.stdout = os.fdopen(c2pread, 'rb', bufsize)
+ if errread:
+ if universal_newlines:
+ self.stderr = os.fdopen(errread, 'rU', bufsize)
+ else:
+ self.stderr = os.fdopen(errread, 'rb', bufsize)
+
+
+ def _translate_newlines(self, data):
+ data = data.replace("\r\n", "\n")
+ data = data.replace("\r", "\n")
+ return data
+
+
+ def __del__(self):
+ if not self._child_created:
+ # We didn't get to successfully create a child process.
+ return
+ # In case the child hasn't been waited on, check if it's done.
+ self.poll(_deadstate=sys.maxint)
+ if self.returncode is None and _active is not None:
+ # Child is still running, keep us alive until we can wait on it.
+ _active.append(self)
+
+
+ def communicate(self, input=None):
+ """Interact with process: Send data to stdin. Read data from
+ stdout and stderr, until end-of-file is reached. Wait for
+ process to terminate. The optional input argument should be a
+ string to be sent to the child process, or None, if no data
+ should be sent to the child.
+
+ communicate() returns a tuple (stdout, stderr)."""
+
+ # Optimization: If we are only using one pipe, or no pipe at
+ # all, using select() or threads is unnecessary.
+ if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
+ stdout = None
+ stderr = None
+ if self.stdin:
+ if input:
+ self.stdin.write(input)
+ self.stdin.close()
+ elif self.stdout:
+ stdout = self.stdout.read()
+ elif self.stderr:
+ stderr = self.stderr.read()
+ self.wait()
+ return (stdout, stderr)
+
+ return self._communicate(input)
+
+
+ if mswindows:
+ #
+ # Windows methods
+ #
+ def _get_handles(self, stdin, stdout, stderr):
+ """Construct and return tupel with IO objects:
+ p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+ """
+ if stdin is None and stdout is None and stderr is None:
+ return (None, None, None, None, None, None)
+
+ p2cread, p2cwrite = None, None
+ c2pread, c2pwrite = None, None
+ errread, errwrite = None, None
+
+ if stdin is None:
+ p2cread = GetStdHandle(STD_INPUT_HANDLE)
+ elif stdin == PIPE:
+ p2cread, p2cwrite = CreatePipe(None, 0)
+ # Detach and turn into fd
+ p2cwrite = p2cwrite.Detach()
+ p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
+ elif is_int(stdin):
+ p2cread = msvcrt.get_osfhandle(stdin)
+ else:
+ # Assuming file-like object
+ p2cread = msvcrt.get_osfhandle(stdin.fileno())
+ p2cread = self._make_inheritable(p2cread)
+
+ if stdout is None:
+ c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
+ elif stdout == PIPE:
+ c2pread, c2pwrite = CreatePipe(None, 0)
+ # Detach and turn into fd
+ c2pread = c2pread.Detach()
+ c2pread = msvcrt.open_osfhandle(c2pread, 0)
+ elif is_int(stdout):
+ c2pwrite = msvcrt.get_osfhandle(stdout)
+ else:
+ # Assuming file-like object
+ c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
+ c2pwrite = self._make_inheritable(c2pwrite)
+
+ if stderr is None:
+ errwrite = GetStdHandle(STD_ERROR_HANDLE)
+ elif stderr == PIPE:
+ errread, errwrite = CreatePipe(None, 0)
+ # Detach and turn into fd
+ errread = errread.Detach()
+ errread = msvcrt.open_osfhandle(errread, 0)
+ elif stderr == STDOUT:
+ errwrite = c2pwrite
+ elif is_int(stderr):
+ errwrite = msvcrt.get_osfhandle(stderr)
+ else:
+ # Assuming file-like object
+ errwrite = msvcrt.get_osfhandle(stderr.fileno())
+ errwrite = self._make_inheritable(errwrite)
+
+ return (p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+
+
+ def _make_inheritable(self, handle):
+ """Return a duplicate of handle, which is inheritable"""
+ return DuplicateHandle(GetCurrentProcess(), handle,
+ GetCurrentProcess(), 0, 1,
+ DUPLICATE_SAME_ACCESS)
+
+
+ def _find_w9xpopen(self):
+ """Find and return absolut path to w9xpopen.exe"""
+ w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
+ "w9xpopen.exe")
+ if not os.path.exists(w9xpopen):
+ # Eeek - file-not-found - possibly an embedding
+ # situation - see if we can locate it in sys.exec_prefix
+ w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
+ "w9xpopen.exe")
+ if not os.path.exists(w9xpopen):
+ raise RuntimeError("Cannot locate w9xpopen.exe, which is "
+ "needed for Popen to work with your "
+ "shell or platform.")
+ return w9xpopen
+
+
+ def _execute_child(self, args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite):
+ """Execute program (MS Windows version)"""
+
+ if not isinstance(args, types.StringTypes):
+ args = list2cmdline(args)
+
+ # Process startup details
+ if startupinfo is None:
+ startupinfo = STARTUPINFO()
+ if None not in (p2cread, c2pwrite, errwrite):
+ startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES
+ startupinfo.hStdInput = p2cread
+ startupinfo.hStdOutput = c2pwrite
+ startupinfo.hStdError = errwrite
+
+ if shell:
+ startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW
+ startupinfo.wShowWindow = SW_HIDE
+ comspec = os.environ.get("COMSPEC", "cmd.exe")
+ args = comspec + " /c " + args
+ if (GetVersion() >= 0x80000000L or
+ os.path.basename(comspec).lower() == "command.com"):
+ # Win9x, or using command.com on NT. We need to
+ # use the w9xpopen intermediate program. For more
+ # information, see KB Q150956
+ # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp)
+ w9xpopen = self._find_w9xpopen()
+ args = '"%s" %s' % (w9xpopen, args)
+ # Not passing CREATE_NEW_CONSOLE has been known to
+ # cause random failures on win9x. Specifically a
+ # dialog: "Your program accessed mem currently in
+ # use at xxx" and a hopeful warning about the
+ # stability of your system. Cost is Ctrl+C wont
+ # kill children.
+ creationflags = creationflags | CREATE_NEW_CONSOLE
+
+ # Start the process
+ try:
+ hp, ht, pid, tid = CreateProcess(executable, args,
+ # no special security
+ None, None,
+ # must inherit handles to pass std
+ # handles
+ 1,
+ creationflags,
+ env,
+ cwd,
+ startupinfo)
+ except pywintypes.error, e:
+ # Translate pywintypes.error to WindowsError, which is
+ # a subclass of OSError. FIXME: We should really
+ # translate errno using _sys_errlist (or simliar), but
+ # how can this be done from Python?
+ raise apply(WindowsError, e.args)
+
+ # Retain the process handle, but close the thread handle
+ self._child_created = True
+ self._handle = hp
+ self.pid = pid
+ ht.Close()
+
+ # Child is launched. Close the parent's copy of those pipe
+ # handles that only the child should have open. You need
+ # to make sure that no handles to the write end of the
+ # output pipe are maintained in this process or else the
+ # pipe will not close when the child process exits and the
+ # ReadFile will hang.
+ if p2cread is not None:
+ p2cread.Close()
+ if c2pwrite is not None:
+ c2pwrite.Close()
+ if errwrite is not None:
+ errwrite.Close()
+
+
+ def poll(self, _deadstate=None):
+ """Check if child process has terminated. Returns returncode
+ attribute."""
+ if self.returncode is None:
+ if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
+ self.returncode = GetExitCodeProcess(self._handle)
+ return self.returncode
+
+
+ def wait(self):
+ """Wait for child process to terminate. Returns returncode
+ attribute."""
+ if self.returncode is None:
+ obj = WaitForSingleObject(self._handle, INFINITE)
+ self.returncode = GetExitCodeProcess(self._handle)
+ return self.returncode
+
+
+ def _readerthread(self, fh, buffer):
+ buffer.append(fh.read())
+
+
+ def _communicate(self, input):
+ stdout = None # Return
+ stderr = None # Return
+
+ if self.stdout:
+ stdout = []
+ stdout_thread = threading.Thread(target=self._readerthread,
+ args=(self.stdout, stdout))
+ stdout_thread.setDaemon(True)
+ stdout_thread.start()
+ if self.stderr:
+ stderr = []
+ stderr_thread = threading.Thread(target=self._readerthread,
+ args=(self.stderr, stderr))
+ stderr_thread.setDaemon(True)
+ stderr_thread.start()
+
+ if self.stdin:
+ if input is not None:
+ self.stdin.write(input)
+ self.stdin.close()
+
+ if self.stdout:
+ stdout_thread.join()
+ if self.stderr:
+ stderr_thread.join()
+
+ # All data exchanged. Translate lists into strings.
+ if stdout is not None:
+ stdout = stdout[0]
+ if stderr is not None:
+ stderr = stderr[0]
+
+ # Translate newlines, if requested. We cannot let the file
+ # object do the translation: It is based on stdio, which is
+ # impossible to combine with select (unless forcing no
+ # buffering).
+ if self.universal_newlines and hasattr(file, 'newlines'):
+ if stdout:
+ stdout = self._translate_newlines(stdout)
+ if stderr:
+ stderr = self._translate_newlines(stderr)
+
+ self.wait()
+ return (stdout, stderr)
+
+ else:
+ #
+ # POSIX methods
+ #
+ def _get_handles(self, stdin, stdout, stderr):
+ """Construct and return tupel with IO objects:
+ p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+ """
+ p2cread, p2cwrite = None, None
+ c2pread, c2pwrite = None, None
+ errread, errwrite = None, None
+
+ if stdin is None:
+ pass
+ elif stdin == PIPE:
+ p2cread, p2cwrite = os.pipe()
+ elif is_int(stdin):
+ p2cread = stdin
+ else:
+ # Assuming file-like object
+ p2cread = stdin.fileno()
+
+ if stdout is None:
+ pass
+ elif stdout == PIPE:
+ c2pread, c2pwrite = os.pipe()
+ elif is_int(stdout):
+ c2pwrite = stdout
+ else:
+ # Assuming file-like object
+ c2pwrite = stdout.fileno()
+
+ if stderr is None:
+ pass
+ elif stderr == PIPE:
+ errread, errwrite = os.pipe()
+ elif stderr == STDOUT:
+ errwrite = c2pwrite
+ elif is_int(stderr):
+ errwrite = stderr
+ else:
+ # Assuming file-like object
+ errwrite = stderr.fileno()
+
+ return (p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+
+
+ def _set_cloexec_flag(self, fd):
+ try:
+ cloexec_flag = fcntl.FD_CLOEXEC
+ except AttributeError:
+ cloexec_flag = 1
+
+ old = fcntl.fcntl(fd, fcntl.F_GETFD)
+ fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
+
+
+ def _close_fds(self, but):
+ for i in xrange(3, MAXFD):
+ if i == but:
+ continue
+ try:
+ os.close(i)
+ except KeyboardInterrupt:
+ raise # SCons: don't swallow keyboard interrupts
+ except:
+ pass
+
+
+ def _execute_child(self, args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite):
+ """Execute program (POSIX version)"""
+
+ if is_string(args):
+ args = [args]
+
+ if shell:
+ args = ["/bin/sh", "-c"] + args
+
+ if executable is None:
+ executable = args[0]
+
+ # For transferring possible exec failure from child to parent
+ # The first char specifies the exception type: 0 means
+ # OSError, 1 means some other error.
+ errpipe_read, errpipe_write = os.pipe()
+ self._set_cloexec_flag(errpipe_write)
+
+ self.pid = os.fork()
+ self._child_created = True
+ if self.pid == 0:
+ # Child
+ try:
+ # Close parent's pipe ends
+ if p2cwrite:
+ os.close(p2cwrite)
+ if c2pread:
+ os.close(c2pread)
+ if errread:
+ os.close(errread)
+ os.close(errpipe_read)
+
+ # Dup fds for child
+ if p2cread:
+ os.dup2(p2cread, 0)
+ if c2pwrite:
+ os.dup2(c2pwrite, 1)
+ if errwrite:
+ os.dup2(errwrite, 2)
+
+ # Close pipe fds. Make sure we don't close the same
+ # fd more than once, or standard fds.
+ try:
+ set
+ except NameError:
+ # Fall-back for earlier Python versions, so epydoc
+ # can use this module directly to execute things.
+ if p2cread:
+ os.close(p2cread)
+ if c2pwrite and c2pwrite not in (p2cread,):
+ os.close(c2pwrite)
+ if errwrite and errwrite not in (p2cread, c2pwrite):
+ os.close(errwrite)
+ else:
+ for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)):
+ if fd: os.close(fd)
+
+ # Close all other fds, if asked for
+ if close_fds:
+ self._close_fds(but=errpipe_write)
+
+ if cwd is not None:
+ os.chdir(cwd)
+
+ if preexec_fn:
+ apply(preexec_fn)
+
+ if env is None:
+ os.execvp(executable, args)
+ else:
+ os.execvpe(executable, args, env)
+
+ except KeyboardInterrupt:
+ raise # SCons: don't swallow keyboard interrupts
+
+ except:
+ exc_type, exc_value, tb = sys.exc_info()
+ # Save the traceback and attach it to the exception object
+ exc_lines = traceback.format_exception(exc_type,
+ exc_value,
+ tb)
+ exc_value.child_traceback = string.join(exc_lines, '')
+ os.write(errpipe_write, pickle.dumps(exc_value))
+
+ # This exitcode won't be reported to applications, so it
+ # really doesn't matter what we return.
+ os._exit(255)
+
+ # Parent
+ os.close(errpipe_write)
+ if p2cread and p2cwrite:
+ os.close(p2cread)
+ if c2pwrite and c2pread:
+ os.close(c2pwrite)
+ if errwrite and errread:
+ os.close(errwrite)
+
+ # Wait for exec to fail or succeed; possibly raising exception
+ data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB
+ os.close(errpipe_read)
+ if data != "":
+ os.waitpid(self.pid, 0)
+ child_exception = pickle.loads(data)
+ raise child_exception
+
+
+ def _handle_exitstatus(self, sts):
+ if os.WIFSIGNALED(sts):
+ self.returncode = -os.WTERMSIG(sts)
+ elif os.WIFEXITED(sts):
+ self.returncode = os.WEXITSTATUS(sts)
+ else:
+ # Should never happen
+ raise RuntimeError("Unknown child exit status!")
+
+
+ def poll(self, _deadstate=None):
+ """Check if child process has terminated. Returns returncode
+ attribute."""
+ if self.returncode is None:
+ try:
+ pid, sts = os.waitpid(self.pid, os.WNOHANG)
+ if pid == self.pid:
+ self._handle_exitstatus(sts)
+ except os.error:
+ if _deadstate is not None:
+ self.returncode = _deadstate
+ return self.returncode
+
+
+ def wait(self):
+ """Wait for child process to terminate. Returns returncode
+ attribute."""
+ if self.returncode is None:
+ pid, sts = os.waitpid(self.pid, 0)
+ self._handle_exitstatus(sts)
+ return self.returncode
+
+
+ def _communicate(self, input):
+ read_set = []
+ write_set = []
+ stdout = None # Return
+ stderr = None # Return
+
+ if self.stdin:
+ # Flush stdio buffer. This might block, if the user has
+ # been writing to .stdin in an uncontrolled fashion.
+ self.stdin.flush()
+ if input:
+ write_set.append(self.stdin)
+ else:
+ self.stdin.close()
+ if self.stdout:
+ read_set.append(self.stdout)
+ stdout = []
+ if self.stderr:
+ read_set.append(self.stderr)
+ stderr = []
+
+ input_offset = 0
+ while read_set or write_set:
+ rlist, wlist, xlist = select.select(read_set, write_set, [])
+
+ if self.stdin in wlist:
+ # When select has indicated that the file is writable,
+ # we can write up to PIPE_BUF bytes without risk
+ # blocking. POSIX defines PIPE_BUF >= 512
+ bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512))
+ input_offset = input_offset + bytes_written
+ if input_offset >= len(input):
+ self.stdin.close()
+ write_set.remove(self.stdin)
+
+ if self.stdout in rlist:
+ data = os.read(self.stdout.fileno(), 1024)
+ if data == "":
+ self.stdout.close()
+ read_set.remove(self.stdout)
+ stdout.append(data)
+
+ if self.stderr in rlist:
+ data = os.read(self.stderr.fileno(), 1024)
+ if data == "":
+ self.stderr.close()
+ read_set.remove(self.stderr)
+ stderr.append(data)
+
+ # All data exchanged. Translate lists into strings.
+ if stdout is not None:
+ stdout = string.join(stdout, '')
+ if stderr is not None:
+ stderr = string.join(stderr, '')
+
+ # Translate newlines, if requested. We cannot let the file
+ # object do the translation: It is based on stdio, which is
+ # impossible to combine with select (unless forcing no
+ # buffering).
+ if self.universal_newlines and hasattr(file, 'newlines'):
+ if stdout:
+ stdout = self._translate_newlines(stdout)
+ if stderr:
+ stderr = self._translate_newlines(stderr)
+
+ self.wait()
+ return (stdout, stderr)
+
+
+def _demo_posix():
+ #
+ # Example 1: Simple redirection: Get process list
+ #
+ plist = Popen(["ps"], stdout=PIPE).communicate()[0]
+ print "Process list:"
+ print plist
+
+ #
+ # Example 2: Change uid before executing child
+ #
+ if os.getuid() == 0:
+ p = Popen(["id"], preexec_fn=lambda: os.setuid(100))
+ p.wait()
+
+ #
+ # Example 3: Connecting several subprocesses
+ #
+ print "Looking for 'hda'..."
+ p1 = Popen(["dmesg"], stdout=PIPE)
+ p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+ print repr(p2.communicate()[0])
+
+ #
+ # Example 4: Catch execution error
+ #
+ print
+ print "Trying a weird file..."
+ try:
+ print Popen(["/this/path/does/not/exist"]).communicate()
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ print "The file didn't exist. I thought so..."
+ print "Child traceback:"
+ print e.child_traceback
+ else:
+ print "Error", e.errno
+ else:
+ sys.stderr.write( "Gosh. No error.\n" )
+
+
+def _demo_windows():
+ #
+ # Example 1: Connecting several subprocesses
+ #
+ print "Looking for 'PROMPT' in set output..."
+ p1 = Popen("set", stdout=PIPE, shell=True)
+ p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE)
+ print repr(p2.communicate()[0])
+
+ #
+ # Example 2: Simple execution of program
+ #
+ print "Executing calc..."
+ p = Popen("calc")
+ p.wait()
+
+
+if __name__ == "__main__":
+ if mswindows:
+ _demo_windows()
+ else:
+ _demo_posix()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/_scons_textwrap.py b/src/engine/SCons/compat/_scons_textwrap.py
new file mode 100644
index 0000000..81781af
--- /dev/null
+++ b/src/engine/SCons/compat/_scons_textwrap.py
@@ -0,0 +1,382 @@
+"""Text wrapping and filling.
+"""
+
+# Copyright (C) 1999-2001 Gregory P. Ward.
+# Copyright (C) 2002, 2003 Python Software Foundation.
+# Written by Greg Ward <gward@python.net>
+
+__revision__ = "$Id: textwrap.py,v 1.32.8.2 2004/05/13 01:48:15 gward Exp $"
+
+import string, re
+
+try:
+ unicode
+except NameError:
+ class unicode:
+ pass
+
+# Do the right thing with boolean values for all known Python versions
+# (so this module can be copied to projects that don't depend on Python
+# 2.3, e.g. Optik and Docutils).
+try:
+ True, False
+except NameError:
+ (True, False) = (1, 0)
+
+__all__ = ['TextWrapper', 'wrap', 'fill']
+
+# Hardcode the recognized whitespace characters to the US-ASCII
+# whitespace characters. The main reason for doing this is that in
+# ISO-8859-1, 0xa0 is non-breaking whitespace, so in certain locales
+# that character winds up in string.whitespace. Respecting
+# string.whitespace in those cases would 1) make textwrap treat 0xa0 the
+# same as any other whitespace char, which is clearly wrong (it's a
+# *non-breaking* space), 2) possibly cause problems with Unicode,
+# since 0xa0 is not in range(128).
+_whitespace = '\t\n\x0b\x0c\r '
+
+class TextWrapper:
+ """
+ Object for wrapping/filling text. The public interface consists of
+ the wrap() and fill() methods; the other methods are just there for
+ subclasses to override in order to tweak the default behaviour.
+ If you want to completely replace the main wrapping algorithm,
+ you'll probably have to override _wrap_chunks().
+
+ Several instance attributes control various aspects of wrapping:
+ width (default: 70)
+ the maximum width of wrapped lines (unless break_long_words
+ is false)
+ initial_indent (default: "")
+ string that will be prepended to the first line of wrapped
+ output. Counts towards the line's width.
+ subsequent_indent (default: "")
+ string that will be prepended to all lines save the first
+ of wrapped output; also counts towards each line's width.
+ expand_tabs (default: true)
+ Expand tabs in input text to spaces before further processing.
+ Each tab will become 1 .. 8 spaces, depending on its position in
+ its line. If false, each tab is treated as a single character.
+ replace_whitespace (default: true)
+ Replace all whitespace characters in the input text by spaces
+ after tab expansion. Note that if expand_tabs is false and
+ replace_whitespace is true, every tab will be converted to a
+ single space!
+ fix_sentence_endings (default: false)
+ Ensure that sentence-ending punctuation is always followed
+ by two spaces. Off by default because the algorithm is
+ (unavoidably) imperfect.
+ break_long_words (default: true)
+ Break words longer than 'width'. If false, those words will not
+ be broken, and some lines might be longer than 'width'.
+ """
+
+ whitespace_trans = string.maketrans(_whitespace, ' ' * len(_whitespace))
+
+ unicode_whitespace_trans = {}
+ try:
+ uspace = eval("ord(u' ')")
+ except SyntaxError:
+ # Python1.5 doesn't understand u'' syntax, in which case we
+ # won't actually use the unicode translation below, so it
+ # doesn't matter what value we put in the table.
+ uspace = ord(' ')
+ for x in map(ord, _whitespace):
+ unicode_whitespace_trans[x] = uspace
+
+ # This funky little regex is just the trick for splitting
+ # text up into word-wrappable chunks. E.g.
+ # "Hello there -- you goof-ball, use the -b option!"
+ # splits into
+ # Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option!
+ # (after stripping out empty strings).
+ try:
+ wordsep_re = re.compile(r'(\s+|' # any whitespace
+ r'[^\s\w]*\w{2,}-(?=\w{2,})|' # hyphenated words
+ r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
+ except re.error:
+ # Pre-2.0 Python versions don't have the (?<= negative look-behind
+ # assertion. It mostly doesn't matter for the simple input
+ # SCons is going to give it, so just leave it out.
+ wordsep_re = re.compile(r'(\s+|' # any whitespace
+ r'-*\w{2,}-(?=\w{2,}))') # hyphenated words
+
+ # XXX will there be a locale-or-charset-aware version of
+ # string.lowercase in 2.3?
+ sentence_end_re = re.compile(r'[%s]' # lowercase letter
+ r'[\.\!\?]' # sentence-ending punct.
+ r'[\"\']?' # optional end-of-quote
+ % string.lowercase)
+
+
+ def __init__(self,
+ width=70,
+ initial_indent="",
+ subsequent_indent="",
+ expand_tabs=True,
+ replace_whitespace=True,
+ fix_sentence_endings=False,
+ break_long_words=True):
+ self.width = width
+ self.initial_indent = initial_indent
+ self.subsequent_indent = subsequent_indent
+ self.expand_tabs = expand_tabs
+ self.replace_whitespace = replace_whitespace
+ self.fix_sentence_endings = fix_sentence_endings
+ self.break_long_words = break_long_words
+
+
+ # -- Private methods -----------------------------------------------
+ # (possibly useful for subclasses to override)
+
+ def _munge_whitespace(self, text):
+ """_munge_whitespace(text : string) -> string
+
+ Munge whitespace in text: expand tabs and convert all other
+ whitespace characters to spaces. Eg. " foo\tbar\n\nbaz"
+ becomes " foo bar baz".
+ """
+ if self.expand_tabs:
+ text = string.expandtabs(text)
+ if self.replace_whitespace:
+ if type(text) == type(''):
+ text = string.translate(text, self.whitespace_trans)
+ elif isinstance(text, unicode):
+ text = string.translate(text, self.unicode_whitespace_trans)
+ return text
+
+
+ def _split(self, text):
+ """_split(text : string) -> [string]
+
+ Split the text to wrap into indivisible chunks. Chunks are
+ not quite the same as words; see wrap_chunks() for full
+ details. As an example, the text
+ Look, goof-ball -- use the -b option!
+ breaks into the following chunks:
+ 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ',
+ 'use', ' ', 'the', ' ', '-b', ' ', 'option!'
+ """
+ chunks = self.wordsep_re.split(text)
+ chunks = filter(None, chunks)
+ return chunks
+
+ def _fix_sentence_endings(self, chunks):
+ """_fix_sentence_endings(chunks : [string])
+
+ Correct for sentence endings buried in 'chunks'. Eg. when the
+ original text contains "... foo.\nBar ...", munge_whitespace()
+ and split() will convert that to [..., "foo.", " ", "Bar", ...]
+ which has one too few spaces; this method simply changes the one
+ space to two.
+ """
+ i = 0
+ pat = self.sentence_end_re
+ while i < len(chunks)-1:
+ if chunks[i+1] == " " and pat.search(chunks[i]):
+ chunks[i+1] = " "
+ i = i + 2
+ else:
+ i = i + 1
+
+ def _handle_long_word(self, chunks, cur_line, cur_len, width):
+ """_handle_long_word(chunks : [string],
+ cur_line : [string],
+ cur_len : int, width : int)
+
+ Handle a chunk of text (most likely a word, not whitespace) that
+ is too long to fit in any line.
+ """
+ space_left = max(width - cur_len, 1)
+
+ # If we're allowed to break long words, then do so: put as much
+ # of the next chunk onto the current line as will fit.
+ if self.break_long_words:
+ cur_line.append(chunks[0][0:space_left])
+ chunks[0] = chunks[0][space_left:]
+
+ # Otherwise, we have to preserve the long word intact. Only add
+ # it to the current line if there's nothing already there --
+ # that minimizes how much we violate the width constraint.
+ elif not cur_line:
+ cur_line.append(chunks.pop(0))
+
+ # If we're not allowed to break long words, and there's already
+ # text on the current line, do nothing. Next time through the
+ # main loop of _wrap_chunks(), we'll wind up here again, but
+ # cur_len will be zero, so the next line will be entirely
+ # devoted to the long word that we can't handle right now.
+
+ def _wrap_chunks(self, chunks):
+ """_wrap_chunks(chunks : [string]) -> [string]
+
+ Wrap a sequence of text chunks and return a list of lines of
+ length 'self.width' or less. (If 'break_long_words' is false,
+ some lines may be longer than this.) Chunks correspond roughly
+ to words and the whitespace between them: each chunk is
+ indivisible (modulo 'break_long_words'), but a line break can
+ come between any two chunks. Chunks should not have internal
+ whitespace; ie. a chunk is either all whitespace or a "word".
+ Whitespace chunks will be removed from the beginning and end of
+ lines, but apart from that whitespace is preserved.
+ """
+ lines = []
+ if self.width <= 0:
+ raise ValueError("invalid width %r (must be > 0)" % self.width)
+
+ while chunks:
+
+ # Start the list of chunks that will make up the current line.
+ # cur_len is just the length of all the chunks in cur_line.
+ cur_line = []
+ cur_len = 0
+
+ # Figure out which static string will prefix this line.
+ if lines:
+ indent = self.subsequent_indent
+ else:
+ indent = self.initial_indent
+
+ # Maximum width for this line.
+ width = self.width - len(indent)
+
+ # First chunk on line is whitespace -- drop it, unless this
+ # is the very beginning of the text (ie. no lines started yet).
+ if string.strip(chunks[0]) == '' and lines:
+ del chunks[0]
+
+ while chunks:
+ l = len(chunks[0])
+
+ # Can at least squeeze this chunk onto the current line.
+ if cur_len + l <= width:
+ cur_line.append(chunks.pop(0))
+ cur_len = cur_len + l
+
+ # Nope, this line is full.
+ else:
+ break
+
+ # The current line is full, and the next chunk is too big to
+ # fit on *any* line (not just this one).
+ if chunks and len(chunks[0]) > width:
+ self._handle_long_word(chunks, cur_line, cur_len, width)
+
+ # If the last chunk on this line is all whitespace, drop it.
+ if cur_line and string.strip(cur_line[-1]) == '':
+ del cur_line[-1]
+
+ # Convert current line back to a string and store it in list
+ # of all lines (return value).
+ if cur_line:
+ lines.append(indent + string.join(cur_line, ''))
+
+ return lines
+
+
+ # -- Public interface ----------------------------------------------
+
+ def wrap(self, text):
+ """wrap(text : string) -> [string]
+
+ Reformat the single paragraph in 'text' so it fits in lines of
+ no more than 'self.width' columns, and return a list of wrapped
+ lines. Tabs in 'text' are expanded with string.expandtabs(),
+ and all other whitespace characters (including newline) are
+ converted to space.
+ """
+ text = self._munge_whitespace(text)
+ indent = self.initial_indent
+ chunks = self._split(text)
+ if self.fix_sentence_endings:
+ self._fix_sentence_endings(chunks)
+ return self._wrap_chunks(chunks)
+
+ def fill(self, text):
+ """fill(text : string) -> string
+
+ Reformat the single paragraph in 'text' to fit in lines of no
+ more than 'self.width' columns, and return a new string
+ containing the entire wrapped paragraph.
+ """
+ return string.join(self.wrap(text), "\n")
+
+
+# -- Convenience interface ---------------------------------------------
+
+def wrap(text, width=70, **kwargs):
+ """Wrap a single paragraph of text, returning a list of wrapped lines.
+
+ Reformat the single paragraph in 'text' so it fits in lines of no
+ more than 'width' columns, and return a list of wrapped lines. By
+ default, tabs in 'text' are expanded with string.expandtabs(), and
+ all other whitespace characters (including newline) are converted to
+ space. See TextWrapper class for available keyword args to customize
+ wrapping behaviour.
+ """
+ kw = kwargs.copy()
+ kw['width'] = width
+ w = apply(TextWrapper, (), kw)
+ return w.wrap(text)
+
+def fill(text, width=70, **kwargs):
+ """Fill a single paragraph of text, returning a new string.
+
+ Reformat the single paragraph in 'text' to fit in lines of no more
+ than 'width' columns, and return a new string containing the entire
+ wrapped paragraph. As with wrap(), tabs are expanded and other
+ whitespace characters converted to space. See TextWrapper class for
+ available keyword args to customize wrapping behaviour.
+ """
+ kw = kwargs.copy()
+ kw['width'] = width
+ w = apply(TextWrapper, (), kw)
+ return w.fill(text)
+
+
+# -- Loosely related functionality -------------------------------------
+
+def dedent(text):
+ """dedent(text : string) -> string
+
+ Remove any whitespace than can be uniformly removed from the left
+ of every line in `text`.
+
+ This can be used e.g. to make triple-quoted strings line up with
+ the left edge of screen/whatever, while still presenting it in the
+ source code in indented form.
+
+ For example:
+
+ def test():
+ # end first line with \ to avoid the empty line!
+ s = '''\
+ hello
+ world
+ '''
+ print repr(s) # prints ' hello\n world\n '
+ print repr(dedent(s)) # prints 'hello\n world\n'
+ """
+ lines = text.expandtabs().split('\n')
+ margin = None
+ for line in lines:
+ content = line.lstrip()
+ if not content:
+ continue
+ indent = len(line) - len(content)
+ if margin is None:
+ margin = indent
+ else:
+ margin = min(margin, indent)
+
+ if margin is not None and margin > 0:
+ for i in range(len(lines)):
+ lines[i] = lines[i][margin:]
+
+ return string.join(lines, '\n')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/compat/builtins.py b/src/engine/SCons/compat/builtins.py
new file mode 100644
index 0000000..a79b2ad
--- /dev/null
+++ b/src/engine/SCons/compat/builtins.py
@@ -0,0 +1,187 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+# Portions of the following are derived from the compat.py file in
+# Twisted, under the following copyright:
+#
+# Copyright (c) 2001-2004 Twisted Matrix Laboratories
+
+__doc__ = """
+Compatibility idioms for __builtin__ names
+
+This module adds names to the __builtin__ module for things that we want
+to use in SCons but which don't show up until later Python versions than
+the earliest ones we support.
+
+This module checks for the following __builtin__ names:
+
+ all()
+ any()
+ bool()
+ dict()
+ True
+ False
+ zip()
+
+Implementations of functions are *NOT* guaranteed to be fully compliant
+with these functions in later versions of Python. We are only concerned
+with adding functionality that we actually use in SCons, so be wary
+if you lift this code for other uses. (That said, making these more
+nearly the same as later, official versions is still a desirable goal,
+we just don't need to be obsessive about it.)
+
+If you're looking at this with pydoc and various names don't show up in
+the FUNCTIONS or DATA output, that means those names are already built in
+to this version of Python and we don't need to add them from this module.
+"""
+
+__revision__ = "src/engine/SCons/compat/builtins.py 4577 2009/12/27 19:44:43 scons"
+
+import __builtin__
+
+try:
+ all
+except NameError:
+ # Pre-2.5 Python has no all() function.
+ def all(iterable):
+ """
+ Returns True if all elements of the iterable are true.
+ """
+ for element in iterable:
+ if not element:
+ return False
+ return True
+ __builtin__.all = all
+ all = all
+
+try:
+ any
+except NameError:
+ # Pre-2.5 Python has no any() function.
+ def any(iterable):
+ """
+ Returns True if any element of the iterable is true.
+ """
+ for element in iterable:
+ if element:
+ return True
+ return False
+ __builtin__.any = any
+ any = any
+
+try:
+ bool
+except NameError:
+ # Pre-2.2 Python has no bool() function.
+ def bool(value):
+ """Demote a value to 0 or 1, depending on its truth value.
+
+ This is not to be confused with types.BooleanType, which is
+ way too hard to duplicate in early Python versions to be
+ worth the trouble.
+ """
+ return not not value
+ __builtin__.bool = bool
+ bool = bool
+
+try:
+ dict
+except NameError:
+ # Pre-2.2 Python has no dict() keyword.
+ def dict(seq=[], **kwargs):
+ """
+ New dictionary initialization.
+ """
+ d = {}
+ for k, v in seq:
+ d[k] = v
+ d.update(kwargs)
+ return d
+ __builtin__.dict = dict
+
+try:
+ False
+except NameError:
+ # Pre-2.2 Python has no False keyword.
+ __builtin__.False = not 1
+ # Assign to False in this module namespace so it shows up in pydoc output.
+ False = False
+
+try:
+ True
+except NameError:
+ # Pre-2.2 Python has no True keyword.
+ __builtin__.True = not 0
+ # Assign to True in this module namespace so it shows up in pydoc output.
+ True = True
+
+try:
+ file
+except NameError:
+ # Pre-2.2 Python has no file() function.
+ __builtin__.file = open
+
+#
+try:
+ zip
+except NameError:
+ # Pre-2.2 Python has no zip() function.
+ def zip(*lists):
+ """
+ Emulates the behavior we need from the built-in zip() function
+ added in Python 2.2.
+
+ Returns a list of tuples, where each tuple contains the i-th
+ element rom each of the argument sequences. The returned
+ list is truncated in length to the length of the shortest
+ argument sequence.
+ """
+ result = []
+ for i in xrange(min(map(len, lists))):
+ result.append(tuple(map(lambda l, i=i: l[i], lists)))
+ return result
+ __builtin__.zip = zip
+
+
+
+#if sys.version_info[:3] in ((2, 2, 0), (2, 2, 1)):
+# def lstrip(s, c=string.whitespace):
+# while s and s[0] in c:
+# s = s[1:]
+# return s
+# def rstrip(s, c=string.whitespace):
+# while s and s[-1] in c:
+# s = s[:-1]
+# return s
+# def strip(s, c=string.whitespace, l=lstrip, r=rstrip):
+# return l(r(s, c), c)
+#
+# object.__setattr__(str, 'lstrip', lstrip)
+# object.__setattr__(str, 'rstrip', rstrip)
+# object.__setattr__(str, 'strip', strip)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py
new file mode 100644
index 0000000..2a16d37
--- /dev/null
+++ b/src/engine/SCons/cpp.py
@@ -0,0 +1,598 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/cpp.py 4577 2009/12/27 19:44:43 scons"
+
+__doc__ = """
+SCons C Pre-Processor module
+"""
+
+# TODO(1.5): remove this import
+# This module doesn't use anything from SCons by name, but we import SCons
+# here to pull in zip() from the SCons.compat layer for early Pythons.
+import SCons
+
+import os
+import re
+import string
+
+#
+# First "subsystem" of regular expressions that we set up:
+#
+# Stuff to turn the C preprocessor directives in a file's contents into
+# a list of tuples that we can process easily.
+#
+
+# A table of regular expressions that fetch the arguments from the rest of
+# a C preprocessor line. Different directives have different arguments
+# that we want to fetch, using the regular expressions to which the lists
+# of preprocessor directives map.
+cpp_lines_dict = {
+ # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument,
+ # separated from the keyword by white space.
+ ('if', 'elif', 'ifdef', 'ifndef',)
+ : '\s+(.+)',
+
+ # Fetch the rest of a #import/#include/#include_next line as one
+ # argument, with white space optional.
+ ('import', 'include', 'include_next',)
+ : '\s*(.+)',
+
+ # We don't care what comes after a #else or #endif line.
+ ('else', 'endif',) : '',
+
+ # Fetch three arguments from a #define line:
+ # 1) The #defined keyword.
+ # 2) The optional parentheses and arguments (if it's a function-like
+ # macro, '' if it's not).
+ # 3) The expansion value.
+ ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)',
+
+ # Fetch the #undefed keyword from a #undef line.
+ ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)',
+}
+
+# Create a table that maps each individual C preprocessor directive to
+# the corresponding compiled regular expression that fetches the arguments
+# we care about.
+Table = {}
+for op_list, expr in cpp_lines_dict.items():
+ e = re.compile(expr)
+ for op in op_list:
+ Table[op] = e
+del e
+del op
+del op_list
+
+# Create a list of the expressions we'll use to match all of the
+# preprocessor directives. These are the same as the directives
+# themselves *except* that we must use a negative lookahead assertion
+# when matching "if" so it doesn't match the "if" in "ifdef."
+override = {
+ 'if' : 'if(?!def)',
+}
+l = map(lambda x, o=override: o.get(x, x), Table.keys())
+
+
+# Turn the list of expressions into one big honkin' regular expression
+# that will match all the preprocessor lines at once. This will return
+# a list of tuples, one for each preprocessor line. The preprocessor
+# directive will be the first element in each tuple, and the rest of
+# the line will be the second element.
+e = '^\s*#\s*(' + string.join(l, '|') + ')(.*)$'
+
+# And last but not least, compile the expression.
+CPP_Expression = re.compile(e, re.M)
+
+
+
+
+#
+# Second "subsystem" of regular expressions that we set up:
+#
+# Stuff to translate a C preprocessor expression (as found on a #if or
+# #elif line) into an equivalent Python expression that we can eval().
+#
+
+# A dictionary that maps the C representation of Boolean operators
+# to their Python equivalents.
+CPP_to_Python_Ops_Dict = {
+ '!' : ' not ',
+ '!=' : ' != ',
+ '&&' : ' and ',
+ '||' : ' or ',
+ '?' : ' and ',
+ ':' : ' or ',
+ '\r' : '',
+}
+
+CPP_to_Python_Ops_Sub = lambda m, d=CPP_to_Python_Ops_Dict: d[m.group(0)]
+
+# We have to sort the keys by length so that longer expressions
+# come *before* shorter expressions--in particular, "!=" must
+# come before "!" in the alternation. Without this, the Python
+# re module, as late as version 2.2.2, empirically matches the
+# "!" in "!=" first, instead of finding the longest match.
+# What's up with that?
+l = CPP_to_Python_Ops_Dict.keys()
+l.sort(lambda a, b: cmp(len(b), len(a)))
+
+# Turn the list of keys into one regular expression that will allow us
+# to substitute all of the operators at once.
+expr = string.join(map(re.escape, l), '|')
+
+# ...and compile the expression.
+CPP_to_Python_Ops_Expression = re.compile(expr)
+
+# A separate list of expressions to be evaluated and substituted
+# sequentially, not all at once.
+CPP_to_Python_Eval_List = [
+ ['defined\s+(\w+)', '__dict__.has_key("\\1")'],
+ ['defined\s*\((\w+)\)', '__dict__.has_key("\\1")'],
+ ['/\*.*\*/', ''],
+ ['/\*.*', ''],
+ ['//.*', ''],
+ ['(0x[0-9A-Fa-f]*)[UL]+', '\\1L'],
+]
+
+# Replace the string representations of the regular expressions in the
+# list with compiled versions.
+for l in CPP_to_Python_Eval_List:
+ l[0] = re.compile(l[0])
+
+# Wrap up all of the above into a handy function.
+def CPP_to_Python(s):
+ """
+ Converts a C pre-processor expression into an equivalent
+ Python expression that can be evaluated.
+ """
+ s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s)
+ for expr, repl in CPP_to_Python_Eval_List:
+ s = expr.sub(repl, s)
+ return s
+
+
+
+del expr
+del l
+del override
+
+
+
+class FunctionEvaluator:
+ """
+ Handles delayed evaluation of a #define function call.
+ """
+ def __init__(self, name, args, expansion):
+ """
+ Squirrels away the arguments and expansion value of a #define
+ macro function for later evaluation when we must actually expand
+ a value that uses it.
+ """
+ self.name = name
+ self.args = function_arg_separator.split(args)
+ try:
+ expansion = string.split(expansion, '##')
+ except (AttributeError, TypeError):
+ # Python 1.5 throws TypeError if "expansion" isn't a string,
+ # later versions throw AttributeError.
+ pass
+ self.expansion = expansion
+ def __call__(self, *values):
+ """
+ Evaluates the expansion of a #define macro function called
+ with the specified values.
+ """
+ if len(self.args) != len(values):
+ raise ValueError, "Incorrect number of arguments to `%s'" % self.name
+ # Create a dictionary that maps the macro arguments to the
+ # corresponding values in this "call." We'll use this when we
+ # eval() the expansion so that arguments will get expanded to
+ # the right values.
+ locals = {}
+ for k, v in zip(self.args, values):
+ locals[k] = v
+
+ parts = []
+ for s in self.expansion:
+ if not s in self.args:
+ s = repr(s)
+ parts.append(s)
+ statement = string.join(parts, ' + ')
+
+ return eval(statement, globals(), locals)
+
+
+
+# Find line continuations.
+line_continuations = re.compile('\\\\\r?\n')
+
+# Search for a "function call" macro on an expansion. Returns the
+# two-tuple of the "function" name itself, and a string containing the
+# arguments within the call parentheses.
+function_name = re.compile('(\S+)\(([^)]*)\)')
+
+# Split a string containing comma-separated function call arguments into
+# the separate arguments.
+function_arg_separator = re.compile(',\s*')
+
+
+
+class PreProcessor:
+ """
+ The main workhorse class for handling C pre-processing.
+ """
+ def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0):
+ global Table
+
+ cpppath = tuple(cpppath)
+
+ self.searchpath = {
+ '"' : (current,) + cpppath,
+ '<' : cpppath + (current,),
+ }
+
+ # Initialize our C preprocessor namespace for tracking the
+ # values of #defined keywords. We use this namespace to look
+ # for keywords on #ifdef/#ifndef lines, and to eval() the
+ # expressions on #if/#elif lines (after massaging them from C to
+ # Python).
+ self.cpp_namespace = dict.copy()
+ self.cpp_namespace['__dict__'] = self.cpp_namespace
+
+ if all:
+ self.do_include = self.all_include
+
+ # For efficiency, a dispatch table maps each C preprocessor
+ # directive (#if, #define, etc.) to the method that should be
+ # called when we see it. We accomodate state changes (#if,
+ # #ifdef, #ifndef) by pushing the current dispatch table on a
+ # stack and changing what method gets called for each relevant
+ # directive we might see next at this level (#else, #elif).
+ # #endif will simply pop the stack.
+ d = {
+ 'scons_current_file' : self.scons_current_file
+ }
+ for op in Table.keys():
+ d[op] = getattr(self, 'do_' + op)
+ self.default_table = d
+
+ # Controlling methods.
+
+ def tupleize(self, contents):
+ """
+ Turns the contents of a file into a list of easily-processed
+ tuples describing the CPP lines in the file.
+
+ The first element of each tuple is the line's preprocessor
+ directive (#if, #include, #define, etc., minus the initial '#').
+ The remaining elements are specific to the type of directive, as
+ pulled apart by the regular expression.
+ """
+ global CPP_Expression, Table
+ contents = line_continuations.sub('', contents)
+ cpp_tuples = CPP_Expression.findall(contents)
+ return map(lambda m, t=Table:
+ (m[0],) + t[m[0]].match(m[1]).groups(),
+ cpp_tuples)
+
+ def __call__(self, file):
+ """
+ Pre-processes a file.
+
+ This is the main public entry point.
+ """
+ self.current_file = file
+ return self.process_contents(self.read_file(file), file)
+
+ def process_contents(self, contents, fname=None):
+ """
+ Pre-processes a file contents.
+
+ This is the main internal entry point.
+ """
+ self.stack = []
+ self.dispatch_table = self.default_table.copy()
+ self.current_file = fname
+ self.tuples = self.tupleize(contents)
+
+ self.initialize_result(fname)
+ while self.tuples:
+ t = self.tuples.pop(0)
+ # Uncomment to see the list of tuples being processed (e.g.,
+ # to validate the CPP lines are being translated correctly).
+ #print t
+ self.dispatch_table[t[0]](t)
+ return self.finalize_result(fname)
+
+ # Dispatch table stack manipulation methods.
+
+ def save(self):
+ """
+ Pushes the current dispatch table on the stack and re-initializes
+ the current dispatch table to the default.
+ """
+ self.stack.append(self.dispatch_table)
+ self.dispatch_table = self.default_table.copy()
+
+ def restore(self):
+ """
+ Pops the previous dispatch table off the stack and makes it the
+ current one.
+ """
+ try: self.dispatch_table = self.stack.pop()
+ except IndexError: pass
+
+ # Utility methods.
+
+ def do_nothing(self, t):
+ """
+ Null method for when we explicitly want the action for a
+ specific preprocessor directive to do nothing.
+ """
+ pass
+
+ def scons_current_file(self, t):
+ self.current_file = t[1]
+
+ def eval_expression(self, t):
+ """
+ Evaluates a C preprocessor expression.
+
+ This is done by converting it to a Python equivalent and
+ eval()ing it in the C preprocessor namespace we use to
+ track #define values.
+ """
+ t = CPP_to_Python(string.join(t[1:]))
+ try: return eval(t, self.cpp_namespace)
+ except (NameError, TypeError): return 0
+
+ def initialize_result(self, fname):
+ self.result = [fname]
+
+ def finalize_result(self, fname):
+ return self.result[1:]
+
+ def find_include_file(self, t):
+ """
+ Finds the #include file for a given preprocessor tuple.
+ """
+ fname = t[2]
+ for d in self.searchpath[t[1]]:
+ if d == os.curdir:
+ f = fname
+ else:
+ f = os.path.join(d, fname)
+ if os.path.isfile(f):
+ return f
+ return None
+
+ def read_file(self, file):
+ return open(file).read()
+
+ # Start and stop processing include lines.
+
+ def start_handling_includes(self, t=None):
+ """
+ Causes the PreProcessor object to start processing #import,
+ #include and #include_next lines.
+
+ This method will be called when a #if, #ifdef, #ifndef or #elif
+ evaluates True, or when we reach the #else in a #if, #ifdef,
+ #ifndef or #elif block where a condition already evaluated
+ False.
+
+ """
+ d = self.dispatch_table
+ d['import'] = self.do_import
+ d['include'] = self.do_include
+ d['include_next'] = self.do_include
+
+ def stop_handling_includes(self, t=None):
+ """
+ Causes the PreProcessor object to stop processing #import,
+ #include and #include_next lines.
+
+ This method will be called when a #if, #ifdef, #ifndef or #elif
+ evaluates False, or when we reach the #else in a #if, #ifdef,
+ #ifndef or #elif block where a condition already evaluated True.
+ """
+ d = self.dispatch_table
+ d['import'] = self.do_nothing
+ d['include'] = self.do_nothing
+ d['include_next'] = self.do_nothing
+
+ # Default methods for handling all of the preprocessor directives.
+ # (Note that what actually gets called for a given directive at any
+ # point in time is really controlled by the dispatch_table.)
+
+ def _do_if_else_condition(self, condition):
+ """
+ Common logic for evaluating the conditions on #if, #ifdef and
+ #ifndef lines.
+ """
+ self.save()
+ d = self.dispatch_table
+ if condition:
+ self.start_handling_includes()
+ d['elif'] = self.stop_handling_includes
+ d['else'] = self.stop_handling_includes
+ else:
+ self.stop_handling_includes()
+ d['elif'] = self.do_elif
+ d['else'] = self.start_handling_includes
+
+ def do_ifdef(self, t):
+ """
+ Default handling of a #ifdef line.
+ """
+ self._do_if_else_condition(self.cpp_namespace.has_key(t[1]))
+
+ def do_ifndef(self, t):
+ """
+ Default handling of a #ifndef line.
+ """
+ self._do_if_else_condition(not self.cpp_namespace.has_key(t[1]))
+
+ def do_if(self, t):
+ """
+ Default handling of a #if line.
+ """
+ self._do_if_else_condition(self.eval_expression(t))
+
+ def do_elif(self, t):
+ """
+ Default handling of a #elif line.
+ """
+ d = self.dispatch_table
+ if self.eval_expression(t):
+ self.start_handling_includes()
+ d['elif'] = self.stop_handling_includes
+ d['else'] = self.stop_handling_includes
+
+ def do_else(self, t):
+ """
+ Default handling of a #else line.
+ """
+ pass
+
+ def do_endif(self, t):
+ """
+ Default handling of a #endif line.
+ """
+ self.restore()
+
+ def do_define(self, t):
+ """
+ Default handling of a #define line.
+ """
+ _, name, args, expansion = t
+ try:
+ expansion = int(expansion)
+ except (TypeError, ValueError):
+ pass
+ if args:
+ evaluator = FunctionEvaluator(name, args[1:-1], expansion)
+ self.cpp_namespace[name] = evaluator
+ else:
+ self.cpp_namespace[name] = expansion
+
+ def do_undef(self, t):
+ """
+ Default handling of a #undef line.
+ """
+ try: del self.cpp_namespace[t[1]]
+ except KeyError: pass
+
+ def do_import(self, t):
+ """
+ Default handling of a #import line.
+ """
+ # XXX finish this -- maybe borrow/share logic from do_include()...?
+ pass
+
+ def do_include(self, t):
+ """
+ Default handling of a #include line.
+ """
+ t = self.resolve_include(t)
+ include_file = self.find_include_file(t)
+ if include_file:
+ #print "include_file =", include_file
+ self.result.append(include_file)
+ contents = self.read_file(include_file)
+ new_tuples = [('scons_current_file', include_file)] + \
+ self.tupleize(contents) + \
+ [('scons_current_file', self.current_file)]
+ self.tuples[:] = new_tuples + self.tuples
+
+ # Date: Tue, 22 Nov 2005 20:26:09 -0500
+ # From: Stefan Seefeld <seefeld@sympatico.ca>
+ #
+ # By the way, #include_next is not the same as #include. The difference
+ # being that #include_next starts its search in the path following the
+ # path that let to the including file. In other words, if your system
+ # include paths are ['/foo', '/bar'], and you are looking at a header
+ # '/foo/baz.h', it might issue an '#include_next <baz.h>' which would
+ # correctly resolve to '/bar/baz.h' (if that exists), but *not* see
+ # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html
+ # for more reasoning.
+ #
+ # I have no idea in what context 'import' might be used.
+
+ # XXX is #include_next really the same as #include ?
+ do_include_next = do_include
+
+ # Utility methods for handling resolution of include files.
+
+ def resolve_include(self, t):
+ """Resolve a tuple-ized #include line.
+
+ This handles recursive expansion of values without "" or <>
+ surrounding the name until an initial " or < is found, to handle
+ #include FILE
+ where FILE is a #define somewhere else.
+ """
+ s = t[1]
+ while not s[0] in '<"':
+ #print "s =", s
+ try:
+ s = self.cpp_namespace[s]
+ except KeyError:
+ m = function_name.search(s)
+ s = self.cpp_namespace[m.group(1)]
+ if callable(s):
+ args = function_arg_separator.split(m.group(2))
+ s = apply(s, args)
+ if not s:
+ return None
+ return (t[0], s[0], s[1:-1])
+
+ def all_include(self, t):
+ """
+ """
+ self.result.append(self.resolve_include(t))
+
+class DumbPreProcessor(PreProcessor):
+ """A preprocessor that ignores all #if/#elif/#else/#endif directives
+ and just reports back *all* of the #include files (like the classic
+ SCons scanner did).
+
+ This is functionally equivalent to using a regular expression to
+ find all of the #include lines, only slower. It exists mainly as
+ an example of how the main PreProcessor class can be sub-classed
+ to tailor its behavior.
+ """
+ def __init__(self, *args, **kw):
+ apply(PreProcessor.__init__, (self,)+args, kw)
+ d = self.default_table
+ for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']:
+ d[func] = d[func] = self.do_nothing
+
+del __revision__
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/cppTests.py b/src/engine/SCons/cppTests.py
new file mode 100644
index 0000000..5bd08f8
--- /dev/null
+++ b/src/engine/SCons/cppTests.py
@@ -0,0 +1,715 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/cppTests.py 4577 2009/12/27 19:44:43 scons"
+
+import string
+import sys
+import unittest
+
+import cpp
+
+
+
+basic_input = """
+#include "file1-yes"
+#include <file2-yes>
+"""
+
+
+substitution_input = """
+#define FILE3 "file3-yes"
+#define FILE4 <file4-yes>
+
+#include FILE3
+#include FILE4
+
+#define XXX_FILE5 YYY_FILE5
+#define YYY_FILE5 ZZZ_FILE5
+#define ZZZ_FILE5 FILE5
+
+#define FILE5 "file5-yes"
+#define FILE6 <file6-yes>
+
+#define XXX_FILE6 YYY_FILE6
+#define YYY_FILE6 ZZZ_FILE6
+#define ZZZ_FILE6 FILE6
+
+#include XXX_FILE5
+#include XXX_FILE6
+"""
+
+
+ifdef_input = """
+#define DEFINED 0
+
+#ifdef DEFINED
+#include "file7-yes"
+#else
+#include "file7-no"
+#endif
+
+#ifdef NOT_DEFINED
+#include <file8-no>
+#else
+#include <file8-yes>
+#endif
+"""
+
+
+if_boolean_input = """
+#define ZERO 0
+#define ONE 1
+
+#if ZERO
+#include "file9-no"
+#else
+#include "file9-yes"
+#endif
+
+#if ONE
+#include <file10-yes>
+#else
+#include <file10-no>
+#endif
+
+#if ZERO
+#include "file11-no-1"
+#elif ZERO
+#include "file11-no-2"
+#else
+#include "file11-yes"
+#endif
+
+#if ZERO
+#include <file12-no-1>
+#elif ONE
+#include <file12-yes>
+#else
+#include <file12-no-2>
+#endif
+
+#if ONE
+#include "file13-yes"
+#elif ZERO
+#include "file13-no-1"
+#else
+#include "file13-no-2"
+#endif
+
+#if ONE
+#include <file14-yes>
+#elif ONE
+#include <file14-no-1>
+#else
+#include <file14-no-2>
+#endif
+"""
+
+
+if_defined_input = """
+#define DEFINED 0
+
+#if defined(DEFINED)
+#include "file15-yes"
+#endif
+
+#if ! defined(DEFINED)
+#include <file16-no>
+#else
+#include <file16-yes>
+#endif
+
+#if defined DEFINED
+#include "file17-yes"
+#endif
+
+#if ! defined DEFINED
+#include <file18-no>
+#else
+#include <file18-yes>
+#endif
+"""
+
+
+expression_input = """
+#define ZERO 0
+#define ONE 1
+
+#if ZERO && ZERO
+#include "file19-no"
+#else
+#include "file19-yes"
+#endif
+
+#if ZERO && ONE
+#include <file20-no>
+#else
+#include <file20-yes>
+#endif
+
+#if ONE && ZERO
+#include "file21-no"
+#else
+#include "file21-yes"
+#endif
+
+#if ONE && ONE
+#include <file22-yes>
+#else
+#include <file22-no>
+#endif
+
+#if ZERO || ZERO
+#include "file23-no"
+#else
+#include "file23-yes"
+#endif
+
+#if ZERO || ONE
+#include <file24-yes>
+#else
+#include <file24-no>
+#endif
+
+#if ONE || ZERO
+#include "file25-yes"
+#else
+#include "file25-no"
+#endif
+
+#if ONE || ONE
+#include <file26-yes>
+#else
+#include <file26-no>
+#endif
+
+#if ONE == ONE
+#include "file27-yes"
+#else
+#include "file27-no"
+#endif
+
+#if ONE != ONE
+#include <file28-no>
+#else
+#include <file28-yes>
+#endif
+
+#if ! (ONE == ONE)
+#include "file29-no"
+#else
+#include "file29-yes"
+#endif
+
+#if ! (ONE != ONE)
+#include <file30-yes>
+#else
+#include <file30-no>
+#endif
+"""
+
+
+undef_input = """
+#define UNDEFINE 0
+
+#ifdef UNDEFINE
+#include "file31-yes"
+#else
+#include "file31-no"
+#endif
+
+#undef UNDEFINE
+
+#ifdef UNDEFINE
+#include <file32-no>
+#else
+#include <file32-yes>
+#endif
+"""
+
+
+macro_function_input = """
+#define ZERO 0
+#define ONE 1
+
+#define FUNC33(x) "file33-yes"
+#define FUNC34(x) <file34-yes>
+
+#include FUNC33(ZERO)
+#include FUNC34(ZERO)
+
+#define FILE35 "file35-yes"
+#define FILE36 <file36-yes>
+
+#define FUNC35(x, y) FILE35
+#define FUNC36(x, y) FILE36
+
+#include FUNC35(ZERO, ONE)
+#include FUNC36(ZERO, ONE)
+
+#define FILE37 "file37-yes"
+#define FILE38 <file38-yes>
+
+#define FUNC37a(x, y) FILE37
+#define FUNC38a(x, y) FILE38
+
+#define FUNC37b(x, y) FUNC37a(x, y)
+#define FUNC38b(x, y) FUNC38a(x, y)
+
+#define FUNC37c(x, y) FUNC37b(x, y)
+#define FUNC38c(x, y) FUNC38b(x, y)
+
+#include FUNC37c(ZERO, ONE)
+#include FUNC38c(ZERO, ONE)
+
+#define FILE39 "file39-yes"
+#define FILE40 <file40-yes>
+
+#define FUNC39a(x0, y0) FILE39
+#define FUNC40a(x0, y0) FILE40
+
+#define FUNC39b(x1, y2) FUNC39a(x1, y1)
+#define FUNC40b(x1, y2) FUNC40a(x1, y1)
+
+#define FUNC39c(x2, y2) FUNC39b(x2, y2)
+#define FUNC40c(x2, y2) FUNC40b(x2, y2)
+
+#include FUNC39c(ZERO, ONE)
+#include FUNC40c(ZERO, ONE)
+
+/* Make sure we don't die if the expansion isn't a string. */
+#define FUNC_INTEGER(x) 1
+
+/* Make sure one-character names are recognized. */
+#define _(x) translate(x)
+#undef _
+"""
+
+
+token_pasting_input = """
+#define PASTE_QUOTE(q, name) q##name##-yes##q
+#define PASTE_ANGLE(name) <##name##-yes>
+
+#define FUNC41 PASTE_QUOTE(", file41)
+#define FUNC42 PASTE_ANGLE(file42)
+
+#include FUNC41
+#include FUNC42
+"""
+
+
+no_space_input = """
+#include<file43-yes>
+#include"file44-yes"
+"""
+
+
+
+# pp_class = PreProcessor
+# #pp_class = DumbPreProcessor
+
+# pp = pp_class(current = ".",
+# cpppath = ['/usr/include'],
+# print_all = 1)
+# #pp(open(sys.argv[1]).read())
+# pp(input)
+
+
+class cppTestCase(unittest.TestCase):
+ def setUp(self):
+ self.cpp = self.cpp_class(current = ".",
+ cpppath = ['/usr/include'])
+
+ def test_basic(self):
+ """Test basic #include scanning"""
+ expect = self.basic_expect
+ result = self.cpp.process_contents(basic_input)
+ assert expect == result, (expect, result)
+
+ def test_substitution(self):
+ """Test substitution of #include files using CPP variables"""
+ expect = self.substitution_expect
+ result = self.cpp.process_contents(substitution_input)
+ assert expect == result, (expect, result)
+
+ def test_ifdef(self):
+ """Test basic #ifdef processing"""
+ expect = self.ifdef_expect
+ result = self.cpp.process_contents(ifdef_input)
+ assert expect == result, (expect, result)
+
+ def test_if_boolean(self):
+ """Test #if with Boolean values"""
+ expect = self.if_boolean_expect
+ result = self.cpp.process_contents(if_boolean_input)
+ assert expect == result, (expect, result)
+
+ def test_if_defined(self):
+ """Test #if defined() idioms"""
+ expect = self.if_defined_expect
+ result = self.cpp.process_contents(if_defined_input)
+ assert expect == result, (expect, result)
+
+ def test_expression(self):
+ """Test #if with arithmetic expressions"""
+ expect = self.expression_expect
+ result = self.cpp.process_contents(expression_input)
+ assert expect == result, (expect, result)
+
+ def test_undef(self):
+ """Test #undef handling"""
+ expect = self.undef_expect
+ result = self.cpp.process_contents(undef_input)
+ assert expect == result, (expect, result)
+
+ def test_macro_function(self):
+ """Test using macro functions to express file names"""
+ expect = self.macro_function_expect
+ result = self.cpp.process_contents(macro_function_input)
+ assert expect == result, (expect, result)
+
+ def test_token_pasting(self):
+ """Test token-pasting to construct file names"""
+ expect = self.token_pasting_expect
+ result = self.cpp.process_contents(token_pasting_input)
+ assert expect == result, (expect, result)
+
+ def test_no_space(self):
+ """Test no space between #include and the quote"""
+ expect = self.no_space_expect
+ result = self.cpp.process_contents(no_space_input)
+ assert expect == result, (expect, result)
+
+class cppAllTestCase(cppTestCase):
+ def setUp(self):
+ self.cpp = self.cpp_class(current = ".",
+ cpppath = ['/usr/include'],
+ all=1)
+
+class PreProcessorTestCase(cppAllTestCase):
+ cpp_class = cpp.PreProcessor
+
+ basic_expect = [
+ ('include', '"', 'file1-yes'),
+ ('include', '<', 'file2-yes'),
+ ]
+
+ substitution_expect = [
+ ('include', '"', 'file3-yes'),
+ ('include', '<', 'file4-yes'),
+ ('include', '"', 'file5-yes'),
+ ('include', '<', 'file6-yes'),
+ ]
+
+ ifdef_expect = [
+ ('include', '"', 'file7-yes'),
+ ('include', '<', 'file8-yes'),
+ ]
+
+ if_boolean_expect = [
+ ('include', '"', 'file9-yes'),
+ ('include', '<', 'file10-yes'),
+ ('include', '"', 'file11-yes'),
+ ('include', '<', 'file12-yes'),
+ ('include', '"', 'file13-yes'),
+ ('include', '<', 'file14-yes'),
+ ]
+
+ if_defined_expect = [
+ ('include', '"', 'file15-yes'),
+ ('include', '<', 'file16-yes'),
+ ('include', '"', 'file17-yes'),
+ ('include', '<', 'file18-yes'),
+ ]
+
+ expression_expect = [
+ ('include', '"', 'file19-yes'),
+ ('include', '<', 'file20-yes'),
+ ('include', '"', 'file21-yes'),
+ ('include', '<', 'file22-yes'),
+ ('include', '"', 'file23-yes'),
+ ('include', '<', 'file24-yes'),
+ ('include', '"', 'file25-yes'),
+ ('include', '<', 'file26-yes'),
+ ('include', '"', 'file27-yes'),
+ ('include', '<', 'file28-yes'),
+ ('include', '"', 'file29-yes'),
+ ('include', '<', 'file30-yes'),
+ ]
+
+ undef_expect = [
+ ('include', '"', 'file31-yes'),
+ ('include', '<', 'file32-yes'),
+ ]
+
+ macro_function_expect = [
+ ('include', '"', 'file33-yes'),
+ ('include', '<', 'file34-yes'),
+ ('include', '"', 'file35-yes'),
+ ('include', '<', 'file36-yes'),
+ ('include', '"', 'file37-yes'),
+ ('include', '<', 'file38-yes'),
+ ('include', '"', 'file39-yes'),
+ ('include', '<', 'file40-yes'),
+ ]
+
+ token_pasting_expect = [
+ ('include', '"', 'file41-yes'),
+ ('include', '<', 'file42-yes'),
+ ]
+
+ no_space_expect = [
+ ('include', '<', 'file43-yes'),
+ ('include', '"', 'file44-yes'),
+ ]
+
+class DumbPreProcessorTestCase(cppAllTestCase):
+ cpp_class = cpp.DumbPreProcessor
+
+ basic_expect = [
+ ('include', '"', 'file1-yes'),
+ ('include', '<', 'file2-yes'),
+ ]
+
+ substitution_expect = [
+ ('include', '"', 'file3-yes'),
+ ('include', '<', 'file4-yes'),
+ ('include', '"', 'file5-yes'),
+ ('include', '<', 'file6-yes'),
+ ]
+
+ ifdef_expect = [
+ ('include', '"', 'file7-yes'),
+ ('include', '"', 'file7-no'),
+ ('include', '<', 'file8-no'),
+ ('include', '<', 'file8-yes'),
+ ]
+
+ if_boolean_expect = [
+ ('include', '"', 'file9-no'),
+ ('include', '"', 'file9-yes'),
+ ('include', '<', 'file10-yes'),
+ ('include', '<', 'file10-no'),
+ ('include', '"', 'file11-no-1'),
+ ('include', '"', 'file11-no-2'),
+ ('include', '"', 'file11-yes'),
+ ('include', '<', 'file12-no-1'),
+ ('include', '<', 'file12-yes'),
+ ('include', '<', 'file12-no-2'),
+ ('include', '"', 'file13-yes'),
+ ('include', '"', 'file13-no-1'),
+ ('include', '"', 'file13-no-2'),
+ ('include', '<', 'file14-yes'),
+ ('include', '<', 'file14-no-1'),
+ ('include', '<', 'file14-no-2'),
+ ]
+
+ if_defined_expect = [
+ ('include', '"', 'file15-yes'),
+ ('include', '<', 'file16-no'),
+ ('include', '<', 'file16-yes'),
+ ('include', '"', 'file17-yes'),
+ ('include', '<', 'file18-no'),
+ ('include', '<', 'file18-yes'),
+ ]
+
+ expression_expect = [
+ ('include', '"', 'file19-no'),
+ ('include', '"', 'file19-yes'),
+ ('include', '<', 'file20-no'),
+ ('include', '<', 'file20-yes'),
+ ('include', '"', 'file21-no'),
+ ('include', '"', 'file21-yes'),
+ ('include', '<', 'file22-yes'),
+ ('include', '<', 'file22-no'),
+ ('include', '"', 'file23-no'),
+ ('include', '"', 'file23-yes'),
+ ('include', '<', 'file24-yes'),
+ ('include', '<', 'file24-no'),
+ ('include', '"', 'file25-yes'),
+ ('include', '"', 'file25-no'),
+ ('include', '<', 'file26-yes'),
+ ('include', '<', 'file26-no'),
+ ('include', '"', 'file27-yes'),
+ ('include', '"', 'file27-no'),
+ ('include', '<', 'file28-no'),
+ ('include', '<', 'file28-yes'),
+ ('include', '"', 'file29-no'),
+ ('include', '"', 'file29-yes'),
+ ('include', '<', 'file30-yes'),
+ ('include', '<', 'file30-no'),
+ ]
+
+ undef_expect = [
+ ('include', '"', 'file31-yes'),
+ ('include', '"', 'file31-no'),
+ ('include', '<', 'file32-no'),
+ ('include', '<', 'file32-yes'),
+ ]
+
+ macro_function_expect = [
+ ('include', '"', 'file33-yes'),
+ ('include', '<', 'file34-yes'),
+ ('include', '"', 'file35-yes'),
+ ('include', '<', 'file36-yes'),
+ ('include', '"', 'file37-yes'),
+ ('include', '<', 'file38-yes'),
+ ('include', '"', 'file39-yes'),
+ ('include', '<', 'file40-yes'),
+ ]
+
+ token_pasting_expect = [
+ ('include', '"', 'file41-yes'),
+ ('include', '<', 'file42-yes'),
+ ]
+
+ no_space_expect = [
+ ('include', '<', 'file43-yes'),
+ ('include', '"', 'file44-yes'),
+ ]
+
+
+
+import os
+import re
+import shutil
+import tempfile
+
+tempfile.template = 'cppTests.'
+if os.name in ('posix', 'nt'):
+ tempfile.template = 'cppTests.' + str(os.getpid()) + '.'
+else:
+ tempfile.template = 'cppTests.'
+
+_Cleanup = []
+
+def _clean():
+ for dir in _Cleanup:
+ if os.path.exists(dir):
+ shutil.rmtree(dir)
+
+sys.exitfunc = _clean
+
+class fileTestCase(unittest.TestCase):
+ cpp_class = cpp.DumbPreProcessor
+
+ def setUp(self):
+ try:
+ path = tempfile.mktemp(prefix=tempfile.template)
+ except TypeError:
+ # The tempfile.mktemp() function in earlier versions of Python
+ # has no prefix argument, but uses the tempfile.template
+ # value that we set above.
+ path = tempfile.mktemp()
+ _Cleanup.append(path)
+ os.mkdir(path)
+ self.tempdir = path
+ self.orig_cwd = os.getcwd()
+ os.chdir(path)
+
+ def tearDown(self):
+ os.chdir(self.orig_cwd)
+ shutil.rmtree(self.tempdir)
+ _Cleanup.remove(self.tempdir)
+
+ def strip_initial_spaces(self, s):
+ #lines = s.split('\n')
+ lines = string.split(s, '\n')
+ spaces = re.match(' *', lines[0]).group(0)
+ def strip_spaces(l, spaces=spaces):
+ #if l.startswith(spaces):
+ if l[:len(spaces)] == spaces:
+ l = l[len(spaces):]
+ return l
+ #return '\n'.join([ strip_spaces(l) for l in lines ])
+ return string.join(map(strip_spaces, lines), '\n')
+
+ def write(self, file, contents):
+ open(file, 'w').write(self.strip_initial_spaces(contents))
+
+ def test_basic(self):
+ """Test basic file inclusion"""
+ self.write('f1.h', """\
+ #include "f2.h"
+ """)
+ self.write('f2.h', """\
+ #include <f3.h>
+ """)
+ self.write('f3.h', """\
+ """)
+ p = cpp.DumbPreProcessor(current = os.curdir,
+ cpppath = [os.curdir])
+ result = p('f1.h')
+ assert result == ['f2.h', 'f3.h'], result
+
+ def test_current_file(self):
+ """Test use of the .current_file attribute"""
+ self.write('f1.h', """\
+ #include <f2.h>
+ """)
+ self.write('f2.h', """\
+ #include "f3.h"
+ """)
+ self.write('f3.h', """\
+ """)
+ class MyPreProcessor(cpp.DumbPreProcessor):
+ def __init__(self, *args, **kw):
+ apply(cpp.DumbPreProcessor.__init__, (self,) + args, kw)
+ self.files = []
+ def __call__(self, file):
+ self.files.append(file)
+ return cpp.DumbPreProcessor.__call__(self, file)
+ def scons_current_file(self, t):
+ r = cpp.DumbPreProcessor.scons_current_file(self, t)
+ self.files.append(self.current_file)
+ return r
+ p = MyPreProcessor(current = os.curdir, cpppath = [os.curdir])
+ result = p('f1.h')
+ assert result == ['f2.h', 'f3.h'], result
+ assert p.files == ['f1.h', 'f2.h', 'f3.h', 'f2.h', 'f1.h'], p.files
+
+
+
+if __name__ == '__main__':
+ suite = unittest.TestSuite()
+ tclasses = [ PreProcessorTestCase,
+ DumbPreProcessorTestCase,
+ fileTestCase,
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ try:
+ names = list(set(names))
+ except NameError:
+ pass
+ names.sort()
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/dblite.py b/src/engine/SCons/dblite.py
new file mode 100644
index 0000000..bcb2aa0
--- /dev/null
+++ b/src/engine/SCons/dblite.py
@@ -0,0 +1,248 @@
+# dblite.py module contributed by Ralf W. Grosse-Kunstleve.
+# Extended for Unicode by Steven Knight.
+
+import cPickle
+import time
+import shutil
+import os
+import types
+import __builtin__
+
+keep_all_files = 00000
+ignore_corrupt_dbfiles = 0
+
+def corruption_warning(filename):
+ print "Warning: Discarding corrupt database:", filename
+
+if hasattr(types, 'UnicodeType'):
+ def is_string(s):
+ t = type(s)
+ return t is types.StringType or t is types.UnicodeType
+else:
+ def is_string(s):
+ return type(s) is types.StringType
+
+try:
+ unicode('a')
+except NameError:
+ def unicode(s): return s
+
+dblite_suffix = '.dblite'
+tmp_suffix = '.tmp'
+
+class dblite:
+
+ # Squirrel away references to the functions in various modules
+ # that we'll use when our __del__() method calls our sync() method
+ # during shutdown. We might get destroyed when Python is in the midst
+ # of tearing down the different modules we import in an essentially
+ # arbitrary order, and some of the various modules's global attributes
+ # may already be wiped out from under us.
+ #
+ # See the discussion at:
+ # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html
+
+ _open = __builtin__.open
+ _cPickle_dump = cPickle.dump
+ _os_chmod = os.chmod
+ try:
+ _os_chown = os.chown
+ except AttributeError:
+ _os_chown = None
+ _os_rename = os.rename
+ _os_unlink = os.unlink
+ _shutil_copyfile = shutil.copyfile
+ _time_time = time.time
+
+ def __init__(self, file_base_name, flag, mode):
+ assert flag in (None, "r", "w", "c", "n")
+ if (flag is None): flag = "r"
+ base, ext = os.path.splitext(file_base_name)
+ if ext == dblite_suffix:
+ # There's already a suffix on the file name, don't add one.
+ self._file_name = file_base_name
+ self._tmp_name = base + tmp_suffix
+ else:
+ self._file_name = file_base_name + dblite_suffix
+ self._tmp_name = file_base_name + tmp_suffix
+ self._flag = flag
+ self._mode = mode
+ self._dict = {}
+ self._needs_sync = 00000
+ if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0):
+ # running as root; chown back to current owner/group when done
+ try:
+ statinfo = os.stat(self._file_name)
+ self._chown_to = statinfo.st_uid
+ self._chgrp_to = statinfo.st_gid
+ except OSError, e:
+ # db file doesn't exist yet.
+ # Check os.environ for SUDO_UID, use if set
+ self._chown_to = int(os.environ.get('SUDO_UID', -1))
+ self._chgrp_to = int(os.environ.get('SUDO_GID', -1))
+ else:
+ self._chown_to = -1 # don't chown
+ self._chgrp_to = -1 # don't chgrp
+ if (self._flag == "n"):
+ self._open(self._file_name, "wb", self._mode)
+ else:
+ try:
+ f = self._open(self._file_name, "rb")
+ except IOError, e:
+ if (self._flag != "c"):
+ raise e
+ self._open(self._file_name, "wb", self._mode)
+ else:
+ p = f.read()
+ if (len(p) > 0):
+ try:
+ self._dict = cPickle.loads(p)
+ except (cPickle.UnpicklingError, EOFError):
+ if (ignore_corrupt_dbfiles == 0): raise
+ if (ignore_corrupt_dbfiles == 1):
+ corruption_warning(self._file_name)
+
+ def __del__(self):
+ if (self._needs_sync):
+ self.sync()
+
+ def sync(self):
+ self._check_writable()
+ f = self._open(self._tmp_name, "wb", self._mode)
+ self._cPickle_dump(self._dict, f, 1)
+ f.close()
+ # Windows doesn't allow renaming if the file exists, so unlink
+ # it first, chmod'ing it to make sure we can do so. On UNIX, we
+ # may not be able to chmod the file if it's owned by someone else
+ # (e.g. from a previous run as root). We should still be able to
+ # unlink() the file if the directory's writable, though, so ignore
+ # any OSError exception thrown by the chmod() call.
+ try: self._os_chmod(self._file_name, 0777)
+ except OSError: pass
+ self._os_unlink(self._file_name)
+ self._os_rename(self._tmp_name, self._file_name)
+ if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1
+ try:
+ self._os_chown(self._file_name, self._chown_to, self._chgrp_to)
+ except OSError:
+ pass
+ self._needs_sync = 00000
+ if (keep_all_files):
+ self._shutil_copyfile(
+ self._file_name,
+ self._file_name + "_" + str(int(self._time_time())))
+
+ def _check_writable(self):
+ if (self._flag == "r"):
+ raise IOError("Read-only database: %s" % self._file_name)
+
+ def __getitem__(self, key):
+ return self._dict[key]
+
+ def __setitem__(self, key, value):
+ self._check_writable()
+ if (not is_string(key)):
+ raise TypeError, "key `%s' must be a string but is %s" % (key, type(key))
+ if (not is_string(value)):
+ raise TypeError, "value `%s' must be a string but is %s" % (value, type(value))
+ self._dict[key] = value
+ self._needs_sync = 0001
+
+ def keys(self):
+ return self._dict.keys()
+
+ def has_key(self, key):
+ return key in self._dict
+
+ def __contains__(self, key):
+ return key in self._dict
+
+ def iterkeys(self):
+ return self._dict.iterkeys()
+
+ __iter__ = iterkeys
+
+ def __len__(self):
+ return len(self._dict)
+
+def open(file, flag=None, mode=0666):
+ return dblite(file, flag, mode)
+
+def _exercise():
+ db = open("tmp", "n")
+ assert len(db) == 0
+ db["foo"] = "bar"
+ assert db["foo"] == "bar"
+ db[unicode("ufoo")] = unicode("ubar")
+ assert db[unicode("ufoo")] == unicode("ubar")
+ db.sync()
+ db = open("tmp", "c")
+ assert len(db) == 2, len(db)
+ assert db["foo"] == "bar"
+ db["bar"] = "foo"
+ assert db["bar"] == "foo"
+ db[unicode("ubar")] = unicode("ufoo")
+ assert db[unicode("ubar")] == unicode("ufoo")
+ db.sync()
+ db = open("tmp", "r")
+ assert len(db) == 4, len(db)
+ assert db["foo"] == "bar"
+ assert db["bar"] == "foo"
+ assert db[unicode("ufoo")] == unicode("ubar")
+ assert db[unicode("ubar")] == unicode("ufoo")
+ try:
+ db.sync()
+ except IOError, e:
+ assert str(e) == "Read-only database: tmp.dblite"
+ else:
+ raise RuntimeError, "IOError expected."
+ db = open("tmp", "w")
+ assert len(db) == 4
+ db["ping"] = "pong"
+ db.sync()
+ try:
+ db[(1,2)] = "tuple"
+ except TypeError, e:
+ assert str(e) == "key `(1, 2)' must be a string but is <type 'tuple'>", str(e)
+ else:
+ raise RuntimeError, "TypeError exception expected"
+ try:
+ db["list"] = [1,2]
+ except TypeError, e:
+ assert str(e) == "value `[1, 2]' must be a string but is <type 'list'>", str(e)
+ else:
+ raise RuntimeError, "TypeError exception expected"
+ db = open("tmp", "r")
+ assert len(db) == 5
+ db = open("tmp", "n")
+ assert len(db) == 0
+ _open("tmp.dblite", "w")
+ db = open("tmp", "r")
+ _open("tmp.dblite", "w").write("x")
+ try:
+ db = open("tmp", "r")
+ except cPickle.UnpicklingError:
+ pass
+ else:
+ raise RuntimeError, "cPickle exception expected."
+ global ignore_corrupt_dbfiles
+ ignore_corrupt_dbfiles = 2
+ db = open("tmp", "r")
+ assert len(db) == 0
+ os.unlink("tmp.dblite")
+ try:
+ db = open("tmp", "w")
+ except IOError, e:
+ assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e)
+ else:
+ raise RuntimeError, "IOError expected."
+ print "OK"
+
+if (__name__ == "__main__"):
+ _exercise()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/exitfuncs.py b/src/engine/SCons/exitfuncs.py
new file mode 100644
index 0000000..4179c37
--- /dev/null
+++ b/src/engine/SCons/exitfuncs.py
@@ -0,0 +1,77 @@
+"""SCons.exitfuncs
+
+Register functions which are executed when SCons exits for any reason.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/exitfuncs.py 4577 2009/12/27 19:44:43 scons"
+
+
+
+_exithandlers = []
+def _run_exitfuncs():
+ """run any registered exit functions
+
+ _exithandlers is traversed in reverse order so functions are executed
+ last in, first out.
+ """
+
+ while _exithandlers:
+ func, targs, kargs = _exithandlers.pop()
+ apply(func, targs, kargs)
+
+def register(func, *targs, **kargs):
+ """register a function to be executed upon normal program termination
+
+ func - function to be called at exit
+ targs - optional arguments to pass to func
+ kargs - optional keyword arguments to pass to func
+ """
+ _exithandlers.append((func, targs, kargs))
+
+import sys
+
+try:
+ x = sys.exitfunc
+
+ # if x isn't our own exit func executive, assume it's another
+ # registered exit function - append it to our list...
+ if x != _run_exitfuncs:
+ register(x)
+
+except AttributeError:
+ pass
+
+# make our exit function get run by python when it exits:
+sys.exitfunc = _run_exitfuncs
+
+del sys
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/setup.cfg b/src/engine/setup.cfg
new file mode 100644
index 0000000..94ede1f
--- /dev/null
+++ b/src/engine/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_rpm]
+group = Development/Tools
diff --git a/src/engine/setup.py b/src/engine/setup.py
new file mode 100644
index 0000000..dab75bd
--- /dev/null
+++ b/src/engine/setup.py
@@ -0,0 +1,74 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/setup.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import sys
+
+(head, tail) = os.path.split(sys.argv[0])
+
+if head:
+ os.chdir(head)
+ sys.argv[0] = tail
+
+# Code to figure out the package name from the current directory.
+# May come in handy to allow this setup.py to switch-hit between
+# python-scons and python2-scons.
+#head, package = os.path.split(os.getcwd())
+#suffix = "-1.2.0.d20091224"
+#if package[-len(suffix):] == suffix:
+# package = package[:-len(suffix)]
+
+package = 'python-scons'
+
+from distutils.core import setup
+
+ver = {
+ 'python-scons': '1.5',
+ 'python2-scons': '2.1',
+}
+
+setup(name = package,
+ version = "1.2.0.d20091224",
+ description = "SCons Python %s extension modules" % ver[package],
+ long_description = """SCons is an Open Source software construction tool--that is, a build tool; an
+improved substitute for the classic Make utility; a better way to build
+software.""",
+ author = "Steven Knight",
+ author_email = "knight@baldmt.com",
+ url = "http://www.scons.org/",
+ licence = "MIT, freely distributable",
+ keywords = "scons, cons, make, build tool, make tool",
+ packages = ["SCons",
+ "SCons.Node",
+ "SCons.Scanner",
+ "SCons.Sig",
+ "SCons.Script"])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/os_spawnv_fix.diff b/src/os_spawnv_fix.diff
new file mode 100644
index 0000000..926f896
--- /dev/null
+++ b/src/os_spawnv_fix.diff
@@ -0,0 +1,83 @@
+? dist/src/Mac/IDE scripts/Hold option to open a script
+? dist/src/Mac/IDE scripts/Insert file name
+? dist/src/Mac/IDE scripts/Insert folder name
+? dist/src/Mac/IDE scripts/Search Python Documentation
+? dist/src/Mac/IDE scripts/Hack/Remove .pyc files
+? dist/src/Mac/IDE scripts/Hack/Toolbox Assistant
+Index: dist/src/Modules/posixmodule.c
+===================================================================
+RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v
+retrieving revision 2.213
+diff -c -c -r2.213 posixmodule.c
+*** dist/src/Modules/posixmodule.c 2001/12/03 20:41:00 2.213
+--- dist/src/Modules/posixmodule.c 2001/12/05 00:52:58
+***************
+*** 1668,1674 ****
+ #ifdef HAVE_SPAWNV
+ static char posix_spawnv__doc__[] =
+ "spawnv(mode, path, args)\n\
+! Execute an executable path with arguments, replacing current process.\n\
+ \n\
+ mode: mode of process creation\n\
+ path: path of executable file\n\
+--- 1668,1674 ----
+ #ifdef HAVE_SPAWNV
+ static char posix_spawnv__doc__[] =
+ "spawnv(mode, path, args)\n\
+! Execute the program 'path' in a new process.\n\
+ \n\
+ mode: mode of process creation\n\
+ path: path of executable file\n\
+***************
+*** 1717,1724 ****
+
+ if (mode == _OLD_P_OVERLAY)
+ mode = _P_OVERLAY;
+ spawnval = _spawnv(mode, path, argvlist);
+!
+ PyMem_DEL(argvlist);
+
+ if (spawnval == -1)
+--- 1717,1727 ----
+
+ if (mode == _OLD_P_OVERLAY)
+ mode = _P_OVERLAY;
++
++ Py_BEGIN_ALLOW_THREADS
+ spawnval = _spawnv(mode, path, argvlist);
+! Py_END_ALLOW_THREADS
+!
+ PyMem_DEL(argvlist);
+
+ if (spawnval == -1)
+***************
+*** 1734,1740 ****
+
+ static char posix_spawnve__doc__[] =
+ "spawnve(mode, path, args, env)\n\
+! Execute a path with arguments and environment, replacing current process.\n\
+ \n\
+ mode: mode of process creation\n\
+ path: path of executable file\n\
+--- 1737,1743 ----
+
+ static char posix_spawnve__doc__[] =
+ "spawnve(mode, path, args, env)\n\
+! Execute the program 'path' in a new process.\n\
+ \n\
+ mode: mode of process creation\n\
+ path: path of executable file\n\
+***************
+*** 1830,1836 ****
+--- 1833,1843 ----
+
+ if (mode == _OLD_P_OVERLAY)
+ mode = _P_OVERLAY;
++
++ Py_BEGIN_ALLOW_THREADS
+ spawnval = _spawnve(mode, path, argvlist, envlist);
++ Py_END_ALLOW_THREADS
++
+ if (spawnval == -1)
+ (void) posix_error();
+ else
diff --git a/src/script/MANIFEST.in b/src/script/MANIFEST.in
new file mode 100644
index 0000000..f324ed4
--- /dev/null
+++ b/src/script/MANIFEST.in
@@ -0,0 +1,3 @@
+scons
+sconsign
+scons-time
diff --git a/src/script/README.txt b/src/script/README.txt
new file mode 100644
index 0000000..0060819
--- /dev/null
+++ b/src/script/README.txt
@@ -0,0 +1,169 @@
+###
+### THIS FILE IS NO LONGER USED. THIS IS THE README FILE FOR THE
+### SEPARATE SCRIPT PACKAGE FROM THE ORIGINAL (DRAFT) PACKAGING
+### SCHEME. WE'RE SAVING THIS IN CASE WE NEED OR WANT TO RESURRECT
+### A SEPARATE SCRIPT PACKAGE IN THE FUTURE.
+###
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+# src/script/README.txt 4577 2009/12/27 19:44:43 scons
+
+
+ SCons - a software construction tool
+
+ Version 1.2.0.d20091224
+
+
+This is an alpha release of SCons, a tool for building software (and
+other files). SCons is implemented in Python, and its "configuration
+files" are actually Python scripts, allowing you to use the full power
+of a real scripting language to solve build problems. You do not,
+however, need to know Python to use SCons effectively.
+
+
+LATEST VERSION
+==============
+
+Before going further, you can check that this package you have is
+the latest version by checking the SCons download page at:
+
+ http://www.scons.org/download.html
+
+
+ABOUT SCONS PACKAGES
+====================
+
+The complete SCons system is comprised of three separate packages:
+
+ scons
+ The scons script itself, plus the SCons build engine
+ installed into an SCons-specific library directory.
+
+ python-scons
+ The SCons build engine, installed into the standard
+ Python library directory.
+
+ scons-script [THIS PACKAGE]
+ Only the scons script itself.
+
+Depending on what you want to do with SCons, you may need to install
+additional (or other) packages:
+
+ If you just want to use scons (the script) to build software:
+
+ Do NOT install this package. Install the scons package instead,
+ which contains a copy of both the script and the build engine.
+ You will not need to install any other packages.
+
+ If you do NOT want to use the scons script, but you want to use the
+ SCons build engine in other Python software:
+
+ Do NOT install this package. Install the python-scons package
+ instead.
+
+ If you want to use the scons script AND you want to use the SCons
+ build engine in other Python software:
+
+ Do NOT install this package. Install both the scons and
+ python-scons packages instead.
+
+ Note that this installs two separate copies of the build engine,
+ one (in an SCons-specific library directory) used by the scons
+ script itself and one (in the standard Python library) used by
+ other software. This allows you the flexibility to upgrade
+ one build engine without affecting the other.
+
+ If you want the scons script and other Python software to use the
+ same version of the build engine:
+
+ Install this package AND the python-scons package.
+
+
+INSTALLATION
+============
+
+To install this package, simply run the provided Python-standard setup
+script as follows:
+
+ # python setup.py
+
+You should have system installation privileges (that is, "root" or
+"Administrator") when running the setup.py script.
+
+
+DOCUMENTATION
+=============
+
+Documentation for SCons is available at:
+
+ http://www.scons.org/doc.html
+
+
+LICENSING
+=========
+
+SCons is distributed under the MIT license, a full copy of which is
+available in the LICENSE.txt file. The MIT license is an approved Open
+Source license, which means:
+
+ This software is OSI Certified Open Source Software. OSI
+ Certified is a certification mark of the Open Source Initiative.
+
+More information about OSI certifications and Open Source software is
+available at:
+
+ http://www.opensource.org/
+
+
+REPORTING BUGS
+==============
+
+You can report bugs either by following the "Tracker - Bugs" link
+on the SCons project page:
+
+ http://sourceforge.net/projects/scons/
+
+or by sending mail to the SCons developers mailing list:
+
+ scons-devel@lists.sourceforge.net
+
+
+MAILING LISTS
+=============
+
+A mailing list for users of SCons is available. You may send
+questions or comments to the list at:
+
+ scons-users@lists.sourceforge.net
+
+You may subscribe to the mailing list at:
+
+ http://lists.sourceforge.net/lists/listinfo/scons-users
+
+There is also a low-volume mailing list available for announcements
+about SCons. Subscribe at:
+
+ http://lists.sourceforge.net/lists/listinfo/scons-announce
+
+
+FOR MORE INFORMATION
+====================
+
+Check the SCons web site at:
+
+ http://www.scons.org/
+
+
+AUTHOR INFO
+===========
+
+Steven Knight
+knight at baldmt dot com
+http://www.baldmt.com/~knight/
+
+With more than a little help from:
+ Chad Austin
+ Charles Crain
+ Steve Leblanc
+ Anthony Roach
+ Steven Shaw
+
diff --git a/src/script/scons-post-install.py b/src/script/scons-post-install.py
new file mode 100644
index 0000000..7c715d7
--- /dev/null
+++ b/src/script/scons-post-install.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# scons-post-install - SCons post install script for Windows
+#
+# A script for configuring "App Paths" registry key so that SCons could
+# be run from any directory the same way Python is.
+#
+
+#
+# SCons - a Software Constructor
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/script/scons-post-install.py 4577 2009/12/27 19:44:43 scons"
+
+import os.path
+import sys
+
+scons_bat_path = os.path.join(sys.prefix, 'Scripts', 'scons.bat')
+
+app_paths_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SCons.bat'
+
+def install():
+ if sys.platform == 'win32':
+ try:
+ import _winreg
+ except ImportError:
+ pass
+ else:
+ print 'Writing "App Paths" registry entry for %s' % scons_bat_path
+ _winreg.SetValue(
+ _winreg.HKEY_LOCAL_MACHINE,
+ app_paths_key,
+ _winreg.REG_SZ,
+ scons_bat_path)
+ print 'Done.'
+
+
+def remove():
+ if sys.platform == 'win32':
+ try:
+ import _winreg
+ except ImportError:
+ pass
+ else:
+ # print 'Remove "App Paths" registry entry'
+ _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, app_paths_key)
+
+
+if len(sys.argv) > 1:
+ if sys.argv[1] == '-install':
+ install()
+ elif sys.argv[1] == '-remove':
+ remove()
+
+sys.exit(0)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/script/scons-time.py b/src/script/scons-time.py
new file mode 100644
index 0000000..dafbe4b
--- /dev/null
+++ b/src/script/scons-time.py
@@ -0,0 +1,1529 @@
+#!/usr/bin/env python
+#
+# scons-time - run SCons timings and collect statistics
+#
+# A script for running a configuration through SCons with a standard
+# set of invocations to collect timing and memory statistics and to
+# capture the results in a consistent set of output files for display
+# and analysis.
+#
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+from __future__ import nested_scopes
+
+__revision__ = "src/script/scons-time.py 4577 2009/12/27 19:44:43 scons"
+
+import getopt
+import glob
+import os
+import os.path
+import re
+import shutil
+import string
+import sys
+import tempfile
+import time
+
+try:
+ False
+except NameError:
+ # Pre-2.2 Python has no False keyword.
+ import __builtin__
+ __builtin__.False = not 1
+
+try:
+ True
+except NameError:
+ # Pre-2.2 Python has no True keyword.
+ import __builtin__
+ __builtin__.True = not 0
+
+def make_temp_file(**kw):
+ try:
+ result = tempfile.mktemp(**kw)
+ try:
+ result = os.path.realpath(result)
+ except AttributeError:
+ # Python 2.1 has no os.path.realpath() method.
+ pass
+ except TypeError:
+ try:
+ save_template = tempfile.template
+ prefix = kw['prefix']
+ del kw['prefix']
+ tempfile.template = prefix
+ result = tempfile.mktemp(**kw)
+ finally:
+ tempfile.template = save_template
+ return result
+
+def HACK_for_exec(cmd, *args):
+ '''
+ For some reason, Python won't allow an exec() within a function
+ that also declares an internal function (including lambda functions).
+ This function is a hack that calls exec() in a function with no
+ internal functions.
+ '''
+ if not args: exec(cmd)
+ elif len(args) == 1: exec cmd in args[0]
+ else: exec cmd in args[0], args[1]
+
+class Plotter:
+ def increment_size(self, largest):
+ """
+ Return the size of each horizontal increment line for a specified
+ maximum value. This returns a value that will provide somewhere
+ between 5 and 9 horizontal lines on the graph, on some set of
+ boundaries that are multiples of 10/100/1000/etc.
+ """
+ i = largest / 5
+ if not i:
+ return largest
+ multiplier = 1
+ while i >= 10:
+ i = i / 10
+ multiplier = multiplier * 10
+ return i * multiplier
+
+ def max_graph_value(self, largest):
+ # Round up to next integer.
+ largest = int(largest) + 1
+ increment = self.increment_size(largest)
+ return ((largest + increment - 1) / increment) * increment
+
+class Line:
+ def __init__(self, points, type, title, label, comment, fmt="%s %s"):
+ self.points = points
+ self.type = type
+ self.title = title
+ self.label = label
+ self.comment = comment
+ self.fmt = fmt
+
+ def print_label(self, inx, x, y):
+ if self.label:
+ print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y)
+
+ def plot_string(self):
+ if self.title:
+ title_string = 'title "%s"' % self.title
+ else:
+ title_string = 'notitle'
+ return "'-' %s with lines lt %s" % (title_string, self.type)
+
+ def print_points(self, fmt=None):
+ if fmt is None:
+ fmt = self.fmt
+ if self.comment:
+ print '# %s' % self.comment
+ for x, y in self.points:
+ # If y is None, it usually represents some kind of break
+ # in the line's index number. We might want to represent
+ # this some way rather than just drawing the line straight
+ # between the two points on either side.
+ if not y is None:
+ print fmt % (x, y)
+ print 'e'
+
+ def get_x_values(self):
+ return [ p[0] for p in self.points ]
+
+ def get_y_values(self):
+ return [ p[1] for p in self.points ]
+
+class Gnuplotter(Plotter):
+
+ def __init__(self, title, key_location):
+ self.lines = []
+ self.title = title
+ self.key_location = key_location
+
+ def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'):
+ if points:
+ line = Line(points, type, title, label, comment, fmt)
+ self.lines.append(line)
+
+ def plot_string(self, line):
+ return line.plot_string()
+
+ def vertical_bar(self, x, type, label, comment):
+ if self.get_min_x() <= x and x <= self.get_max_x():
+ points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))]
+ self.line(points, type, label, comment)
+
+ def get_all_x_values(self):
+ result = []
+ for line in self.lines:
+ result.extend(line.get_x_values())
+ return filter(lambda r: not r is None, result)
+
+ def get_all_y_values(self):
+ result = []
+ for line in self.lines:
+ result.extend(line.get_y_values())
+ return filter(lambda r: not r is None, result)
+
+ def get_min_x(self):
+ try:
+ return self.min_x
+ except AttributeError:
+ try:
+ self.min_x = min(self.get_all_x_values())
+ except ValueError:
+ self.min_x = 0
+ return self.min_x
+
+ def get_max_x(self):
+ try:
+ return self.max_x
+ except AttributeError:
+ try:
+ self.max_x = max(self.get_all_x_values())
+ except ValueError:
+ self.max_x = 0
+ return self.max_x
+
+ def get_min_y(self):
+ try:
+ return self.min_y
+ except AttributeError:
+ try:
+ self.min_y = min(self.get_all_y_values())
+ except ValueError:
+ self.min_y = 0
+ return self.min_y
+
+ def get_max_y(self):
+ try:
+ return self.max_y
+ except AttributeError:
+ try:
+ self.max_y = max(self.get_all_y_values())
+ except ValueError:
+ self.max_y = 0
+ return self.max_y
+
+ def draw(self):
+
+ if not self.lines:
+ return
+
+ if self.title:
+ print 'set title "%s"' % self.title
+ print 'set key %s' % self.key_location
+
+ min_y = self.get_min_y()
+ max_y = self.max_graph_value(self.get_max_y())
+ range = max_y - min_y
+ incr = range / 10.0
+ start = min_y + (max_y / 2.0) + (2.0 * incr)
+ position = [ start - (i * incr) for i in xrange(5) ]
+
+ inx = 1
+ for line in self.lines:
+ line.print_label(inx, line.points[0][0]-1,
+ position[(inx-1) % len(position)])
+ inx += 1
+
+ plot_strings = [ self.plot_string(l) for l in self.lines ]
+ print 'plot ' + ', \\\n '.join(plot_strings)
+
+ for line in self.lines:
+ line.print_points()
+
+
+
+def untar(fname):
+ import tarfile
+ tar = tarfile.open(name=fname, mode='r')
+ for tarinfo in tar:
+ tar.extract(tarinfo)
+ tar.close()
+
+def unzip(fname):
+ import zipfile
+ zf = zipfile.ZipFile(fname, 'r')
+ for name in zf.namelist():
+ dir = os.path.dirname(name)
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ open(name, 'w').write(zf.read(name))
+
+def read_tree(dir):
+ def read_files(arg, dirname, fnames):
+ for fn in fnames:
+ fn = os.path.join(dirname, fn)
+ if os.path.isfile(fn):
+ open(fn, 'rb').read()
+ os.path.walk('.', read_files, None)
+
+def redirect_to_file(command, log):
+ return '%s > %s 2>&1' % (command, log)
+
+def tee_to_file(command, log):
+ return '%s 2>&1 | tee %s' % (command, log)
+
+
+
+class SConsTimer:
+ """
+ Usage: scons-time SUBCOMMAND [ARGUMENTS]
+ Type "scons-time help SUBCOMMAND" for help on a specific subcommand.
+
+ Available subcommands:
+ func Extract test-run data for a function
+ help Provides help
+ mem Extract --debug=memory data from test runs
+ obj Extract --debug=count data from test runs
+ time Extract --debug=time data from test runs
+ run Runs a test configuration
+ """
+
+ name = 'scons-time'
+ name_spaces = ' '*len(name)
+
+ def makedict(**kw):
+ return kw
+
+ default_settings = makedict(
+ aegis = 'aegis',
+ aegis_project = None,
+ chdir = None,
+ config_file = None,
+ initial_commands = [],
+ key_location = 'bottom left',
+ orig_cwd = os.getcwd(),
+ outdir = None,
+ prefix = '',
+ python = '"%s"' % sys.executable,
+ redirect = redirect_to_file,
+ scons = None,
+ scons_flags = '--debug=count --debug=memory --debug=time --debug=memoizer',
+ scons_lib_dir = None,
+ scons_wrapper = None,
+ startup_targets = '--help',
+ subdir = None,
+ subversion_url = None,
+ svn = 'svn',
+ svn_co_flag = '-q',
+ tar = 'tar',
+ targets = '',
+ targets0 = None,
+ targets1 = None,
+ targets2 = None,
+ title = None,
+ unzip = 'unzip',
+ verbose = False,
+ vertical_bars = [],
+
+ unpack_map = {
+ '.tar.gz' : (untar, '%(tar)s xzf %%s'),
+ '.tgz' : (untar, '%(tar)s xzf %%s'),
+ '.tar' : (untar, '%(tar)s xf %%s'),
+ '.zip' : (unzip, '%(unzip)s %%s'),
+ },
+ )
+
+ run_titles = [
+ 'Startup',
+ 'Full build',
+ 'Up-to-date build',
+ ]
+
+ run_commands = [
+ '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s',
+ '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s',
+ '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s',
+ ]
+
+ stages = [
+ 'pre-read',
+ 'post-read',
+ 'pre-build',
+ 'post-build',
+ ]
+
+ stage_strings = {
+ 'pre-read' : 'Memory before reading SConscript files:',
+ 'post-read' : 'Memory after reading SConscript files:',
+ 'pre-build' : 'Memory before building targets:',
+ 'post-build' : 'Memory after building targets:',
+ }
+
+ memory_string_all = 'Memory '
+
+ default_stage = stages[-1]
+
+ time_strings = {
+ 'total' : 'Total build time',
+ 'SConscripts' : 'Total SConscript file execution time',
+ 'SCons' : 'Total SCons execution time',
+ 'commands' : 'Total command execution time',
+ }
+
+ time_string_all = 'Total .* time'
+
+ #
+
+ def __init__(self):
+ self.__dict__.update(self.default_settings)
+
+ # Functions for displaying and executing commands.
+
+ def subst(self, x, dictionary):
+ try:
+ return x % dictionary
+ except TypeError:
+ # x isn't a string (it's probably a Python function),
+ # so just return it.
+ return x
+
+ def subst_variables(self, command, dictionary):
+ """
+ Substitutes (via the format operator) the values in the specified
+ dictionary into the specified command.
+
+ The command can be an (action, string) tuple. In all cases, we
+ perform substitution on strings and don't worry if something isn't
+ a string. (It's probably a Python function to be executed.)
+ """
+ try:
+ command + ''
+ except TypeError:
+ action = command[0]
+ string = command[1]
+ args = command[2:]
+ else:
+ action = command
+ string = action
+ args = (())
+ action = self.subst(action, dictionary)
+ string = self.subst(string, dictionary)
+ return (action, string, args)
+
+ def _do_not_display(self, msg, *args):
+ pass
+
+ def display(self, msg, *args):
+ """
+ Displays the specified message.
+
+ Each message is prepended with a standard prefix of our name
+ plus the time.
+ """
+ if callable(msg):
+ msg = msg(*args)
+ else:
+ msg = msg % args
+ if msg is None:
+ return
+ fmt = '%s[%s]: %s\n'
+ sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg))
+
+ def _do_not_execute(self, action, *args):
+ pass
+
+ def execute(self, action, *args):
+ """
+ Executes the specified action.
+
+ The action is called if it's a callable Python function, and
+ otherwise passed to os.system().
+ """
+ if callable(action):
+ action(*args)
+ else:
+ os.system(action % args)
+
+ def run_command_list(self, commands, dict):
+ """
+ Executes a list of commands, substituting values from the
+ specified dictionary.
+ """
+ commands = [ self.subst_variables(c, dict) for c in commands ]
+ for action, string, args in commands:
+ self.display(string, *args)
+ sys.stdout.flush()
+ status = self.execute(action, *args)
+ if status:
+ sys.exit(status)
+
+ def log_display(self, command, log):
+ command = self.subst(command, self.__dict__)
+ if log:
+ command = self.redirect(command, log)
+ return command
+
+ def log_execute(self, command, log):
+ command = self.subst(command, self.__dict__)
+ output = os.popen(command).read()
+ if self.verbose:
+ sys.stdout.write(output)
+ open(log, 'wb').write(output)
+
+ #
+
+ def archive_splitext(self, path):
+ """
+ Splits an archive name into a filename base and extension.
+
+ This is like os.path.splitext() (which it calls) except that it
+ also looks for '.tar.gz' and treats it as an atomic extensions.
+ """
+ if path.endswith('.tar.gz'):
+ return path[:-7], path[-7:]
+ else:
+ return os.path.splitext(path)
+
+ def args_to_files(self, args, tail=None):
+ """
+ Takes a list of arguments, expands any glob patterns, and
+ returns the last "tail" files from the list.
+ """
+ files = []
+ for a in args:
+ g = glob.glob(a)
+ g.sort()
+ files.extend(g)
+
+ if tail:
+ files = files[-tail:]
+
+ return files
+
+ def ascii_table(self, files, columns,
+ line_function, file_function=lambda x: x,
+ *args, **kw):
+
+ header_fmt = ' '.join(['%12s'] * len(columns))
+ line_fmt = header_fmt + ' %s'
+
+ print header_fmt % columns
+
+ for file in files:
+ t = line_function(file, *args, **kw)
+ if t is None:
+ t = []
+ diff = len(columns) - len(t)
+ if diff > 0:
+ t += [''] * diff
+ t.append(file_function(file))
+ print line_fmt % tuple(t)
+
+ def collect_results(self, files, function, *args, **kw):
+ results = {}
+
+ for file in files:
+ base = os.path.splitext(file)[0]
+ run, index = string.split(base, '-')[-2:]
+
+ run = int(run)
+ index = int(index)
+
+ value = function(file, *args, **kw)
+
+ try:
+ r = results[index]
+ except KeyError:
+ r = []
+ results[index] = r
+ r.append((run, value))
+
+ return results
+
+ def doc_to_help(self, obj):
+ """
+ Translates an object's __doc__ string into help text.
+
+ This strips a consistent number of spaces from each line in the
+ help text, essentially "outdenting" the text to the left-most
+ column.
+ """
+ doc = obj.__doc__
+ if doc is None:
+ return ''
+ return self.outdent(doc)
+
+ def find_next_run_number(self, dir, prefix):
+ """
+ Returns the next run number in a directory for the specified prefix.
+
+ Examines the contents the specified directory for files with the
+ specified prefix, extracts the run numbers from each file name,
+ and returns the next run number after the largest it finds.
+ """
+ x = re.compile(re.escape(prefix) + '-([0-9]+).*')
+ matches = map(lambda e, x=x: x.match(e), os.listdir(dir))
+ matches = filter(None, matches)
+ if not matches:
+ return 0
+ run_numbers = map(lambda m: int(m.group(1)), matches)
+ return int(max(run_numbers)) + 1
+
+ def gnuplot_results(self, results, fmt='%s %.3f'):
+ """
+ Prints out a set of results in Gnuplot format.
+ """
+ gp = Gnuplotter(self.title, self.key_location)
+
+ indices = results.keys()
+ indices.sort()
+
+ for i in indices:
+ try:
+ t = self.run_titles[i]
+ except IndexError:
+ t = '??? %s ???' % i
+ results[i].sort()
+ gp.line(results[i], i+1, t, None, t, fmt=fmt)
+
+ for bar_tuple in self.vertical_bars:
+ try:
+ x, type, label, comment = bar_tuple
+ except ValueError:
+ x, type, label = bar_tuple
+ comment = label
+ gp.vertical_bar(x, type, label, comment)
+
+ gp.draw()
+
+ def logfile_name(self, invocation):
+ """
+ Returns the absolute path of a log file for the specificed
+ invocation number.
+ """
+ name = self.prefix_run + '-%d.log' % invocation
+ return os.path.join(self.outdir, name)
+
+ def outdent(self, s):
+ """
+ Strip as many spaces from each line as are found at the beginning
+ of the first line in the list.
+ """
+ lines = s.split('\n')
+ if lines[0] == '':
+ lines = lines[1:]
+ spaces = re.match(' *', lines[0]).group(0)
+ def strip_initial_spaces(l, s=spaces):
+ if l.startswith(spaces):
+ l = l[len(spaces):]
+ return l
+ return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n'
+
+ def profile_name(self, invocation):
+ """
+ Returns the absolute path of a profile file for the specified
+ invocation number.
+ """
+ name = self.prefix_run + '-%d.prof' % invocation
+ return os.path.join(self.outdir, name)
+
+ def set_env(self, key, value):
+ os.environ[key] = value
+
+ #
+
+ def get_debug_times(self, file, time_string=None):
+ """
+ Fetch times from the --debug=time strings in the specified file.
+ """
+ if time_string is None:
+ search_string = self.time_string_all
+ else:
+ search_string = time_string
+ contents = open(file).read()
+ if not contents:
+ sys.stderr.write('file %s has no contents!\n' % repr(file))
+ return None
+ result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:]
+ result = [ float(r) for r in result ]
+ if not time_string is None:
+ try:
+ result = result[0]
+ except IndexError:
+ sys.stderr.write('file %s has no results!\n' % repr(file))
+ return None
+ return result
+
+ def get_function_profile(self, file, function):
+ """
+ Returns the file, line number, function name, and cumulative time.
+ """
+ try:
+ import pstats
+ except ImportError, e:
+ sys.stderr.write('%s: func: %s\n' % (self.name, e))
+ sys.stderr.write('%s This version of Python is missing the profiler.\n' % self.name_spaces)
+ sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.name_spaces)
+ sys.exit(1)
+ statistics = pstats.Stats(file).stats
+ matches = [ e for e in statistics.items() if e[0][2] == function ]
+ r = matches[0]
+ return r[0][0], r[0][1], r[0][2], r[1][3]
+
+ def get_function_time(self, file, function):
+ """
+ Returns just the cumulative time for the specified function.
+ """
+ return self.get_function_profile(file, function)[3]
+
+ def get_memory(self, file, memory_string=None):
+ """
+ Returns a list of integers of the amount of memory used. The
+ default behavior is to return all the stages.
+ """
+ if memory_string is None:
+ search_string = self.memory_string_all
+ else:
+ search_string = memory_string
+ lines = open(file).readlines()
+ lines = [ l for l in lines if l.startswith(search_string) ][-4:]
+ result = [ int(l.split()[-1]) for l in lines[-4:] ]
+ if len(result) == 1:
+ result = result[0]
+ return result
+
+ def get_object_counts(self, file, object_name, index=None):
+ """
+ Returns the counts of the specified object_name.
+ """
+ object_string = ' ' + object_name + '\n'
+ lines = open(file).readlines()
+ line = [ l for l in lines if l.endswith(object_string) ][0]
+ result = [ int(field) for field in line.split()[:4] ]
+ if index is not None:
+ result = result[index]
+ return result
+
+ #
+
+ command_alias = {}
+
+ def execute_subcommand(self, argv):
+ """
+ Executes the do_*() function for the specified subcommand (argv[0]).
+ """
+ if not argv:
+ return
+ cmdName = self.command_alias.get(argv[0], argv[0])
+ try:
+ func = getattr(self, 'do_' + cmdName)
+ except AttributeError:
+ return self.default(argv)
+ try:
+ return func(argv)
+ except TypeError, e:
+ sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e))
+ import traceback
+ traceback.print_exc(file=sys.stderr)
+ sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName))
+
+ def default(self, argv):
+ """
+ The default behavior for an unknown subcommand. Prints an
+ error message and exits.
+ """
+ sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0]))
+ sys.stderr.write('Type "%s help" for usage.\n' % self.name)
+ sys.exit(1)
+
+ #
+
+ def do_help(self, argv):
+ """
+ """
+ if argv[1:]:
+ for arg in argv[1:]:
+ try:
+ func = getattr(self, 'do_' + arg)
+ except AttributeError:
+ sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg))
+ else:
+ try:
+ help = getattr(self, 'help_' + arg)
+ except AttributeError:
+ sys.stdout.write(self.doc_to_help(func))
+ sys.stdout.flush()
+ else:
+ help()
+ else:
+ doc = self.doc_to_help(self.__class__)
+ if doc:
+ sys.stdout.write(doc)
+ sys.stdout.flush()
+ return None
+
+ #
+
+ def help_func(self):
+ help = """\
+ Usage: scons-time func [OPTIONS] FILE [...]
+
+ -C DIR, --chdir=DIR Change to DIR before looking for files
+ -f FILE, --file=FILE Read configuration from specified FILE
+ --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
+ --func=NAME, --function=NAME Report time for function NAME
+ -h, --help Print this help and exit
+ -p STRING, --prefix=STRING Use STRING as log file/profile prefix
+ -t NUMBER, --tail=NUMBER Only report the last NUMBER files
+ --title=TITLE Specify the output plot TITLE
+ """
+ sys.stdout.write(self.outdent(help))
+ sys.stdout.flush()
+
+ def do_func(self, argv):
+ """
+ """
+ format = 'ascii'
+ function_name = '_main'
+ tail = None
+
+ short_opts = '?C:f:hp:t:'
+
+ long_opts = [
+ 'chdir=',
+ 'file=',
+ 'fmt=',
+ 'format=',
+ 'func=',
+ 'function=',
+ 'help',
+ 'prefix=',
+ 'tail=',
+ 'title=',
+ ]
+
+ opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+
+ for o, a in opts:
+ if o in ('-C', '--chdir'):
+ self.chdir = a
+ elif o in ('-f', '--file'):
+ self.config_file = a
+ elif o in ('--fmt', '--format'):
+ format = a
+ elif o in ('--func', '--function'):
+ function_name = a
+ elif o in ('-?', '-h', '--help'):
+ self.do_help(['help', 'func'])
+ sys.exit(0)
+ elif o in ('--max',):
+ max_time = int(a)
+ elif o in ('-p', '--prefix'):
+ self.prefix = a
+ elif o in ('-t', '--tail'):
+ tail = int(a)
+ elif o in ('--title',):
+ self.title = a
+
+ if self.config_file:
+ exec open(self.config_file, 'rU').read() in self.__dict__
+
+ if self.chdir:
+ os.chdir(self.chdir)
+
+ if not args:
+
+ pattern = '%s*.prof' % self.prefix
+ args = self.args_to_files([pattern], tail)
+
+ if not args:
+ if self.chdir:
+ directory = self.chdir
+ else:
+ directory = os.getcwd()
+
+ sys.stderr.write('%s: func: No arguments specified.\n' % self.name)
+ sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
+ sys.stderr.write('%s Type "%s help func" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+
+ else:
+
+ args = self.args_to_files(args, tail)
+
+ cwd_ = os.getcwd() + os.sep
+
+ if format == 'ascii':
+
+ for file in args:
+ try:
+ f, line, func, time = \
+ self.get_function_profile(file, function_name)
+ except ValueError, e:
+ sys.stderr.write("%s: func: %s: %s\n" %
+ (self.name, file, e))
+ else:
+ if f.startswith(cwd_):
+ f = f[len(cwd_):]
+ print "%.3f %s:%d(%s)" % (time, f, line, func)
+
+ elif format == 'gnuplot':
+
+ results = self.collect_results(args, self.get_function_time,
+ function_name)
+
+ self.gnuplot_results(results)
+
+ else:
+
+ sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format))
+ sys.exit(1)
+
+ #
+
+ def help_mem(self):
+ help = """\
+ Usage: scons-time mem [OPTIONS] FILE [...]
+
+ -C DIR, --chdir=DIR Change to DIR before looking for files
+ -f FILE, --file=FILE Read configuration from specified FILE
+ --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
+ -h, --help Print this help and exit
+ -p STRING, --prefix=STRING Use STRING as log file/profile prefix
+ --stage=STAGE Plot memory at the specified stage:
+ pre-read, post-read, pre-build,
+ post-build (default: post-build)
+ -t NUMBER, --tail=NUMBER Only report the last NUMBER files
+ --title=TITLE Specify the output plot TITLE
+ """
+ sys.stdout.write(self.outdent(help))
+ sys.stdout.flush()
+
+ def do_mem(self, argv):
+
+ format = 'ascii'
+ logfile_path = lambda x: x
+ stage = self.default_stage
+ tail = None
+
+ short_opts = '?C:f:hp:t:'
+
+ long_opts = [
+ 'chdir=',
+ 'file=',
+ 'fmt=',
+ 'format=',
+ 'help',
+ 'prefix=',
+ 'stage=',
+ 'tail=',
+ 'title=',
+ ]
+
+ opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+
+ for o, a in opts:
+ if o in ('-C', '--chdir'):
+ self.chdir = a
+ elif o in ('-f', '--file'):
+ self.config_file = a
+ elif o in ('--fmt', '--format'):
+ format = a
+ elif o in ('-?', '-h', '--help'):
+ self.do_help(['help', 'mem'])
+ sys.exit(0)
+ elif o in ('-p', '--prefix'):
+ self.prefix = a
+ elif o in ('--stage',):
+ if not a in self.stages:
+ sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a))
+ sys.exit(1)
+ stage = a
+ elif o in ('-t', '--tail'):
+ tail = int(a)
+ elif o in ('--title',):
+ self.title = a
+
+ if self.config_file:
+ HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__)
+
+ if self.chdir:
+ os.chdir(self.chdir)
+ logfile_path = lambda x, c=self.chdir: os.path.join(c, x)
+
+ if not args:
+
+ pattern = '%s*.log' % self.prefix
+ args = self.args_to_files([pattern], tail)
+
+ if not args:
+ if self.chdir:
+ directory = self.chdir
+ else:
+ directory = os.getcwd()
+
+ sys.stderr.write('%s: mem: No arguments specified.\n' % self.name)
+ sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
+ sys.stderr.write('%s Type "%s help mem" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+
+ else:
+
+ args = self.args_to_files(args, tail)
+
+ cwd_ = os.getcwd() + os.sep
+
+ if format == 'ascii':
+
+ self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_path)
+
+ elif format == 'gnuplot':
+
+ results = self.collect_results(args, self.get_memory,
+ self.stage_strings[stage])
+
+ self.gnuplot_results(results)
+
+ else:
+
+ sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, format))
+ sys.exit(1)
+
+ return 0
+
+ #
+
+ def help_obj(self):
+ help = """\
+ Usage: scons-time obj [OPTIONS] OBJECT FILE [...]
+
+ -C DIR, --chdir=DIR Change to DIR before looking for files
+ -f FILE, --file=FILE Read configuration from specified FILE
+ --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
+ -h, --help Print this help and exit
+ -p STRING, --prefix=STRING Use STRING as log file/profile prefix
+ --stage=STAGE Plot memory at the specified stage:
+ pre-read, post-read, pre-build,
+ post-build (default: post-build)
+ -t NUMBER, --tail=NUMBER Only report the last NUMBER files
+ --title=TITLE Specify the output plot TITLE
+ """
+ sys.stdout.write(self.outdent(help))
+ sys.stdout.flush()
+
+ def do_obj(self, argv):
+
+ format = 'ascii'
+ logfile_path = lambda x: x
+ stage = self.default_stage
+ tail = None
+
+ short_opts = '?C:f:hp:t:'
+
+ long_opts = [
+ 'chdir=',
+ 'file=',
+ 'fmt=',
+ 'format=',
+ 'help',
+ 'prefix=',
+ 'stage=',
+ 'tail=',
+ 'title=',
+ ]
+
+ opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+
+ for o, a in opts:
+ if o in ('-C', '--chdir'):
+ self.chdir = a
+ elif o in ('-f', '--file'):
+ self.config_file = a
+ elif o in ('--fmt', '--format'):
+ format = a
+ elif o in ('-?', '-h', '--help'):
+ self.do_help(['help', 'obj'])
+ sys.exit(0)
+ elif o in ('-p', '--prefix'):
+ self.prefix = a
+ elif o in ('--stage',):
+ if not a in self.stages:
+ sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a))
+ sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+ stage = a
+ elif o in ('-t', '--tail'):
+ tail = int(a)
+ elif o in ('--title',):
+ self.title = a
+
+ if not args:
+ sys.stderr.write('%s: obj: Must specify an object name.\n' % self.name)
+ sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+
+ object_name = args.pop(0)
+
+ if self.config_file:
+ HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__)
+
+ if self.chdir:
+ os.chdir(self.chdir)
+ logfile_path = lambda x, c=self.chdir: os.path.join(c, x)
+
+ if not args:
+
+ pattern = '%s*.log' % self.prefix
+ args = self.args_to_files([pattern], tail)
+
+ if not args:
+ if self.chdir:
+ directory = self.chdir
+ else:
+ directory = os.getcwd()
+
+ sys.stderr.write('%s: obj: No arguments specified.\n' % self.name)
+ sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
+ sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+
+ else:
+
+ args = self.args_to_files(args, tail)
+
+ cwd_ = os.getcwd() + os.sep
+
+ if format == 'ascii':
+
+ self.ascii_table(args, tuple(self.stages), self.get_object_counts, logfile_path, object_name)
+
+ elif format == 'gnuplot':
+
+ stage_index = 0
+ for s in self.stages:
+ if stage == s:
+ break
+ stage_index = stage_index + 1
+
+ results = self.collect_results(args, self.get_object_counts,
+ object_name, stage_index)
+
+ self.gnuplot_results(results)
+
+ else:
+
+ sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, format))
+ sys.exit(1)
+
+ return 0
+
+ #
+
+ def help_run(self):
+ help = """\
+ Usage: scons-time run [OPTIONS] [FILE ...]
+
+ --aegis=PROJECT Use SCons from the Aegis PROJECT
+ --chdir=DIR Name of unpacked directory for chdir
+ -f FILE, --file=FILE Read configuration from specified FILE
+ -h, --help Print this help and exit
+ -n, --no-exec No execute, just print command lines
+ --number=NUMBER Put output in files for run NUMBER
+ --outdir=OUTDIR Put output files in OUTDIR
+ -p STRING, --prefix=STRING Use STRING as log file/profile prefix
+ --python=PYTHON Time using the specified PYTHON
+ -q, --quiet Don't print command lines
+ --scons=SCONS Time using the specified SCONS
+ --svn=URL, --subversion=URL Use SCons from Subversion URL
+ -v, --verbose Display output of commands
+ """
+ sys.stdout.write(self.outdent(help))
+ sys.stdout.flush()
+
+ def do_run(self, argv):
+ """
+ """
+ run_number_list = [None]
+
+ short_opts = '?f:hnp:qs:v'
+
+ long_opts = [
+ 'aegis=',
+ 'file=',
+ 'help',
+ 'no-exec',
+ 'number=',
+ 'outdir=',
+ 'prefix=',
+ 'python=',
+ 'quiet',
+ 'scons=',
+ 'svn=',
+ 'subdir=',
+ 'subversion=',
+ 'verbose',
+ ]
+
+ opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+
+ for o, a in opts:
+ if o in ('--aegis',):
+ self.aegis_project = a
+ elif o in ('-f', '--file'):
+ self.config_file = a
+ elif o in ('-?', '-h', '--help'):
+ self.do_help(['help', 'run'])
+ sys.exit(0)
+ elif o in ('-n', '--no-exec'):
+ self.execute = self._do_not_execute
+ elif o in ('--number',):
+ run_number_list = self.split_run_numbers(a)
+ elif o in ('--outdir',):
+ self.outdir = a
+ elif o in ('-p', '--prefix'):
+ self.prefix = a
+ elif o in ('--python',):
+ self.python = a
+ elif o in ('-q', '--quiet'):
+ self.display = self._do_not_display
+ elif o in ('-s', '--subdir'):
+ self.subdir = a
+ elif o in ('--scons',):
+ self.scons = a
+ elif o in ('--svn', '--subversion'):
+ self.subversion_url = a
+ elif o in ('-v', '--verbose'):
+ self.redirect = tee_to_file
+ self.verbose = True
+ self.svn_co_flag = ''
+
+ if not args and not self.config_file:
+ sys.stderr.write('%s: run: No arguments or -f config file specified.\n' % self.name)
+ sys.stderr.write('%s Type "%s help run" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+
+ if self.config_file:
+ exec open(self.config_file, 'rU').read() in self.__dict__
+
+ if args:
+ self.archive_list = args
+
+ archive_file_name = os.path.split(self.archive_list[0])[1]
+
+ if not self.subdir:
+ self.subdir = self.archive_splitext(archive_file_name)[0]
+
+ if not self.prefix:
+ self.prefix = self.archive_splitext(archive_file_name)[0]
+
+ prepare = None
+ if self.subversion_url:
+ prepare = self.prep_subversion_run
+ elif self.aegis_project:
+ prepare = self.prep_aegis_run
+
+ for run_number in run_number_list:
+ self.individual_run(run_number, self.archive_list, prepare)
+
+ def split_run_numbers(self, s):
+ result = []
+ for n in s.split(','):
+ try:
+ x, y = n.split('-')
+ except ValueError:
+ result.append(int(n))
+ else:
+ result.extend(range(int(x), int(y)+1))
+ return result
+
+ def scons_path(self, dir):
+ return os.path.join(dir, 'src', 'script', 'scons.py')
+
+ def scons_lib_dir_path(self, dir):
+ return os.path.join(dir, 'src', 'engine')
+
+ def prep_aegis_run(self, commands, removals):
+ self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-')
+ removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir))
+
+ self.aegis_parent_project = os.path.splitext(self.aegis_project)[0]
+ self.scons = self.scons_path(self.aegis_tmpdir)
+ self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir)
+
+ commands.extend([
+ 'mkdir %(aegis_tmpdir)s',
+ (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'),
+ '%(aegis)s -cp -ind -p %(aegis_parent_project)s .',
+ '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .',
+ ])
+
+ def prep_subversion_run(self, commands, removals):
+ self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-')
+ removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir))
+
+ self.scons = self.scons_path(self.svn_tmpdir)
+ self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir)
+
+ commands.extend([
+ 'mkdir %(svn_tmpdir)s',
+ '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s',
+ ])
+
+ def individual_run(self, run_number, archive_list, prepare=None):
+ """
+ Performs an individual run of the default SCons invocations.
+ """
+
+ commands = []
+ removals = []
+
+ if prepare:
+ prepare(commands, removals)
+
+ save_scons = self.scons
+ save_scons_wrapper = self.scons_wrapper
+ save_scons_lib_dir = self.scons_lib_dir
+
+ if self.outdir is None:
+ self.outdir = self.orig_cwd
+ elif not os.path.isabs(self.outdir):
+ self.outdir = os.path.join(self.orig_cwd, self.outdir)
+
+ if self.scons is None:
+ self.scons = self.scons_path(self.orig_cwd)
+
+ if self.scons_lib_dir is None:
+ self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd)
+
+ if self.scons_wrapper is None:
+ self.scons_wrapper = self.scons
+
+ if not run_number:
+ run_number = self.find_next_run_number(self.outdir, self.prefix)
+
+ self.run_number = str(run_number)
+
+ self.prefix_run = self.prefix + '-%03d' % run_number
+
+ if self.targets0 is None:
+ self.targets0 = self.startup_targets
+ if self.targets1 is None:
+ self.targets1 = self.targets
+ if self.targets2 is None:
+ self.targets2 = self.targets
+
+ self.tmpdir = make_temp_file(prefix = self.name + '-')
+
+ commands.extend([
+ 'mkdir %(tmpdir)s',
+
+ (os.chdir, 'cd %%s', self.tmpdir),
+ ])
+
+ for archive in archive_list:
+ if not os.path.isabs(archive):
+ archive = os.path.join(self.orig_cwd, archive)
+ if os.path.isdir(archive):
+ dest = os.path.split(archive)[1]
+ commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest))
+ else:
+ suffix = self.archive_splitext(archive)[1]
+ unpack_command = self.unpack_map.get(suffix)
+ if not unpack_command:
+ dest = os.path.split(archive)[1]
+ commands.append((shutil.copyfile, 'cp %%s %%s', archive, dest))
+ else:
+ commands.append(unpack_command + (archive,))
+
+ commands.extend([
+ (os.chdir, 'cd %%s', self.subdir),
+ ])
+
+ commands.extend(self.initial_commands)
+
+ commands.extend([
+ (lambda: read_tree('.'),
+ 'find * -type f | xargs cat > /dev/null'),
+
+ (self.set_env, 'export %%s=%%s',
+ 'SCONS_LIB_DIR', self.scons_lib_dir),
+
+ '%(python)s %(scons_wrapper)s --version',
+ ])
+
+ index = 0
+ for run_command in self.run_commands:
+ setattr(self, 'prof%d' % index, self.profile_name(index))
+ c = (
+ self.log_execute,
+ self.log_display,
+ run_command,
+ self.logfile_name(index),
+ )
+ commands.append(c)
+ index = index + 1
+
+ commands.extend([
+ (os.chdir, 'cd %%s', self.orig_cwd),
+ ])
+
+ if not os.environ.get('PRESERVE'):
+ commands.extend(removals)
+
+ commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir))
+
+ self.run_command_list(commands, self.__dict__)
+
+ self.scons = save_scons
+ self.scons_lib_dir = save_scons_lib_dir
+ self.scons_wrapper = save_scons_wrapper
+
+ #
+
+ def help_time(self):
+ help = """\
+ Usage: scons-time time [OPTIONS] FILE [...]
+
+ -C DIR, --chdir=DIR Change to DIR before looking for files
+ -f FILE, --file=FILE Read configuration from specified FILE
+ --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
+ -h, --help Print this help and exit
+ -p STRING, --prefix=STRING Use STRING as log file/profile prefix
+ -t NUMBER, --tail=NUMBER Only report the last NUMBER files
+ --which=TIMER Plot timings for TIMER: total,
+ SConscripts, SCons, commands.
+ """
+ sys.stdout.write(self.outdent(help))
+ sys.stdout.flush()
+
+ def do_time(self, argv):
+
+ format = 'ascii'
+ logfile_path = lambda x: x
+ tail = None
+ which = 'total'
+
+ short_opts = '?C:f:hp:t:'
+
+ long_opts = [
+ 'chdir=',
+ 'file=',
+ 'fmt=',
+ 'format=',
+ 'help',
+ 'prefix=',
+ 'tail=',
+ 'title=',
+ 'which=',
+ ]
+
+ opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+
+ for o, a in opts:
+ if o in ('-C', '--chdir'):
+ self.chdir = a
+ elif o in ('-f', '--file'):
+ self.config_file = a
+ elif o in ('--fmt', '--format'):
+ format = a
+ elif o in ('-?', '-h', '--help'):
+ self.do_help(['help', 'time'])
+ sys.exit(0)
+ elif o in ('-p', '--prefix'):
+ self.prefix = a
+ elif o in ('-t', '--tail'):
+ tail = int(a)
+ elif o in ('--title',):
+ self.title = a
+ elif o in ('--which',):
+ if not a in self.time_strings.keys():
+ sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a))
+ sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+ which = a
+
+ if self.config_file:
+ HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__)
+
+ if self.chdir:
+ os.chdir(self.chdir)
+ logfile_path = lambda x, c=self.chdir: os.path.join(c, x)
+
+ if not args:
+
+ pattern = '%s*.log' % self.prefix
+ args = self.args_to_files([pattern], tail)
+
+ if not args:
+ if self.chdir:
+ directory = self.chdir
+ else:
+ directory = os.getcwd()
+
+ sys.stderr.write('%s: time: No arguments specified.\n' % self.name)
+ sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
+ sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name))
+ sys.exit(1)
+
+ else:
+
+ args = self.args_to_files(args, tail)
+
+ cwd_ = os.getcwd() + os.sep
+
+ if format == 'ascii':
+
+ columns = ("Total", "SConscripts", "SCons", "commands")
+ self.ascii_table(args, columns, self.get_debug_times, logfile_path)
+
+ elif format == 'gnuplot':
+
+ results = self.collect_results(args, self.get_debug_times,
+ self.time_strings[which])
+
+ self.gnuplot_results(results, fmt='%s %.6f')
+
+ else:
+
+ sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, format))
+ sys.exit(1)
+
+if __name__ == '__main__':
+ opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version'])
+
+ ST = SConsTimer()
+
+ for o, a in opts:
+ if o in ('-?', '-h', '--help'):
+ ST.do_help(['help'])
+ sys.exit(0)
+ elif o in ('-V', '--version'):
+ sys.stdout.write('scons-time version\n')
+ sys.exit(0)
+
+ if not args:
+ sys.stderr.write('Type "%s help" for usage.\n' % ST.name)
+ sys.exit(1)
+
+ ST.execute_subcommand(args)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/script/scons.bat b/src/script/scons.bat
new file mode 100644
index 0000000..566387a
--- /dev/null
+++ b/src/script/scons.bat
@@ -0,0 +1,31 @@
+@REM Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+@REM src/script/scons.bat 4577 2009/12/27 19:44:43 scons
+@echo off
+set SCONS_ERRORLEVEL=
+if "%OS%" == "Windows_NT" goto WinNT
+
+@REM for 9x/Me you better not have more than 9 args
+python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-1.2.0.d20091224'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-1.2.0.d20091224'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %1 %2 %3 %4 %5 %6 %7 %8 %9
+@REM no way to set exit status of this script for 9x/Me
+goto endscons
+
+@REM Credit where credit is due: we return the exit code despite our
+@REM use of setlocal+endlocal using a technique from Bear's Journal:
+@REM http://code-bear.com/bearlog/2007/06/01/getting-the-exit-code-from-a-batch-file-that-is-run-from-a-python-program/
+
+:WinNT
+setlocal
+@REM ensure the script will be executed with the Python it was installed for
+set path=%~dp0;%~dp0..;%path%
+python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-1.2.0.d20091224'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-1.2.0.d20091224'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %*
+endlocal & set SCONS_ERRORLEVEL=%ERRORLEVEL%
+
+if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto returncode
+if errorlevel 9009 echo you do not have python in your PATH
+goto endscons
+
+:returncode
+exit /B %SCONS_ERRORLEVEL%
+
+:endscons
+call :returncode %SCONS_ERRORLEVEL%
diff --git a/src/script/scons.py b/src/script/scons.py
new file mode 100644
index 0000000..6e21a09
--- /dev/null
+++ b/src/script/scons.py
@@ -0,0 +1,184 @@
+#! /usr/bin/env python
+#
+# SCons - a Software Constructor
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/script/scons.py 4577 2009/12/27 19:44:43 scons"
+
+__version__ = "1.2.0.d20091224"
+
+__build__ = "r4577[MODIFIED]"
+
+__buildsys__ = "scons-dev"
+
+__date__ = "2009/12/27 19:44:43"
+
+__developer__ = "scons"
+
+import os
+import os.path
+import sys
+
+##############################################################################
+# BEGIN STANDARD SCons SCRIPT HEADER
+#
+# This is the cut-and-paste logic so that a self-contained script can
+# interoperate correctly with different SCons versions and installation
+# locations for the engine. If you modify anything in this section, you
+# should also change other scripts that use this same header.
+##############################################################################
+
+# Strip the script directory from sys.path() so on case-insensitive
+# (WIN32) systems Python doesn't think that the "scons" script is the
+# "SCons" package. Replace it with our own library directories
+# (version-specific first, in case they installed by hand there,
+# followed by generic) so we pick up the right version of the build
+# engine modules if they're in either directory.
+
+
+# Check to see if the python version is > 3.0 which is currently unsupported
+# If so exit with error message
+try:
+ if sys.version_info >= (3,0,0):
+ msg = "scons: *** SCons version %s does not run under Python version %s.\n"
+ sys.stderr.write(msg % (__version__, sys.version.split()[0]))
+ sys.exit(1)
+except AttributeError:
+ # Pre-1.6 Python has no sys.version_info
+ # No need to check version as we then know the version is < 3.0.0 and supported
+ pass
+
+script_dir = sys.path[0]
+
+if script_dir in sys.path:
+ sys.path.remove(script_dir)
+
+libs = []
+
+if os.environ.has_key("SCONS_LIB_DIR"):
+ libs.append(os.environ["SCONS_LIB_DIR"])
+
+local_version = 'scons-local-' + __version__
+local = 'scons-local'
+if script_dir:
+ local_version = os.path.join(script_dir, local_version)
+ local = os.path.join(script_dir, local)
+libs.append(os.path.abspath(local_version))
+libs.append(os.path.abspath(local))
+
+scons_version = 'scons-%s' % __version__
+
+prefs = []
+
+if sys.platform == 'win32':
+ # sys.prefix is (likely) C:\Python*;
+ # check only C:\Python*.
+ prefs.append(sys.prefix)
+ prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages'))
+else:
+ # On other (POSIX) platforms, things are more complicated due to
+ # the variety of path names and library locations. Try to be smart
+ # about it.
+ if script_dir == 'bin':
+ # script_dir is `pwd`/bin;
+ # check `pwd`/lib/scons*.
+ prefs.append(os.getcwd())
+ else:
+ if script_dir == '.' or script_dir == '':
+ script_dir = os.getcwd()
+ head, tail = os.path.split(script_dir)
+ if tail == "bin":
+ # script_dir is /foo/bin;
+ # check /foo/lib/scons*.
+ prefs.append(head)
+
+ head, tail = os.path.split(sys.prefix)
+ if tail == "usr":
+ # sys.prefix is /foo/usr;
+ # check /foo/usr/lib/scons* first,
+ # then /foo/usr/local/lib/scons*.
+ prefs.append(sys.prefix)
+ prefs.append(os.path.join(sys.prefix, "local"))
+ elif tail == "local":
+ h, t = os.path.split(head)
+ if t == "usr":
+ # sys.prefix is /foo/usr/local;
+ # check /foo/usr/local/lib/scons* first,
+ # then /foo/usr/lib/scons*.
+ prefs.append(sys.prefix)
+ prefs.append(head)
+ else:
+ # sys.prefix is /foo/local;
+ # check only /foo/local/lib/scons*.
+ prefs.append(sys.prefix)
+ else:
+ # sys.prefix is /foo (ends in neither /usr or /local);
+ # check only /foo/lib/scons*.
+ prefs.append(sys.prefix)
+
+ temp = map(lambda x: os.path.join(x, 'lib'), prefs)
+ temp.extend(map(lambda x: os.path.join(x,
+ 'lib',
+ 'python' + sys.version[:3],
+ 'site-packages'),
+ prefs))
+ prefs = temp
+
+ # Add the parent directory of the current python's library to the
+ # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64,
+ # not /usr/lib.
+ try:
+ libpath = os.__file__
+ except AttributeError:
+ pass
+ else:
+ # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*.
+ libpath, tail = os.path.split(libpath)
+ # Split /usr/libfoo/python* to /usr/libfoo
+ libpath, tail = os.path.split(libpath)
+ # Check /usr/libfoo/scons*.
+ prefs.append(libpath)
+
+# Look first for 'scons-__version__' in all of our preference libs,
+# then for 'scons'.
+libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
+libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
+
+sys.path = libs + sys.path
+
+##############################################################################
+# END STANDARD SCons SCRIPT HEADER
+##############################################################################
+
+if __name__ == "__main__":
+ import SCons.Script
+ # this does all the work, and calls sys.exit
+ # with the proper exit status when done.
+ SCons.Script.main()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/script/sconsign.py b/src/script/sconsign.py
new file mode 100644
index 0000000..406c7f5
--- /dev/null
+++ b/src/script/sconsign.py
@@ -0,0 +1,508 @@
+#! /usr/bin/env python
+#
+# SCons - a Software Constructor
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/script/sconsign.py 4577 2009/12/27 19:44:43 scons"
+
+__version__ = "1.2.0.d20091224"
+
+__build__ = "r4577[MODIFIED]"
+
+__buildsys__ = "scons-dev"
+
+__date__ = "2009/12/27 19:44:43"
+
+__developer__ = "scons"
+
+import os
+import os.path
+import sys
+import time
+
+##############################################################################
+# BEGIN STANDARD SCons SCRIPT HEADER
+#
+# This is the cut-and-paste logic so that a self-contained script can
+# interoperate correctly with different SCons versions and installation
+# locations for the engine. If you modify anything in this section, you
+# should also change other scripts that use this same header.
+##############################################################################
+
+# Strip the script directory from sys.path() so on case-insensitive
+# (WIN32) systems Python doesn't think that the "scons" script is the
+# "SCons" package. Replace it with our own library directories
+# (version-specific first, in case they installed by hand there,
+# followed by generic) so we pick up the right version of the build
+# engine modules if they're in either directory.
+
+script_dir = sys.path[0]
+
+if script_dir in sys.path:
+ sys.path.remove(script_dir)
+
+libs = []
+
+if os.environ.has_key("SCONS_LIB_DIR"):
+ libs.append(os.environ["SCONS_LIB_DIR"])
+
+local_version = 'scons-local-' + __version__
+local = 'scons-local'
+if script_dir:
+ local_version = os.path.join(script_dir, local_version)
+ local = os.path.join(script_dir, local)
+libs.append(os.path.abspath(local_version))
+libs.append(os.path.abspath(local))
+
+scons_version = 'scons-%s' % __version__
+
+prefs = []
+
+if sys.platform == 'win32':
+ # sys.prefix is (likely) C:\Python*;
+ # check only C:\Python*.
+ prefs.append(sys.prefix)
+ prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages'))
+else:
+ # On other (POSIX) platforms, things are more complicated due to
+ # the variety of path names and library locations. Try to be smart
+ # about it.
+ if script_dir == 'bin':
+ # script_dir is `pwd`/bin;
+ # check `pwd`/lib/scons*.
+ prefs.append(os.getcwd())
+ else:
+ if script_dir == '.' or script_dir == '':
+ script_dir = os.getcwd()
+ head, tail = os.path.split(script_dir)
+ if tail == "bin":
+ # script_dir is /foo/bin;
+ # check /foo/lib/scons*.
+ prefs.append(head)
+
+ head, tail = os.path.split(sys.prefix)
+ if tail == "usr":
+ # sys.prefix is /foo/usr;
+ # check /foo/usr/lib/scons* first,
+ # then /foo/usr/local/lib/scons*.
+ prefs.append(sys.prefix)
+ prefs.append(os.path.join(sys.prefix, "local"))
+ elif tail == "local":
+ h, t = os.path.split(head)
+ if t == "usr":
+ # sys.prefix is /foo/usr/local;
+ # check /foo/usr/local/lib/scons* first,
+ # then /foo/usr/lib/scons*.
+ prefs.append(sys.prefix)
+ prefs.append(head)
+ else:
+ # sys.prefix is /foo/local;
+ # check only /foo/local/lib/scons*.
+ prefs.append(sys.prefix)
+ else:
+ # sys.prefix is /foo (ends in neither /usr or /local);
+ # check only /foo/lib/scons*.
+ prefs.append(sys.prefix)
+
+ temp = map(lambda x: os.path.join(x, 'lib'), prefs)
+ temp.extend(map(lambda x: os.path.join(x,
+ 'lib',
+ 'python' + sys.version[:3],
+ 'site-packages'),
+ prefs))
+ prefs = temp
+
+ # Add the parent directory of the current python's library to the
+ # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64,
+ # not /usr/lib.
+ try:
+ libpath = os.__file__
+ except AttributeError:
+ pass
+ else:
+ # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*.
+ libpath, tail = os.path.split(libpath)
+ # Split /usr/libfoo/python* to /usr/libfoo
+ libpath, tail = os.path.split(libpath)
+ # Check /usr/libfoo/scons*.
+ prefs.append(libpath)
+
+# Look first for 'scons-__version__' in all of our preference libs,
+# then for 'scons'.
+libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
+libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
+
+sys.path = libs + sys.path
+
+##############################################################################
+# END STANDARD SCons SCRIPT HEADER
+##############################################################################
+
+import cPickle
+import imp
+import string
+import whichdb
+
+import SCons.SConsign
+
+def my_whichdb(filename):
+ if filename[-7:] == ".dblite":
+ return "SCons.dblite"
+ try:
+ f = open(filename + ".dblite", "rb")
+ f.close()
+ return "SCons.dblite"
+ except IOError:
+ pass
+ return _orig_whichdb(filename)
+
+_orig_whichdb = whichdb.whichdb
+whichdb.whichdb = my_whichdb
+
+def my_import(mname):
+ if '.' in mname:
+ i = string.rfind(mname, '.')
+ parent = my_import(mname[:i])
+ fp, pathname, description = imp.find_module(mname[i+1:],
+ parent.__path__)
+ else:
+ fp, pathname, description = imp.find_module(mname)
+ return imp.load_module(mname, fp, pathname, description)
+
+class Flagger:
+ default_value = 1
+ def __setitem__(self, item, value):
+ self.__dict__[item] = value
+ self.default_value = 0
+ def __getitem__(self, item):
+ return self.__dict__.get(item, self.default_value)
+
+Do_Call = None
+Print_Directories = []
+Print_Entries = []
+Print_Flags = Flagger()
+Verbose = 0
+Readable = 0
+
+def default_mapper(entry, name):
+ try:
+ val = eval("entry."+name)
+ except:
+ val = None
+ return str(val)
+
+def map_action(entry, name):
+ try:
+ bact = entry.bact
+ bactsig = entry.bactsig
+ except AttributeError:
+ return None
+ return '%s [%s]' % (bactsig, bact)
+
+def map_timestamp(entry, name):
+ try:
+ timestamp = entry.timestamp
+ except AttributeError:
+ timestamp = None
+ if Readable and timestamp:
+ return "'" + time.ctime(timestamp) + "'"
+ else:
+ return str(timestamp)
+
+def map_bkids(entry, name):
+ try:
+ bkids = entry.bsources + entry.bdepends + entry.bimplicit
+ bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs
+ except AttributeError:
+ return None
+ result = []
+ for i in xrange(len(bkids)):
+ result.append(nodeinfo_string(bkids[i], bkidsigs[i], " "))
+ if result == []:
+ return None
+ return string.join(result, "\n ")
+
+map_field = {
+ 'action' : map_action,
+ 'timestamp' : map_timestamp,
+ 'bkids' : map_bkids,
+}
+
+map_name = {
+ 'implicit' : 'bkids',
+}
+
+def field(name, entry, verbose=Verbose):
+ if not Print_Flags[name]:
+ return None
+ fieldname = map_name.get(name, name)
+ mapper = map_field.get(fieldname, default_mapper)
+ val = mapper(entry, name)
+ if verbose:
+ val = name + ": " + val
+ return val
+
+def nodeinfo_raw(name, ninfo, prefix=""):
+ # This just formats the dictionary, which we would normally use str()
+ # to do, except that we want the keys sorted for deterministic output.
+ d = ninfo.__dict__
+ try:
+ keys = ninfo.field_list + ['_version_id']
+ except AttributeError:
+ keys = d.keys()
+ keys.sort()
+ l = []
+ for k in keys:
+ l.append('%s: %s' % (repr(k), repr(d.get(k))))
+ if '\n' in name:
+ name = repr(name)
+ return name + ': {' + string.join(l, ', ') + '}'
+
+def nodeinfo_cooked(name, ninfo, prefix=""):
+ try:
+ field_list = ninfo.field_list
+ except AttributeError:
+ field_list = []
+ f = lambda x, ni=ninfo, v=Verbose: field(x, ni, v)
+ if '\n' in name:
+ name = repr(name)
+ outlist = [name+':'] + filter(None, map(f, field_list))
+ if Verbose:
+ sep = '\n ' + prefix
+ else:
+ sep = ' '
+ return string.join(outlist, sep)
+
+nodeinfo_string = nodeinfo_cooked
+
+def printfield(name, entry, prefix=""):
+ outlist = field("implicit", entry, 0)
+ if outlist:
+ if Verbose:
+ print " implicit:"
+ print " " + outlist
+ outact = field("action", entry, 0)
+ if outact:
+ if Verbose:
+ print " action: " + outact
+ else:
+ print " " + outact
+
+def printentries(entries, location):
+ if Print_Entries:
+ for name in Print_Entries:
+ try:
+ entry = entries[name]
+ except KeyError:
+ sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location))
+ else:
+ try:
+ ninfo = entry.ninfo
+ except AttributeError:
+ print name + ":"
+ else:
+ print nodeinfo_string(name, entry.ninfo)
+ printfield(name, entry.binfo)
+ else:
+ names = entries.keys()
+ names.sort()
+ for name in names:
+ entry = entries[name]
+ try:
+ ninfo = entry.ninfo
+ except AttributeError:
+ print name + ":"
+ else:
+ print nodeinfo_string(name, entry.ninfo)
+ printfield(name, entry.binfo)
+
+class Do_SConsignDB:
+ def __init__(self, dbm_name, dbm):
+ self.dbm_name = dbm_name
+ self.dbm = dbm
+
+ def __call__(self, fname):
+ # The *dbm modules stick their own file suffixes on the names
+ # that are passed in. This is causes us to jump through some
+ # hoops here to be able to allow the user
+ try:
+ # Try opening the specified file name. Example:
+ # SPECIFIED OPENED BY self.dbm.open()
+ # --------- -------------------------
+ # .sconsign => .sconsign.dblite
+ # .sconsign.dblite => .sconsign.dblite.dblite
+ db = self.dbm.open(fname, "r")
+ except (IOError, OSError), e:
+ print_e = e
+ try:
+ # That didn't work, so try opening the base name,
+ # so that if the actually passed in 'sconsign.dblite'
+ # (for example), the dbm module will put the suffix back
+ # on for us and open it anyway.
+ db = self.dbm.open(os.path.splitext(fname)[0], "r")
+ except (IOError, OSError):
+ # That didn't work either. See if the file name
+ # they specified just exists (independent of the dbm
+ # suffix-mangling).
+ try:
+ open(fname, "r")
+ except (IOError, OSError), e:
+ # Nope, that file doesn't even exist, so report that
+ # fact back.
+ print_e = e
+ sys.stderr.write("sconsign: %s\n" % (print_e))
+ return
+ except KeyboardInterrupt:
+ raise
+ except cPickle.UnpicklingError:
+ sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname))
+ return
+ except Exception, e:
+ sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e))
+ return
+
+ if Print_Directories:
+ for dir in Print_Directories:
+ try:
+ val = db[dir]
+ except KeyError:
+ sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0]))
+ else:
+ self.printentries(dir, val)
+ else:
+ keys = db.keys()
+ keys.sort()
+ for dir in keys:
+ self.printentries(dir, db[dir])
+
+ def printentries(self, dir, val):
+ print '=== ' + dir + ':'
+ printentries(cPickle.loads(val), dir)
+
+def Do_SConsignDir(name):
+ try:
+ fp = open(name, 'rb')
+ except (IOError, OSError), e:
+ sys.stderr.write("sconsign: %s\n" % (e))
+ return
+ try:
+ sconsign = SCons.SConsign.Dir(fp)
+ except KeyboardInterrupt:
+ raise
+ except cPickle.UnpicklingError:
+ sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name))
+ return
+ except Exception, e:
+ sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e))
+ return
+ printentries(sconsign.entries, args[0])
+
+##############################################################################
+
+import getopt
+
+helpstr = """\
+Usage: sconsign [OPTIONS] FILE [...]
+Options:
+ -a, --act, --action Print build action information.
+ -c, --csig Print content signature information.
+ -d DIR, --dir=DIR Print only info about DIR.
+ -e ENTRY, --entry=ENTRY Print only info about ENTRY.
+ -f FORMAT, --format=FORMAT FILE is in the specified FORMAT.
+ -h, --help Print this message and exit.
+ -i, --implicit Print implicit dependency information.
+ -r, --readable Print timestamps in human-readable form.
+ --raw Print raw Python object representations.
+ -s, --size Print file sizes.
+ -t, --timestamp Print timestamp information.
+ -v, --verbose Verbose, describe each field.
+"""
+
+opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv",
+ ['act', 'action',
+ 'csig', 'dir=', 'entry=',
+ 'format=', 'help', 'implicit',
+ 'raw', 'readable',
+ 'size', 'timestamp', 'verbose'])
+
+
+for o, a in opts:
+ if o in ('-a', '--act', '--action'):
+ Print_Flags['action'] = 1
+ elif o in ('-c', '--csig'):
+ Print_Flags['csig'] = 1
+ elif o in ('-d', '--dir'):
+ Print_Directories.append(a)
+ elif o in ('-e', '--entry'):
+ Print_Entries.append(a)
+ elif o in ('-f', '--format'):
+ Module_Map = {'dblite' : 'SCons.dblite',
+ 'sconsign' : None}
+ dbm_name = Module_Map.get(a, a)
+ if dbm_name:
+ try:
+ dbm = my_import(dbm_name)
+ except:
+ sys.stderr.write("sconsign: illegal file format `%s'\n" % a)
+ print helpstr
+ sys.exit(2)
+ Do_Call = Do_SConsignDB(a, dbm)
+ else:
+ Do_Call = Do_SConsignDir
+ elif o in ('-h', '--help'):
+ print helpstr
+ sys.exit(0)
+ elif o in ('-i', '--implicit'):
+ Print_Flags['implicit'] = 1
+ elif o in ('--raw',):
+ nodeinfo_string = nodeinfo_raw
+ elif o in ('-r', '--readable'):
+ Readable = 1
+ elif o in ('-s', '--size'):
+ Print_Flags['size'] = 1
+ elif o in ('-t', '--timestamp'):
+ Print_Flags['timestamp'] = 1
+ elif o in ('-v', '--verbose'):
+ Verbose = 1
+
+if Do_Call:
+ for a in args:
+ Do_Call(a)
+else:
+ for a in args:
+ dbm_name = whichdb.whichdb(a)
+ if dbm_name:
+ Map_Module = {'SCons.dblite' : 'dblite'}
+ dbm = my_import(dbm_name)
+ Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a)
+ else:
+ Do_SConsignDir(a)
+
+sys.exit(0)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/script/setup.cfg b/src/script/setup.cfg
new file mode 100644
index 0000000..94ede1f
--- /dev/null
+++ b/src/script/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_rpm]
+group = Development/Tools
diff --git a/src/script/setup.py b/src/script/setup.py
new file mode 100644
index 0000000..4f958f6
--- /dev/null
+++ b/src/script/setup.py
@@ -0,0 +1,55 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/script/setup.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import sys
+
+(head, tail) = os.path.split(sys.argv[0])
+
+if head:
+ os.chdir(head)
+ sys.argv[0] = tail
+
+from distutils.core import setup
+
+setup(name = "scons-script",
+ version = "1.2.0.d20091224",
+ description = "an Open Source software construction tool script",
+ long_description = """SCons is an Open Source software construction tool--that is, a build tool; an
+improved substitute for the classic Make utility; a better way to build
+software.""",
+ author = "Steven Knight",
+ author_email = "knight@baldmt.com",
+ url = "http://www.scons.org/",
+ licence = "MIT, freely distributable",
+ keywords = "scons, cons, make, build tool, make tool",
+ scripts = ["scons"])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/setup.cfg b/src/setup.cfg
new file mode 100644
index 0000000..a234e22
--- /dev/null
+++ b/src/setup.cfg
@@ -0,0 +1,6 @@
+[bdist_rpm]
+group = Development/Tools
+
+[bdist_wininst]
+title = SCons - a software construction tool
+#install-script = scons-post-install.py
diff --git a/src/setup.py b/src/setup.py
new file mode 100644
index 0000000..f28b3cc
--- /dev/null
+++ b/src/setup.py
@@ -0,0 +1,427 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/setup.py 4577 2009/12/27 19:44:43 scons"
+
+import os
+import os.path
+import stat
+import string
+import sys
+
+Version = "1.2.0.d20091224"
+
+man_pages = [
+ 'scons.1',
+ 'sconsign.1',
+ 'scons-time.1',
+]
+
+(head, tail) = os.path.split(sys.argv[0])
+
+if head:
+ os.chdir(head)
+ sys.argv[0] = tail
+
+is_win32 = 0
+if not sys.platform == 'win32':
+ try:
+ if sys.argv[1] == 'bdist_wininst':
+ is_win32 = 1
+ except IndexError:
+ pass
+else:
+ is_win32 = 1
+
+try:
+ import distutils
+ import distutils.core
+ import distutils.command.install
+ import distutils.command.install_data
+ import distutils.command.install_lib
+ import distutils.command.install_scripts
+ import distutils.command.build_scripts
+except ImportError:
+ sys.stderr.write("""Could not import distutils.
+
+Building or installing SCons from this package requires that the Python
+distutils be installed. See the README or README.txt file from this
+package for instructions on where to find distutils for installation on
+your system, or on how to install SCons from a different package.
+""")
+ sys.exit(1)
+
+_install = distutils.command.install.install
+_install_data = distutils.command.install_data.install_data
+_install_lib = distutils.command.install_lib.install_lib
+_install_scripts = distutils.command.install_scripts.install_scripts
+_build_scripts = distutils.command.build_scripts.build_scripts
+
+class _options:
+ pass
+
+Options = _options()
+
+Installed = []
+
+def set_explicitly(name, args):
+ """
+ Return if the installation directory was set explicitly by the
+ user on the command line. This is complicated by the fact that
+ "install --install-lib=/foo" gets turned into "install_lib
+ --install-dir=/foo" internally.
+ """
+ if args[0] == "install_" + name:
+ s = "--install-dir="
+ else:
+ # The command is something else (usually "install")
+ s = "--install-%s=" % name
+ set = 0
+ length = len(s)
+ for a in args[1:]:
+ if a[:length] == s:
+ set = 1
+ break
+ return set
+
+class install(_install):
+ user_options = _install.user_options + [
+ ('no-scons-script', None,
+ "don't install 'scons', only install 'scons-%s'" % Version),
+ ('no-version-script', None,
+ "don't install 'scons-%s', only install 'scons'" % Version),
+ ('install-bat', None,
+ "install 'scons.bat' script"),
+ ('no-install-bat', None,
+ "do not install 'scons.bat' script"),
+ ('install-man', None,
+ "install SCons man pages"),
+ ('no-install-man', None,
+ "do not install SCons man pages"),
+ ('standard-lib', None,
+ "install SCons library in standard Python location"),
+ ('standalone-lib', None,
+ "install SCons library in separate standalone directory"),
+ ('version-lib', None,
+ "install SCons library in version-numbered directory"),
+ ]
+ boolean_options = _install.boolean_options + [
+ 'no-scons-script',
+ 'no-version-script',
+ 'install-bat',
+ 'no-install-bat',
+ 'install-man',
+ 'no-install-man',
+ 'standard-lib',
+ 'standalone-lib',
+ 'version-lib'
+ ]
+
+ if hasattr(os, 'symlink'):
+ user_options.append(
+ ('hardlink-scons', None,
+ "hard link 'scons' to the version-numbered script, don't make a separate 'scons' copy"),
+ )
+ boolean_options.append('hardlink-script')
+
+ if hasattr(os, 'symlink'):
+ user_options.append(
+ ('symlink-scons', None,
+ "make 'scons' a symbolic link to the version-numbered script, don't make a separate 'scons' copy"),
+ )
+ boolean_options.append('symlink-script')
+
+ def initialize_options(self):
+ _install.initialize_options(self)
+ self.no_scons_script = 0
+ self.no_version_script = 0
+ self.install_bat = 0
+ self.no_install_bat = not is_win32
+ self.install_man = 0
+ self.no_install_man = is_win32
+ self.standard_lib = 0
+ self.standalone_lib = 0
+ self.version_lib = 0
+ self.hardlink_scons = 0
+ self.symlink_scons = 0
+ # Don't warn about having to put the library directory in the
+ # search path.
+ self.warn_dir = 0
+
+ def finalize_options(self):
+ _install.finalize_options(self)
+ if self.install_bat:
+ Options.install_bat = 1
+ else:
+ Options.install_bat = not self.no_install_bat
+ if self.install_man:
+ Options.install_man = 1
+ else:
+ Options.install_man = not self.no_install_man
+ Options.standard_lib = self.standard_lib
+ Options.standalone_lib = self.standalone_lib
+ Options.version_lib = self.version_lib
+ Options.install_scons_script = not self.no_scons_script
+ Options.install_version_script = not self.no_version_script
+ Options.hardlink_scons = self.hardlink_scons
+ Options.symlink_scons = self.symlink_scons
+
+def get_scons_prefix(libdir, is_win32):
+ """
+ Return the right prefix for SCons library installation. Find
+ this by starting with the library installation directory
+ (.../site-packages, most likely) and crawling back up until we reach
+ a directory name beginning with "python" (or "Python").
+ """
+ drive, head = os.path.splitdrive(libdir)
+ while head:
+ if head == os.sep:
+ break
+ head, tail = os.path.split(head)
+ if string.lower(tail)[:6] == "python":
+ # Found the Python library directory...
+ if is_win32:
+ # ...on Win32 systems, "scons" goes in the directory:
+ # C:\PythonXX => C:\PythonXX\scons
+ return os.path.join(drive + head, tail)
+ else:
+ # ...on other systems, "scons" goes above the directory:
+ # /usr/lib/pythonX.X => /usr/lib/scons
+ return os.path.join(drive + head)
+ return libdir
+
+def force_to_usr_local(self):
+ """
+ A hack to decide if we need to "force" the installation directories
+ to be under /usr/local. This is because Mac Os X Tiger and
+ Leopard, by default, put the libraries and scripts in their own
+ directories under /Library or /System/Library.
+ """
+ return (sys.platform[:6] == 'darwin' and
+ (self.install_dir[:9] == '/Library/' or
+ self.install_dir[:16] == '/System/Library/'))
+
+class install_lib(_install_lib):
+ def finalize_options(self):
+ _install_lib.finalize_options(self)
+ if force_to_usr_local(self):
+ self.install_dir = '/usr/local/lib'
+ args = self.distribution.script_args
+ if not set_explicitly("lib", args):
+ # They didn't explicitly specify the installation
+ # directory for libraries...
+ is_win32 = sys.platform == "win32" or args[0] == 'bdist_wininst'
+ prefix = get_scons_prefix(self.install_dir, is_win32)
+ if Options.standalone_lib:
+ # ...but they asked for a standalone directory.
+ self.install_dir = os.path.join(prefix, "scons")
+ elif Options.version_lib or not Options.standard_lib:
+ # ...they asked for a version-specific directory,
+ # or they get it by default.
+ self.install_dir = os.path.join(prefix, "scons-%s" % Version)
+
+ msg = "Installed SCons library modules into %s" % self.install_dir
+ Installed.append(msg)
+
+class install_scripts(_install_scripts):
+ def finalize_options(self):
+ _install_scripts.finalize_options(self)
+ if force_to_usr_local(self):
+ self.install_dir = '/usr/local/bin'
+ self.build_dir = os.path.join('build', 'scripts')
+ msg = "Installed SCons scripts into %s" % self.install_dir
+ Installed.append(msg)
+
+ def do_nothing(self, *args, **kw):
+ pass
+
+ def hardlink_scons(self, src, dst, ver):
+ try: os.unlink(dst)
+ except OSError: pass
+ os.link(ver, dst)
+
+ def symlink_scons(self, src, dst, ver):
+ try: os.unlink(dst)
+ except OSError: pass
+ os.symlink(os.path.split(ver)[1], dst)
+
+ def copy_scons(self, src, dst, *args):
+ try: os.unlink(dst)
+ except OSError: pass
+ self.copy_file(src, dst)
+ self.outfiles.append(dst)
+
+ def report(self, msg, args):
+ # Wrapper around self.announce, used by older distutils versions.
+ self.announce(msg % args)
+
+ def run(self):
+ # This "skip_build/build_scripts" block is cut-and-paste from
+ # distutils.
+ if not self.skip_build:
+ self.run_command('build_scripts')
+
+ # Custom SCons installation stuff.
+ if Options.hardlink_scons:
+ create_basename_script = self.hardlink_scons
+ elif Options.symlink_scons:
+ create_basename_script = self.symlink_scons
+ elif Options.install_scons_script:
+ create_basename_script = self.copy_scons
+ else:
+ create_basename_script = self.do_nothing
+
+ if Options.install_version_script:
+ create_version_script = self.copy_scons
+ else:
+ create_version_script = self.do_nothing
+
+ inputs = self.get_inputs()
+ bat_scripts = filter(lambda x: x[-4:] == '.bat', inputs)
+ non_bat_scripts = filter(lambda x: x[-4:] != '.bat', inputs)
+
+ self.outfiles = []
+ self.mkpath(self.install_dir)
+
+ for src in non_bat_scripts:
+ base = os.path.basename(src)
+ scons = os.path.join(self.install_dir, base)
+ scons_ver = scons + '-' + Version
+ create_version_script(src, scons_ver)
+ create_basename_script(src, scons, scons_ver)
+
+ if Options.install_bat:
+ if is_win32:
+ bat_install_dir = get_scons_prefix(self.install_dir, is_win32)
+ else:
+ bat_install_dir = self.install_dir
+ for src in bat_scripts:
+ scons_bat = os.path.join(bat_install_dir, 'scons.bat')
+ scons_version_bat = os.path.join(bat_install_dir,
+ 'scons-' + Version + '.bat')
+ self.copy_scons(src, scons_bat)
+ self.copy_scons(src, scons_version_bat)
+
+ # This section is cut-and-paste from distutils, modulo being
+ # able
+ if os.name == 'posix':
+ try: report = distutils.log.info
+ except AttributeError: report = self.report
+ # Set the executable bits (owner, group, and world) on
+ # all the scripts we just installed.
+ for file in self.get_outputs():
+ if self.dry_run:
+ report("changing mode of %s", file)
+ else:
+ mode = ((os.stat(file)[stat.ST_MODE]) | 0555) & 07777
+ report("changing mode of %s", file)
+ os.chmod(file, mode)
+
+class build_scripts(_build_scripts):
+ def finalize_options(self):
+ _build_scripts.finalize_options(self)
+ self.build_dir = os.path.join('build', 'scripts')
+
+class install_data(_install_data):
+ def initialize_options(self):
+ _install_data.initialize_options(self)
+ def finalize_options(self):
+ _install_data.finalize_options(self)
+ if force_to_usr_local(self):
+ self.install_dir = '/usr/local'
+ if Options.install_man:
+ if is_win32:
+ dir = 'Doc'
+ else:
+ dir = os.path.join('man', 'man1')
+ self.data_files = [(dir, man_pages)]
+ man_dir = os.path.join(self.install_dir, dir)
+ msg = "Installed SCons man pages into %s" % man_dir
+ Installed.append(msg)
+ else:
+ self.data_files = []
+
+description = "Open Source next-generation build tool."
+
+long_description = """Open Source next-generation build tool.
+Improved, cross-platform substitute for the classic Make
+utility. In short, SCons is an easier, more reliable
+and faster way to build software."""
+
+scripts = [
+ 'script/scons',
+ 'script/sconsign',
+ 'script/scons-time',
+
+ # We include scons.bat in the list of scripts, even on UNIX systems,
+ # because we provide an option to allow it be installed explicitly,
+ # for example if you're installing from UNIX on a share that's
+ # accessible to Windows and you want the scons.bat.
+ 'script/scons.bat',
+]
+
+#if is_win32:
+# scripts = scripts + [
+# 'script/scons-post-install.py'
+# ]
+
+arguments = {
+ 'name' : "scons",
+ 'version' : Version,
+ 'description' : description,
+ 'long_description' : long_description,
+ 'author' : 'Steven Knight',
+ 'author_email' : 'knight@baldmt.com',
+ 'url' : "http://www.scons.org/",
+ 'packages' : ["SCons",
+ "SCons.compat",
+ "SCons.Node",
+ "SCons.Options",
+ "SCons.Platform",
+ "SCons.Scanner",
+ "SCons.Script",
+ "SCons.Tool",
+ "SCons.Tool.MSCommon",
+ "SCons.Tool.packaging",
+ "SCons.Variables",
+ ],
+ 'package_dir' : {'' : 'engine'},
+ 'data_files' : [('man/man1', man_pages)],
+ 'scripts' : scripts,
+ 'cmdclass' : {'install' : install,
+ 'install_lib' : install_lib,
+ 'install_data' : install_data,
+ 'install_scripts' : install_scripts,
+ 'build_scripts' : build_scripts}
+}
+
+apply(distutils.core.setup, (), arguments)
+
+if Installed:
+ print string.join(Installed, '\n')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/test_aegistests.py b/src/test_aegistests.py
new file mode 100644
index 0000000..7ba150d
--- /dev/null
+++ b/src/test_aegistests.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/test_aegistests.py 4577 2009/12/27 19:44:43 scons"
+
+"""
+Verify that we have proper Copyright notices on all the right files
+in our distributions.
+
+Note that this is a packaging test, not a functional test, so the
+name of this script doesn't end in *Tests.py.
+"""
+
+import os
+import popen2
+import re
+import string
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+try:
+ popen2.Popen3
+except AttributeError:
+ def get_stdout(command):
+ (tochild, fromchild, childerr) = os.popen3(command)
+ tochild.close()
+ return fromchild.read()
+else:
+ def get_stdout(command):
+ p = popen2.Popen3(command, 1)
+ p.tochild.close()
+ return p.fromchild.read()
+
+output = get_stdout('aegis -list -unformatted pf') +\
+ get_stdout('aegis -list -unformatted cf')
+lines = string.split(output, '\n')[:-1]
+sources = filter(lambda x: x[:7] == 'source ', lines)
+
+re1 = re.compile(r' src/.*Tests\.py')
+re2 = re.compile(r' src/test_.*\.py')
+re3 = re.compile(r' test/.*\.py')
+
+def filename_is_a_test(x):
+ return re1.search(x) or re2.search(x) or re3.search(x)
+
+test_files = filter(filename_is_a_test, sources)
+
+if test_files:
+ sys.stderr.write("Found the following files with test names not marked as Aegis tests:\n")
+ sys.stderr.write('\t' + string.join(test_files, '\n\t') + '\n')
+ test.fail_test(1)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/test_files.py b/src/test_files.py
new file mode 100644
index 0000000..cea02fd
--- /dev/null
+++ b/src/test_files.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/test_files.py 4577 2009/12/27 19:44:43 scons"
+
+"""
+Verify that we have certain important files in our distribution
+packages.
+
+Note that this is a packaging test, not a functional test, so the
+name of this script doesn't end in *Tests.py.
+"""
+
+import os
+import os.path
+import re
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ cwd = os.getcwd()
+
+def build_path(*args):
+ return apply(os.path.join, (cwd, 'build',) + args)
+
+build_scons_tar_gz = build_path('unpack-tar-gz', 'scons-'+test.scons_version)
+build_scons_zip = build_path('unpack-zip', 'scons-'+test.scons_version)
+build_local_tar_gz = build_path('test-local-tar-gz')
+build_local_zip = build_path('test-local-zip')
+
+scons_files = [
+ 'CHANGES.txt',
+ 'LICENSE.txt',
+ 'README.txt',
+ 'RELEASE.txt',
+]
+
+local_files = [
+ 'scons-LICENSE',
+ 'scons-README',
+]
+
+# Map each directory to search (dictionary keys) to a list of its
+# subsidiary files and directories to exclude from copyright checks.
+check = {
+ build_scons_tar_gz : scons_files,
+ build_scons_zip : scons_files,
+ build_local_tar_gz : local_files,
+ build_local_zip : local_files,
+}
+
+missing = []
+no_result = []
+
+for directory, check_list in check.items():
+ if os.path.exists(directory):
+ for c in check_list:
+ f = os.path.join(directory, c)
+ if not os.path.isfile(f):
+ missing.append(f)
+ else:
+ no_result.append(directory)
+
+if missing:
+ print "Missing the following files:\n"
+ print "\t" + string.join(missing, "\n\t")
+ test.fail_test(1)
+
+if no_result:
+ print "Cannot check files, the following have apparently not been built:"
+ print "\t" + string.join(no_result, "\n\t")
+ test.no_result(1)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/test_interrupts.py b/src/test_interrupts.py
new file mode 100644
index 0000000..c7171f7
--- /dev/null
+++ b/src/test_interrupts.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/test_interrupts.py 4577 2009/12/27 19:44:43 scons"
+
+"""
+Verify that the SCons source code contains only correct handling of
+keyboard interrupts (e.g. Ctrl-C).
+"""
+
+import os
+import os.path
+import re
+import string
+import time
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+# We do not want statements of the form:
+# try:
+# # do something, e.g.
+# return a['x']
+# except:
+# # do the exception handling
+# a['x'] = getx()
+# return a['x']
+#
+# The code above may catch a KeyboardInterrupt exception, which was not
+# intended by the programmer. We check for these situations in all python
+# source files.
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ scons_lib_dir = os.environ['SCONS_LIB_DIR']
+ MANIFEST = os.path.join(scons_lib_dir, 'MANIFEST.in')
+else:
+ #cwd = os.getcwd()
+ scons_lib_dir = os.path.join(cwd, 'build', 'scons')
+ MANIFEST = os.path.join(scons_lib_dir, 'MANIFEST')
+
+# We expect precisely this many uncaught KeyboardInterrupt exceptions
+# from the files in the following dictionary.
+
+expected_uncaught = {
+ 'engine/SCons/Job.py' : 5,
+ 'engine/SCons/Script/Main.py' : 1,
+ 'engine/SCons/Taskmaster.py' : 3,
+}
+
+try:
+ fp = open(MANIFEST)
+except IOError:
+ test.skip_test('%s does not exist; skipping test.\n' % MANIFEST)
+else:
+ files = string.split(fp.read())
+ files = filter(lambda f: f[-3:] == '.py', files)
+
+# some regexps to parse the python files
+tryexc_pat = re.compile(
+r'^(?P<try_or_except>(?P<indent> *)(try|except)( [^\n]*)?:.*)',re.MULTILINE)
+keyboardint_pat = re.compile(r' *except +([^,],)*KeyboardInterrupt([ ,][^\n]*)?:[^\n]*')
+exceptall_pat = re.compile(r' *except(?: *| +Exception *, *[^: ]+):[^\n]*')
+
+uncaughtKeyboardInterrupt = 0
+for f in files:
+ contents = open(os.path.join(scons_lib_dir, f)).read()
+ try_except_lines = {}
+ lastend = 0
+ while 1:
+ match = tryexc_pat.search( contents, lastend )
+ if match is None:
+ break
+ #print match.groups()
+ lastend = match.end()
+ try:
+ indent_list = try_except_lines[match.group('indent')]
+ except:
+ indent_list = []
+ line_num = 1 + string.count(contents[:match.start()], '\n')
+ indent_list.append( (line_num, match.group('try_or_except') ) )
+ try_except_lines[match.group('indent')] = indent_list
+ uncaught_this_file = []
+ for indent in try_except_lines.keys():
+ exc_keyboardint_seen = 0
+ exc_all_seen = 0
+ for (l,statement) in try_except_lines[indent] + [(-1,indent + 'try')]:
+ #print "%4d %s" % (l,statement),
+ m1 = keyboardint_pat.match(statement)
+ m2 = exceptall_pat.match(statement)
+ if string.find(statement, indent + 'try') == 0:
+ if exc_all_seen and not exc_keyboardint_seen:
+ uncaught_this_file.append(line)
+ exc_keyboardint_seen = 0
+ exc_all_seen = 0
+ line = l
+ #print " -> reset"
+ elif m1 is not None:
+ exc_keyboardint_seen = 1
+ #print " -> keyboard -> ", m1.groups()
+ elif m2 is not None:
+ exc_all_seen = 1
+ #print " -> all -> ", m2.groups()
+ else:
+ pass
+ #print "Warning: unknown statement %s" % statement
+ expected_num = expected_uncaught.get(f, 0)
+ if expected_num != len(uncaught_this_file):
+ uncaughtKeyboardInterrupt = 1
+ msg = "%s: expected %d uncaught interrupts, got %d:"
+ print msg % (f, expected_num, len(uncaught_this_file))
+ for line in uncaught_this_file:
+ print " File %s:%d: Uncaught KeyboardInterrupt!" % (f,line)
+
+test.fail_test(uncaughtKeyboardInterrupt)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/test_pychecker.py b/src/test_pychecker.py
new file mode 100644
index 0000000..92b12e4
--- /dev/null
+++ b/src/test_pychecker.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/test_pychecker.py 4577 2009/12/27 19:44:43 scons"
+
+"""
+Use pychecker to catch various Python coding errors.
+"""
+
+import os
+import os.path
+import string
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_test('Not ready for clean pychecker output; skipping test.\n')
+
+try:
+ import pychecker
+except ImportError:
+ pychecker = test.where_is('pychecker')
+ if not pychecker:
+ test.skip_test("Could not find 'pychecker'; skipping test(s).\n")
+ program = pychecker
+ default_arguments = []
+else:
+ pychecker = os.path.join(os.path.split(pychecker.__file__)[0], 'checker.py')
+ program = sys.executable
+ default_arguments = [pychecker]
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ src_engine = os.environ['SCONS_LIB_DIR']
+else:
+ src_engine = os.path.join(cwd, 'build', 'scons-src', 'src', 'engine')
+ if not os.path.exists(src_engine):
+ src_engine = os.path.join(cwd, 'src', 'engine')
+
+src_engine_ = os.path.join(src_engine, '')
+
+MANIFEST = os.path.join(src_engine, 'MANIFEST.in')
+files = string.split(open(MANIFEST).read())
+
+files = filter(lambda f: f[-3:] == '.py', files)
+
+ignore = [
+ 'SCons/compat/__init__.py',
+ 'SCons/compat/_scons_UserString.py',
+ 'SCons/compat/_scons_hashlib.py',
+ 'SCons/compat/_scons_itertools.py',
+ 'SCons/compat/_scons_optparse.py',
+ 'SCons/compat/_scons_sets.py',
+ 'SCons/compat/_scons_sets15.py',
+ 'SCons/compat/_scons_shlex.py',
+ 'SCons/compat/_scons_subprocess.py',
+ 'SCons/compat/_scons_textwrap.py',
+ 'SCons/compat/builtins.py',
+]
+
+u = {}
+for file in files:
+ u[file] = 1
+for file in ignore:
+ try:
+ del u[file]
+ except KeyError:
+ pass
+
+files = u.keys()
+
+files.sort()
+
+mismatches = []
+
+default_arguments.extend([
+ '--quiet',
+ '--limit=1000',
+ # Suppress warnings about unused arguments to functions and methods.
+ # We have too many wrapper functions that intentionally only use some
+ # of their arguments.
+ '--no-argsused',
+])
+
+if sys.platform == 'win32':
+ default_arguments.extend([
+ '--blacklist', '"pywintypes,pywintypes.error"',
+ ])
+
+per_file_arguments = {
+ #'SCons/__init__.py' : [
+ # '--varlist',
+ # '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"',
+ #],
+}
+
+pywintypes_warning = "warning: couldn't find real module for class pywintypes.error (module name: pywintypes)\n"
+
+os.environ['PYTHONPATH'] = src_engine
+
+for file in files:
+
+ args = (default_arguments +
+ per_file_arguments.get(file, []) +
+ [os.path.join(src_engine, file)])
+
+ test.run(program=program, arguments=args, status=None, stderr=None)
+
+ stdout = test.stdout()
+ stdout = string.replace(stdout, src_engine_, '')
+
+ stderr = test.stderr()
+ stderr = string.replace(stderr, src_engine_, '')
+ stderr = string.replace(stderr, pywintypes_warning, '')
+
+ if test.status or stdout or stderr:
+ mismatches.append('\n')
+ mismatches.append(string.join([program] + args) + '\n')
+
+ mismatches.append('STDOUT =====================================\n')
+ mismatches.append(stdout)
+
+ if stderr:
+ mismatches.append('STDERR =====================================\n')
+ mismatches.append(stderr)
+
+if mismatches:
+ print string.join(mismatches[1:], '')
+ test.fail_test()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/test_setup.py b/src/test_setup.py
new file mode 100644
index 0000000..40ea464
--- /dev/null
+++ b/src/test_setup.py
@@ -0,0 +1,335 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/test_setup.py 4577 2009/12/27 19:44:43 scons"
+
+"""
+Test how the setup.py script installs SCons.
+
+Note that this is an installation test, not a functional test, so the
+name of this script doesn't end in *Tests.py.
+"""
+
+import os
+import os.path
+import shutil
+import string
+import sys
+
+try: WindowsError
+except NameError: WindowsError = OSError
+
+import TestSCons
+
+version = TestSCons.TestSCons.scons_version
+
+scons_version = 'scons-%s' % version
+
+python = TestSCons.python
+
+class MyTestSCons(TestSCons.TestSCons):
+
+ _lib_modules = [
+ # A representative smattering of build engine modules.
+ '__init__.py',
+ 'Action.py',
+ 'Builder.py',
+ 'Environment.py',
+ 'Util.py',
+ ]
+
+ _base_scripts = [
+ 'scons',
+ 'sconsign',
+ ]
+
+ _version_scripts = [
+ 'scons-%s' % version,
+ 'sconsign-%s' % version,
+ ]
+
+ _bat_scripts = [
+ 'scons.bat',
+ ]
+
+ _bat_version_scripts = [
+ 'scons-%s.bat' % version,
+ ]
+
+ _man_pages = [
+ 'scons.1',
+ 'sconsign.1',
+ ]
+
+ def __init__(self):
+ TestSCons.TestSCons.__init__(self)
+ self.root = self.workpath('root')
+ self.prefix = self.root + os.path.splitdrive(sys.prefix)[1]
+
+ if sys.platform == 'win32':
+ self.bin_dir = os.path.join(self.prefix, 'Scripts')
+ self.bat_dir = self.prefix
+ self.standalone_lib = os.path.join(self.prefix, 'scons')
+ self.standard_lib = os.path.join(self.prefix,
+ 'Lib',
+ 'site-packages',
+ '')
+ self.version_lib = os.path.join(self.prefix, scons_version)
+ self.man_dir = os.path.join(self.prefix, 'Doc')
+ else:
+ self.bin_dir = os.path.join(self.prefix, 'bin')
+ self.bat_dir = self.bin_dir
+ self.lib_dir = os.path.join(self.prefix, 'lib')
+ self.standalone_lib = os.path.join(self.lib_dir, 'scons')
+ self.standard_lib = os.path.join(self.lib_dir,
+ 'python%s' % sys.version[:3],
+ 'site-packages',
+ '')
+ self.version_lib = os.path.join(self.lib_dir, scons_version)
+ self.man_dir = os.path.join(self.prefix, 'man', 'man1')
+
+ self.prepend_bin_dir = lambda p, d=self.bin_dir: os.path.join(d, p)
+ self.prepend_bat_dir = lambda p, d=self.bat_dir: os.path.join(d, p)
+ self.prepend_man_dir = lambda p, d=self.man_dir: os.path.join(d, p)
+
+ def run(self, *args, **kw):
+ kw['chdir'] = scons_version
+ kw['program'] = python
+ kw['stderr'] = None
+ return apply(TestSCons.TestSCons.run, (self,)+args, kw)
+
+ def remove(self, dir):
+ try: shutil.rmtree(dir)
+ except (OSError, WindowsError): pass
+
+ def stdout_lines(self):
+ return string.split(self.stdout(), '\n')
+
+
+ def lib_line(self, lib):
+ return 'Installed SCons library modules into %s' % lib
+
+ def lib_paths(self, lib_dir):
+ prepend_lib_dir = lambda p, d=lib_dir: os.path.join(d, 'SCons', p)
+ return map(prepend_lib_dir, self._lib_modules)
+
+ def scripts_line(self):
+ return 'Installed SCons scripts into %s' % self.bin_dir
+
+ def base_script_paths(self):
+ scripts = self._base_scripts
+ return map(self.prepend_bin_dir, scripts)
+
+ def version_script_paths(self):
+ scripts = self._version_scripts
+ return map(self.prepend_bin_dir, scripts)
+
+ def bat_script_paths(self):
+ scripts = self._bat_scripts + self._bat_version_scripts
+ return map(self.prepend_bat_dir, scripts)
+
+ def man_page_line(self):
+ return 'Installed SCons man pages into %s' % self.man_dir
+
+ def man_page_paths(self):
+ return map(self.prepend_man_dir, self._man_pages)
+
+
+ def must_have_installed(self, paths):
+ for p in paths:
+ self.must_exist(p)
+
+ def must_not_have_installed(self, paths):
+ for p in paths:
+ self.must_not_exist(p)
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ cwd = os.getcwd()
+
+test = MyTestSCons()
+
+test.subdir(test.root)
+
+tar_gz = os.path.join(cwd, 'build', 'dist', '%s.tar.gz' % scons_version)
+zip = os.path.join(cwd, 'build', 'dist', '%s.zip' % scons_version)
+
+if os.path.isfile(zip):
+ try: import zipfile
+ except ImportError: pass
+ else:
+ zf = zipfile.ZipFile(zip, 'r')
+
+ for name in zf.namelist():
+ dir = os.path.dirname(name)
+ try: os.makedirs(dir)
+ except: pass
+ # if the file exists, then delete it before writing
+ # to it so that we don't end up trying to write to a symlink:
+ if os.path.isfile(name) or os.path.islink(name):
+ os.unlink(name)
+ if not os.path.isdir(name):
+ open(name, 'w').write(zf.read(name))
+
+if not os.path.isdir(scons_version) and os.path.isfile(tar_gz):
+ # Unpack the .tar.gz file. This should create the scons_version/
+ # subdirectory from which we execute the setup.py script therein.
+ os.system("gunzip -c %s | tar xf -" % tar_gz)
+
+if not os.path.isdir(scons_version):
+ print "Cannot test package installation, found none of the following packages:"
+ print "\t" + tar_gz
+ print "\t" + zip
+ test.no_result(1)
+
+# Verify that a virgin installation installs the version library,
+# the scripts and (on UNIX/Linux systems) the man pages.
+test.run(arguments = 'setup.py install --root=%s' % test.root)
+test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines())
+test.must_have_installed(test.lib_paths(test.version_lib))
+
+# Verify that --standard-lib installs into the Python standard library.
+test.run(arguments = 'setup.py install --root=%s --standard-lib' % test.root)
+test.fail_test(not test.lib_line(test.standard_lib) in test.stdout_lines())
+test.must_have_installed(test.lib_paths(test.standard_lib))
+
+# Verify that --standalone-lib installs the standalone library.
+test.run(arguments = 'setup.py install --root=%s --standalone-lib' % test.root)
+test.fail_test(not test.lib_line(test.standalone_lib) in test.stdout_lines())
+test.must_have_installed(test.lib_paths(test.standalone_lib))
+
+# Verify that --version-lib installs into a version-specific library directory.
+test.run(arguments = 'setup.py install --root=%s --version-lib' % test.root)
+test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines())
+
+# Now that all of the libraries are in place,
+# verify that a default installation still installs the version library.
+test.run(arguments = 'setup.py install --root=%s' % test.root)
+test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines())
+
+test.remove(test.version_lib)
+
+# Now with only the standard and standalone libraries in place,
+# verify that a default installation still installs the version library.
+test.run(arguments = 'setup.py install --root=%s' % test.root)
+test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines())
+
+test.remove(test.version_lib)
+test.remove(test.standalone_lib)
+
+# Now with only the standard libraries in place,
+# verify that a default installation still installs the version library.
+test.run(arguments = 'setup.py install --root=%s' % test.root)
+test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines())
+
+
+
+#
+test.run(arguments = 'setup.py install --root=%s' % test.root)
+test.fail_test(not test.scripts_line() in test.stdout_lines())
+if sys.platform == 'win32':
+ test.must_have_installed(test.base_script_paths())
+ test.must_have_installed(test.version_script_paths())
+ test.must_have_installed(test.bat_script_paths())
+else:
+ test.must_have_installed(test.base_script_paths())
+ test.must_have_installed(test.version_script_paths())
+ test.must_not_have_installed(test.bat_script_paths())
+
+test.remove(test.prefix)
+
+test.run(arguments = 'setup.py install --root=%s --no-install-bat' % test.root)
+test.fail_test(not test.scripts_line() in test.stdout_lines())
+test.must_have_installed(test.base_script_paths())
+test.must_have_installed(test.version_script_paths())
+test.must_not_have_installed(test.bat_script_paths())
+
+test.remove(test.prefix)
+
+test.run(arguments = 'setup.py install --root=%s --install-bat' % test.root)
+test.fail_test(not test.scripts_line() in test.stdout_lines())
+test.must_have_installed(test.base_script_paths())
+test.must_have_installed(test.version_script_paths())
+test.must_have_installed(test.bat_script_paths())
+
+test.remove(test.prefix)
+
+test.run(arguments = 'setup.py install --root=%s --no-scons-script' % test.root)
+test.fail_test(not test.scripts_line() in test.stdout_lines())
+test.must_not_have_installed(test.base_script_paths())
+test.must_have_installed(test.version_script_paths())
+# Doesn't matter whether we installed the .bat scripts or not.
+
+test.remove(test.prefix)
+
+test.run(arguments = 'setup.py install --root=%s --no-version-script' % test.root)
+test.fail_test(not test.scripts_line() in test.stdout_lines())
+test.must_have_installed(test.base_script_paths())
+test.must_not_have_installed(test.version_script_paths())
+# Doesn't matter whether we installed the .bat scripts or not.
+
+
+
+test.remove(test.man_dir)
+
+test.run(arguments = 'setup.py install --root=%s' % test.root)
+if sys.platform == 'win32':
+ test.fail_test(test.man_page_line() in test.stdout_lines())
+ test.must_not_have_installed(test.man_page_paths())
+else:
+ test.fail_test(not test.man_page_line() in test.stdout_lines())
+ test.must_have_installed(test.man_page_paths())
+
+test.remove(test.man_dir)
+
+test.run(arguments = 'setup.py install --root=%s --no-install-man' % test.root)
+test.fail_test(test.man_page_line() in test.stdout_lines())
+test.must_not_have_installed(test.man_page_paths())
+
+test.remove(test.man_dir)
+
+test.run(arguments = 'setup.py install --root=%s --install-man' % test.root)
+test.fail_test(not test.man_page_line() in test.stdout_lines())
+test.must_have_installed(test.man_page_paths())
+
+
+
+# Verify that we don't warn about the directory in which we've
+# installed the modules when using a non-standard prefix.
+other_prefix = test.workpath('other-prefix')
+test.subdir(other_prefix)
+test.run(arguments = 'setup.py install --prefix=%s' % other_prefix)
+test.fail_test(string.find(test.stderr(),
+ "you'll have to change the search path yourself")
+ != -1)
+
+# All done.
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/test_strings.py b/src/test_strings.py
new file mode 100644
index 0000000..b11c555
--- /dev/null
+++ b/src/test_strings.py
@@ -0,0 +1,280 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/test_strings.py 4577 2009/12/27 19:44:43 scons"
+
+"""
+Verify that we have proper strings like Copyright notices on all the
+right files in our distributions.
+
+Note that this is a source file and packaging test, not a functional test,
+so the name of this script doesn't end in *Tests.py.
+"""
+
+import fnmatch
+import os
+import os.path
+import re
+import string
+
+import TestCmd
+import TestSCons
+
+# Use TestCmd, not TestSCons, so we don't chdir to a temporary directory.
+test = TestCmd.TestCmd()
+
+scons_version = TestSCons.SConsVersion
+
+def build_path(*args):
+ return apply(os.path.join, ('build',)+args)
+
+build_scons = build_path('scons')
+build_local = build_path('scons-local', 'scons-local-'+scons_version)
+build_src = build_path('scons-src')
+
+class Checker:
+ def __init__(self, directory,
+ search_list = [],
+ remove_list = [],
+ remove_patterns = []):
+ self.directory = directory
+ self.search_list = search_list
+ self.remove_dict = {}
+ for r in remove_list:
+ self.remove_dict[os.path.join(directory, r)] = 1
+ self.remove_patterns = remove_patterns
+
+ def directory_exists(self):
+ return os.path.exists(self.directory)
+
+ def remove_this(self, name, path):
+ if self.remove_dict.get(path):
+ return 1
+ else:
+ for pattern in self.remove_patterns:
+ if fnmatch.fnmatch(name, pattern):
+ return 1
+ return 0
+
+ def search_this(self, path):
+ if self.search_list:
+ for pattern in self.search_list:
+ if fnmatch.fnmatch(path, pattern):
+ return 1
+ return None
+ else:
+ return os.path.isfile(path)
+
+ def visit(self, result, dirname, names):
+ make_path_tuple = lambda n, d=dirname: (n, os.path.join(d, n))
+ for name, path in map(make_path_tuple, names):
+ if self.remove_this(name, path):
+ names.remove(name)
+ elif self.search_this(path):
+ body = open(path, 'r').read()
+ for expr in self.expressions:
+ if not expr.search(body):
+ msg = '%s: missing %s' % (path, repr(expr.pattern))
+ result.append(msg)
+
+ def find_missing(self):
+ result = []
+ os.path.walk(self.directory, self.visit, result)
+ return result
+
+class CheckUnexpandedStrings(Checker):
+ expressions = [
+ re.compile('Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation'),
+ re.compile('src/test_strings.py 4577 2009/12/27 19:44:43 scons'),
+ ]
+ def must_be_built(self):
+ return None
+
+class CheckPassTest(Checker):
+ expressions = [
+ re.compile(r'\.pass_test()'),
+ ]
+ def must_be_built(self):
+ return None
+
+class CheckExpandedCopyright(Checker):
+ expressions = [
+ re.compile('Copyright.*The SCons Foundation'),
+ ]
+ def must_be_built(self):
+ return 1
+
+check_list = [
+
+ CheckUnexpandedStrings(
+ 'src',
+ search_list = [ '*.py' ],
+ remove_list = [
+ 'engine/SCons/compat/_scons_optparse.py',
+ 'engine/SCons/compat/_scons_sets.py',
+ 'engine/SCons/compat/_scons_sets15.py',
+ 'engine/SCons/compat/_scons_shlex.py',
+ 'engine/SCons/compat/_scons_subprocess.py',
+ 'engine/SCons/compat/_scons_textwrap.py',
+ 'engine/SCons/Conftest.py',
+ 'engine/SCons/dblite.py',
+ ],
+ ),
+
+ CheckUnexpandedStrings(
+ 'test',
+ search_list = [ '*.py' ],
+ ),
+
+ CheckPassTest(
+ 'test',
+ search_list = [ '*.py' ],
+ remove_list = [
+ 'Fortran/common.py',
+ ],
+ ),
+
+ CheckExpandedCopyright(
+ build_scons,
+ remove_list = [
+ 'build',
+ 'build-stamp',
+ 'configure-stamp',
+ 'debian',
+ 'dist',
+ 'gentoo',
+ 'engine/SCons/compat/_scons_optparse.py',
+ 'engine/SCons/compat/_scons_sets.py',
+ 'engine/SCons/compat/_scons_sets15.py',
+ 'engine/SCons/compat/_scons_shlex.py',
+ 'engine/SCons/compat/_scons_subprocess.py',
+ 'engine/SCons/compat/_scons_textwrap.py',
+ 'engine/SCons/Conftest.py',
+ 'engine/SCons/dblite.py',
+ 'MANIFEST',
+ 'os_spawnv_fix.diff',
+ 'setup.cfg',
+ ],
+ # We run epydoc on the *.py files, which generates *.pyc files.
+ remove_patterns = [
+ '*.pyc',
+ ]
+ ),
+
+ CheckExpandedCopyright(
+ build_local,
+ remove_list = [
+ 'SCons/compat/_scons_optparse.py',
+ 'SCons/compat/_scons_sets.py',
+ 'SCons/compat/_scons_sets15.py',
+ 'SCons/compat/_scons_shlex.py',
+ 'SCons/compat/_scons_subprocess.py',
+ 'SCons/compat/_scons_textwrap.py',
+ 'SCons/Conftest.py',
+ 'SCons/dblite.py',
+ 'scons-%s.egg-info' % scons_version,
+ ],
+ ),
+
+ CheckExpandedCopyright(
+ build_src,
+ remove_list = [
+ 'bench/timeit.py',
+ 'bin',
+ 'config',
+ 'debian',
+ 'gentoo',
+ 'doc/design',
+ 'doc/MANIFEST',
+ 'doc/python10',
+ 'doc/reference',
+ 'doc/developer/MANIFEST',
+ 'doc/man/MANIFEST',
+ 'doc/user/cons.pl',
+ 'doc/user/MANIFEST',
+ 'doc/user/SCons-win32-install-1.jpg',
+ 'doc/user/SCons-win32-install-2.jpg',
+ 'doc/user/SCons-win32-install-3.jpg',
+ 'doc/user/SCons-win32-install-4.jpg',
+ 'examples',
+ 'gentoo',
+ 'QMTest/classes.qmc',
+ 'QMTest/configuration',
+ 'QMTest/TestCmd.py',
+ 'QMTest/TestCommon.py',
+ 'QMTest/unittest.py',
+ 'src/os_spawnv_fix.diff',
+ 'src/MANIFEST.in',
+ 'src/setup.cfg',
+ 'src/engine/MANIFEST.in',
+ 'src/engine/MANIFEST-xml.in',
+ 'src/engine/setup.cfg',
+ 'src/engine/SCons/compat/_scons_optparse.py',
+ 'src/engine/SCons/compat/_scons_sets.py',
+ 'src/engine/SCons/compat/_scons_sets15.py',
+ 'src/engine/SCons/compat/_scons_shlex.py',
+ 'src/engine/SCons/compat/_scons_subprocess.py',
+ 'src/engine/SCons/compat/_scons_textwrap.py',
+ 'src/engine/SCons/Conftest.py',
+ 'src/engine/SCons/dblite.py',
+ 'src/script/MANIFEST.in',
+ 'src/script/setup.cfg',
+ 'test/Fortran/.exclude_tests',
+ 'timings/changelog.html',
+ 'timings/graph.html',
+ 'timings/index.html',
+ ],
+ remove_patterns = [
+ '*.js',
+ ]
+ ),
+
+]
+
+missing_strings = []
+not_built = []
+
+for collector in check_list:
+ if collector.directory_exists():
+ missing_strings.extend(collector.find_missing())
+ elif collector.must_be_built():
+ not_built.append(collector.directory)
+
+if missing_strings:
+ print "Found the following files with missing strings:"
+ print "\t" + string.join(missing_strings, "\n\t")
+ test.fail_test(1)
+
+if not_built:
+ print "Cannot check all strings, the following have apparently not been built:"
+ print "\t" + string.join(not_built, "\n\t")
+ test.no_result(1)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: