From 1344ba4f0b01aaab3e1cffb82a428d8979991fdf Mon Sep 17 00:00:00 2001 From: stevenknight Date: Tue, 15 Nov 2005 14:33:25 +0000 Subject: [PATCH] Allow explicit target_factory=Dir with Builders that make a directory to override the default, implicit make-a-directory Builder.. git-svn-id: http://scons.tigris.org/svn/scons/trunk@1386 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- doc/man/scons.1 | 1656 +++------------------------- src/CHANGES.txt | 546 --------- src/engine/SCons/Builder.py | 339 +++--- src/engine/SCons/BuilderTests.py | 397 ++----- src/engine/SCons/Node/FS.py | 1449 +++++++++++------------- src/engine/SCons/Node/NodeTests.py | 564 +++------- src/engine/SCons/Node/__init__.py | 606 ++++------ 7 files changed, 1416 insertions(+), 4141 deletions(-) diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 46ad8134..b94d680e 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -31,7 +31,7 @@ .fi .RE .. -.TH SCONS 1 "October 2005" +.TH SCONS 1 "October 2004" .SH NAME scons \- a software construction tool .SH SYNOPSIS @@ -149,28 +149,6 @@ 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['PATH']) -.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 @@ -532,10 +510,8 @@ 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. +Print a count of how many objects are created +of the various classes used internally by SCons. This only works when run under Python 2.1 or later. .TP @@ -543,16 +519,6 @@ This only works when run under Python 2.1 or later. Print the dependency tree after each top-level target is built. This prints out only derived files. -.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 @@ -570,34 +536,11 @@ of a given derived file: $ scons --debug=includes foo.o .EE -.TP ---debug=memoizer -Prints a summary of hits and misses in the Memoizer, -the internal SCons subsystem for caching -various values in memory instead of -recomputing them each time they're needed. - .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 -Disables use of the Memoizer, -the internal SCons subsystem for caching -various values in memory instead of -recomputing them each time they're needed. -This provides more accurate counts of the -underlying function calls in the -Python profiler output when using the -.R --profile= -option. -(When the Memoizer is used, -the profiler counts all -memoized functions as being executed -by the Memoizer's wrapper calls.) +and before and after building. .TP --debug=objects @@ -610,6 +553,7 @@ This only works when run under Python 2.1 or later. Re-run SCons under the control of the .RI pdb Python debugger. +.EE .TP --debug=presub @@ -629,12 +573,6 @@ Building myprog.o with action(s): Prints an internal Python stack trace when encountering an otherwise unexplained error. -.TP ---debug=stree -Print the dependency tree along with status information. This is the -same as the debug=tree option, but additional status information is -provided for each node in the tree. - .TP --debug=time Prints various time profiling information: the time spent @@ -649,47 +587,6 @@ after each top-level target is built. This prints out the complete dependency tree including implicit dependencies and ignored dependencies. -.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 .\" -e, --environment-overrides .\" Variables from the execution environment override construction @@ -827,18 +724,12 @@ Ignored for compatibility with non-GNU versions of .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 +This value determines how old a file must be before its content signature +is cached. 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 content +signature cached. 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. +of 0 means to always cache the signature, no matter how old the file is. .TP -n, --just-print, --dry-run, --recon @@ -1060,10 +951,8 @@ 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 +variable from the user's external environment to the construction environment's .B ENV dictionary. @@ -1106,7 +995,7 @@ have two functions: generate(env, **kw) and exists(env). The .B generate() function -modifies the passed-in environment +modifies the passed in environment to set up variables so that the tool can be executed; it may use any keyword arguments @@ -1119,19 +1008,6 @@ 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 Copy () -and -.BR Tool () -methods: - -.ES -base = Environment(toolpath=['custom_path']) -derived = base.Copy(tools=['custom_tool']) -derived.CustomBuilder() -.EE The elements of the tools list may also be functions or callable objects, @@ -1221,7 +1097,6 @@ ifl ifort ilink ilink32 -intelc jar javac javah @@ -1237,8 +1112,6 @@ mslib mslink msvc msvs -mwcc -mwld nasm pdflatex pdftex @@ -1375,15 +1248,6 @@ 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 of Nodes that represent the target or targets that will be built. A @@ -1402,7 +1266,7 @@ to add a specific flag when compiling one specific object file: .ES -bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR') +bar_obj_list = env.StaticObject('bar.c', CCFLAGS='-DBAR') env.Program(source = ['foo.c', bar_obj_list, 'main.c']) .EE @@ -1449,7 +1313,7 @@ by passing the Node to the Python-builtin function: .ES -bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR') +bar_obj_list = env.StaticObject('bar.c', CCFLAGS='-DBAR') print "The path to bar_obj is:", str(bar_obj_list[0]) .EE @@ -1572,14 +1436,11 @@ the .B DVI builder method will also examine the contents of the -.B .aux -file and invoke the $BIBTEX command line +.B .aux file +and invoke the $BIBTEX command line if the string .B bibdata is found, -start $MAKEINDEX to generate an index if a -.B .ind -file is found and will examine the contents .B .log file and re-run the $LATEXCOM command @@ -1711,16 +1572,6 @@ A synonym for the .B StaticLibrary builder method. -'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.IP LoadableModule() -.IP env.LoadableModule() -On most systems, -this is the same as -.BR SharedLibrary (). -On Mac OS X (Darwin) platforms, -this creates a loadable module bundle. - - '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP M4() .IP env.M4() @@ -1752,101 +1603,55 @@ env.Moc('foo.cpp') # generates foo.moc '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP MSVSProject() .IP env.MSVSProject() -Builds a Microsoft Visual Studio project file, -and by default builds a solution file as well. - +Builds Microsoft Visual Studio project files. 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 +or the version set by .B MSVS_VERSION -in the construction environment). -For Visual Studio 6, it will generate a +in the Environment constructor). +For VS 6, it will generate .B .dsp -file. -For Visual Studio 7 (.NET), it will -generate a -.B .vcproj -file. - -By default, -this also generates a solution file -for the specified project, -a +and .B .dsw -file for Visual Studio 6 -or a +files, for VS 7, it will +generate +.B .vcproj +and .B .sln -file for Visual Studio 7 (.NET). -This behavior may be disabled by specifying -.B auto_build_solution=0 -when you call -.BR MSVSProject (), -in which case you presumably want to -build the solution file(s) -by calling the -.BR MSVSSolution () -Builder (see below). +files. It takes several lists of filenames to be placed into the project -file. -These are currently limited to -.BR srcs , -.BR incs , -.BR localincs , -.BR resources , +file, currently these are limited to +.B srcs, incs, localincs, resources, and -.BR misc . -These are pretty self-explanatory, but it should be noted that these -lists are added to the $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. - -In addition to the above lists of values (which are all optional, -although not specifying any of them results in an empty project file), -the following values may be specified: - -.BR target : -The name of the target -.B .dsp -or -.B .vcproj -file. -The correct -suffix for the version of Visual Studio must be used, but the -.B env['MSVSPROJECTSUFFIX'] -construction variable +.B misc. +These are pretty self explanatory, but it +should be noted that the 'srcs' list is NOT added to the $SOURCES +environment variable. This is because it represents a list of files +to be added to the project file, not the source used to build the +project file (in this case, the 'source' is the SConscript file used +to call MSVSProject). + +In addition to these values (which are all optional, although not +specifying any of them results in an empty project file), the +following values must be specified: + +target: The name of the target .dsp or .vcproj file. The correct +suffix for the version of Visual Studio must be used, but the value + +env['MSVSPROJECTSUFFIX'] + will be defined to the correct value (see example below). -.BR variant : -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 -.B | -(vertical pipe) -character: -.BR Debug|Xbox . -The default target platform is Win32. -Multiple calls to -.BR MSVSProject () -with different variants are allowed; -all variants will be added to the project file with their appropriate +variant: The name of this particular variant. These are typically +things like "Debug" or "Release", but really can be anything you want. +Multiple calls to MSVSProject with different variants are allowed: all +variants will be added to the project file with their appropriate build targets and sources. -.BR buildtarget : -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 -.B buildtarget -entries must match the number of -.B variant -entries. +buildtarget: A list of SCons.Node.FS objects which is returned from +the command which builds the target. This is used to tell SCons what +to build when the 'build' button is pressed inside of the IDE. Example Usage: @@ -1870,59 +1675,6 @@ Example Usage: variant = 'Release') .EE -'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.IP MSVSSolution() -.IP env.MSVSSolution() -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 -.B MSVS_VERSION -in the construction environment). -For Visual Studio 6, it will generate a -.B .dsw -file. -For Visual Studio 7 (.NET), it will -generate a -.B .sln -file. - -The following values must be specified: - -.BR target : -The name of the target .dsw or .sln file. The correct -suffix for the version of Visual Studio must be used, but the value -.B env['MSVSSOLUTIONSUFFIX'] -will be defined to the correct value (see example below). - -.BR variant : -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. - -.BR projects : -A list of project file names, or Project nodes returned by calls to the -.BR MSVSProject () -Builder, -to be placed into the solution file. -(NOTE: Currently only one project is supported per solution.) -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. - -Example Usage: - -.ES - local.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], - projects = ['bar' + env['MSVSPROJECTSUFFIX']], - variant = 'Release') -.EE - '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP Object() .IP env.Object() @@ -2210,13 +1962,6 @@ env.SharedObject(target = 'eee.o', source = 'eee.cpp') env.SharedObject(target = 'fff.obj', source = 'fff.for') .EE -Note that the source files will be scanned -according to the suffix mappings in -.B SourceFileScanner -object. -See the section "Scanner Objects," -below, for a more information. - '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP StaticLibrary() .IP env.StaticLibrary() @@ -2277,8 +2022,6 @@ Source files must have one of the following extensions: .FOR Fortran file .fpp Fortran file + C pre-processor .FPP Fortran file + C pre-processor - .m Objective C file - .mm Objective C++ file .s assembly language file .S WIN32: assembly language file POSIX: assembly language file + C pre-processor @@ -2300,13 +2043,6 @@ env.StaticObject(target = 'bbb.o', source = 'bbb.c++') env.StaticObject(target = 'ccc.obj', source = 'ccc.f') .EE -Note that the source files will be scanned -according to the suffix mappings in -.B SourceFileScanner -object. -See the section "Scanner Objects," -below, for a more information. - '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP Tar() .IP env.Tar() @@ -2320,12 +2056,6 @@ 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 -.B scons -knows about them from other Builder or function calls. .ES env.Tar('src.tar', 'src') @@ -2392,12 +2122,6 @@ 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 -.B scons -knows about them from other Builder or function calls. .ES env.Zip('src.zip', 'src') @@ -2408,21 +2132,9 @@ env.Zip('stuff', ['subdir1', 'subdir2']) env.Zip('stuff', 'another') .EE -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, +C source files, C++ source files, Fortran source files with .B .F (POSIX systems only), @@ -2437,26 +2149,14 @@ and assembly language files with 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. +for C preprocessor dependencies, +so the dependencies do not need to be specified explicitly. +In addition, 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). .SS Methods and Functions to Do Things In addition to Builder methods, @@ -2484,14 +2184,6 @@ environment it looks like: 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 @@ -2574,17 +2266,11 @@ can be converted into an Action object '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI Alias( alias ", [" targets ", [" action ]]) +.RI Alias( alias ", [" targets ]) .TP -.RI env.Alias( alias ", [" targets ", [" action ]]) +.RI env.Alias( alias ", [" targets ]) 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, @@ -2592,8 +2278,7 @@ 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. +alias to add additional targets to the alias. .ES Alias('install') @@ -2602,8 +2287,6 @@ 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 '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -2671,7 +2354,7 @@ Example: .ES print 'before:',env['ENV']['INCLUDE'] include_path = '/foo/bar:/foo' -env.AppendENVPath('INCLUDE', include_path) +env.PrependENVPath('INCLUDE', include_path) print 'after:',env['ENV']['INCLUDE'] yields: @@ -2936,47 +2619,22 @@ Clean(['foo', 'bar'], 'something_else_to_clean') '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI Command( target ", " source ", " action ", [" key = val ", ...])" +.RI Command( target ", " source ", " commands ", [" key = val ", ...])" .TP -.RI env.Command( target ", " source ", " action ", [" key = val ", ...])" +.RI env.Command( target ", " source ", " commands ", [" 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 +Any keyword arguments specified override any same-named existing construction variables. -An action can be an external command, +Note that 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. +see "Action Objects," below. Examples: .ES @@ -3232,24 +2890,17 @@ EnsurePythonVersion(2,2) '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI EnsureSConsVersion( major ", " minor ", [" revision ]) +.RI EnsureSConsVersion( major ", " minor ) .TP -.RI env.EnsureSConsVersion( major ", " minor ", [" revision ]) +.RI env.EnsureSConsVersion( major ", " minor ) Ensure that the SCons version is at least -.IR major.minor , -or -.IR major.minor.revision . -if -.I revision -is specified. +.IR major . minor . 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. .ES -EnsureSConsVersion(0,14) - -EnsureSConsVersion(0,96,90) +EnsureSConsVersion(0,9) .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -3597,7 +3248,7 @@ Returns a list of the target Node or Nodes. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI env.ParseConfig( command ", [" function ", " unique ]) +.RI env.ParseConfig( command ", [" function ]) Calls the specified .I function to modify the environment as specified by the output of @@ -3608,25 +3259,15 @@ 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. - -By default, +and parses the returned .BR -L , .BR -l , .BR -Wa , .BR -Wl , .BR -Wp , .B -I -and other options, -are add to the +and other options +into the .BR LIBPATH , .BR LIBS , .BR ASFLAGS , @@ -3635,7 +3276,7 @@ are add to the .B CPPPATH and .B CCFLAGS -construction variables, +variables, respectively. A returned .B -pthread @@ -3659,7 +3300,7 @@ construction variable. .TP .RI ParseDepends( filename ", [" must_exist ]) .TP -.RI env.ParseDepends( filename ", [" must_exist " " only_one ]) +.RI env.ParseDepends( filename ", [" must_exist ]) Parses the contents of the specified .I filename as a list of dependencies in the style of @@ -3667,7 +3308,6 @@ as a list of dependencies in the style of or .BR mkdep , and explicitly establishes all of the listed dependencies. - By default, it is not an error if the specified @@ -3681,34 +3321,13 @@ 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 +file which called the .B ParseDepends function. @@ -3769,10 +3388,8 @@ env.Platform('posix') Note that the .B win32 platform adds the -.B SYSTEMDRIVE -and .B SYSTEMROOT -variables from the user's external environment +variable from the user's external environment to the construction environment's .B ENV dictionary. @@ -3850,7 +3467,7 @@ after: /foo/bar:/foo:/biz '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI env.PrependUnique( key = val ", [...])" +.RI env.AppendUnique( key = val ", [...])" Appends the specified keyword arguments to the beginning of construction variables in the environment. If the Environment does not have @@ -4190,17 +3807,13 @@ SConscript('bar/SConscript') # will chdir to bar This tells .B scons to store all file signatures -in the specified database +in the specified .IR file . If the .I file -name is omitted, -.B .sconsign +is omitted, +.B .sconsign.dbm 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, @@ -4208,20 +3821,6 @@ 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 @@ -4235,9 +3834,8 @@ 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). +# Stores signatures in ".sconsign.dbm" +# in the top-level SConstruct directory. SConsignFile() # Stores signatures in the file "etc/scons-signatures" @@ -4246,10 +3844,6 @@ 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 '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -4392,92 +3986,7 @@ 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( string ", [" raw ", " target ", " source ", " conv ]) -Performs construction variable interpolation -on the specified string argument. - -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). - -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. - -By default, -all returned values are converted -to their string representation. -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. - -.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 ) @@ -4711,13 +4220,6 @@ In addition to the global functions and methods, 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 @@ -4920,15 +4422,6 @@ The static library archiver. .IP ARCOM The command line used to generate a static library from object files. -.IP ARCOMSTR -The string displayed when an object file -is generated from an assembly-language source file. -If this is not set, then $ARCOM (the command line) is displayed. - -.ES -env = Environment(ARCOMSTR = "Archiving $TARGET") -.EE - .IP ARFLAGS General options passed to the static library archiver. @@ -4939,15 +4432,6 @@ The assembler. The command line used to generate an object file from an assembly-language source file. -.IP ASCOMSTR -The string displayed when an object file -is generated from an assembly-language source file. -If this is not set, then $ASCOM (the command line) is displayed. - -.ES -env = Environment(ASCOMSTR = "Assembling $TARGET") -.EE - .IP ASFLAGS General options passed to the assembler. @@ -4958,22 +4442,6 @@ after first running the file through the C preprocessor. Any options specified in the $ASFLAGS and $CPPFLAGS construction variables are included on this command line. -.IP ASPPCOMSTR -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 $ASPPCOM (the command line) is displayed. - -.ES -env = Environment(ASPPCOMSTR = "Assembling $TARGET") -.EE - -.IP ASPPFLAGS -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 $ASFLAGS. - .IP BIBTEX The bibliography generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. @@ -4983,16 +4451,6 @@ The command line used to call the bibliography generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. -.IP BIBTEXCOMSTR -The string displayed when generating a bibliography -for TeX or LaTeX. -If this is not set, then $BIBTEXCOM (the command line) is displayed. - -.ES -env = Environment(BIBTEXCOMSTR = "Generating bibliography $TARGET") -.EE - - .IP BIBTEXFLAGS General options passed to the bibliography generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. @@ -5002,13 +4460,7 @@ The BitKeeper executable. .IP BITKEEPERCOM The command line for -fetching source files using BitKeeper. - -.IP BITKEEPERCOMSTR -The string displayed when fetching -a source file using BitKeeper. -If this is not set, then $BITKEEPERCOM -(the command line) is displayed. +fetching source files using BitKEeper. .IP BITKEEPERGET The command ($BITKEEPER) and subcommand @@ -5057,15 +4509,6 @@ The command line used to compile a C source file to a (static) object file. Any options specified in the $CCFLAGS and $CPPFLAGS construction variables are included on this command line. -.IP CCCOMSTR -The string displayed when a C source file -is compiled to a (static) object file. -If this is not set, then $CCCOM (the command line) is displayed. - -.ES -env = Environment(CCCOMSTR = "Compiling static object $TARGET") -.EE - .IP CCFLAGS General options that are passed to the C compiler. @@ -5099,25 +4542,6 @@ called to transform the list before concatenation. env['_CPPINCFLAGS'] = '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)', .EE -.IP CONFIGUREDIR -The name of the directory in which -Configure context test files are written. -The default is -.B .sconf_temp -in the top-level directory -containing the -.B SConstruct -file. - -.IP CONFIGURELOG -The name of the Configure context log file. -The default is -.B config.log -in the top-level directory -containing the -.B SConstruct -file. - .IP CPPDEFINES A platform independent specification of C preprocessor definitions. The definitions will be added to command lines @@ -5289,7 +4713,6 @@ The default list is: [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", ".h", ".H", ".hxx", ".hpp", ".hh", ".F", ".fpp", ".FPP", - ".m", ".mm", ".S", ".spp", ".SPP"] .EE @@ -5303,12 +4726,6 @@ Options that are passed to the CVS checkout subcommand. The command line used to fetch source files from a CVS repository. -.IP CVSCOMSTR -The string displayed when fetching -a source file from a CVS repository. -If this is not set, then $CVSCOM -(the command line) is displayed. - .IP CVSFLAGS General options that are passed to CVS. By default, this is set to @@ -5335,10 +4752,7 @@ SCons also treats files with the suffixes .IR .c++ , and .I .C++ -as C++ files, -and files with -.I .mm -suffixes as Objective C++ files. +as C++ files. On case-sensitive systems (Linux, UNIX, and other POSIX-alikes), SCons also treats .I .C @@ -5350,21 +4764,8 @@ The command line used to compile a C++ source file to an object file. Any options specified in the $CXXFLAGS and $CPPFLAGS construction variables are included on this command line. -.IP CXXCOMSTR -The string displayed when a C++ source file -is compiled to a (static) object file. -If this is not set, then $CXXCOM (the command line) is displayed. - -.ES -env = Environment(CXXCOMSTR = "Compiling static object $TARGET") -.EE - .IP CXXFLAGS General options that are passed to the C++ compiler. -By default, this includes the value of $CCFLAGS, -so that setting $CCFLAGS affects both C and C++ compilation. -If you want to add C++-specific flags, -you must set or override the value of $CXXFLAGS. .IP CXXVERSION The version number of the C++ compiler. @@ -5372,12 +4773,8 @@ This may or may not be set, depending on the specific C++ compiler being used. .IP Dir -A function that converts a string -into a Dir instance relative to the target being built. - -.IP Dirs -A function that converts a list of strings -into a list of Dir instances relative to the target being built. +A function that converts a file name into a Dir instance relative to the +target being built. .IP DSUFFIXES The list of suffixes of files that will be scanned @@ -5397,11 +4794,6 @@ General options passed to the TeX DVI file to PDF file converter. .IP DVIPDFCOM The command line used to convert TeX DVI files into a PDF file. -.IP DVIPDFCOMSTR -The string displayed when a TeX DVI file -is converted into a PDF file. -If this is not set, then $DVIPDFCOM (the command line) is displayed. - .IP DVIPS The TeX DVI file to PostScript converter. @@ -5477,11 +4869,6 @@ You should normally set the $FORTRANCOM variable, which specifies the default command line for all Fortran versions. -.IP F77COMSTR -The string displayed when a Fortran 77 source file -is compiled to an object file. -If this is not set, then $F77COM or $FORTRANCOM (the command line) is displayed. - .IP F77FLAGS General user-specified options that are passed to the Fortran 77 compiler. Note that this variable does @@ -5587,12 +4974,6 @@ You should normally set the $FORTRANCOM variable, which specifies the default command line for all Fortran versions. -.IP F90COMSTR -The string displayed when a Fortran 90 source file -is compiled to an object file. -If this is not set, then $F90COM or $FORTRANCOM -(the command line) is displayed. - .IP F90FLAGS General user-specified options that are passed to the Fortran 90 compiler. Note that this variable does @@ -5698,12 +5079,6 @@ You should normally set the $FORTRANCOM variable, which specifies the default command line for all Fortran versions. -.IP F95COMSTR -The string displayed when a Fortran 95 source file -is compiled to an object file. -If this is not set, then $F95COM or $FORTRANCOM -(the command line) is displayed. - .IP F95FLAGS General user-specified options that are passed to the Fortran 95 compiler. Note that this variable does @@ -5804,12 +5179,6 @@ in the $FORTRANFLAGS, $CPPFLAGS, $_CPPDEFFLAGS, $_FORTRANMODFLAG, and $_FORTRANINCFLAGS construction variables are included on this command line. -.IP FORTRANCOMSTR -The string displayed when a Fortran source file -is compiled to an object file. -If this is not set, then $FORTRANCOM -(the command line) is displayed. - .IP FORTRANFLAGS General user-specified options that are passed to the Fortran compiler. Note that this variable does @@ -5837,7 +5206,7 @@ of each directory in $FORTRANPATH. 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. +for module files, as well .IP FORTRANMODDIRPREFIX The prefix used to specify a module directory on the Fortran compiler command @@ -5853,7 +5222,7 @@ This will be appended to the beginning of the directory in the $FORTRANMODDIR construction variables when the $_FORTRANMODFLAG variables is automatically generated. -.IP _FORTRANMODFLAG +.IP FORTRANMODFLAG An automatically-generated construction variable containing the Fortran compiler command-line option for specifying the directory location where the Fortran @@ -5948,70 +5317,9 @@ The default list is: .EE .IP File -A function that converts a string into a File instance relative to the +A function that converts a file name into a File instance relative to the target being built. -.IP FRAMEWORKPATH -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 . -Used by the linker to find user-specified frameworks when linking (see -$FRAMEWORKS). -For example: -.ES - env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') -.EE -.IP -will add -.ES - ... -Fmyframeworkdir -.EE -.IP -to the compiler and linker command lines. - -.IP _FRAMEWORKPATH -On Mac OS X with gcc, an automatically-generated construction variable -containing the linker command-line options corresponding to FRAMEWORKPATH. - -.IP FRAMEWORKPATHPREFIX -On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. -(see $FRAMEWORKPATH). -The default value is -.BR -F . - -.IP FRAMEWORKPREFIX -On Mac OS X with gcc, -the prefix to be used for linking in frameworks -(see $FRAMEWORKS). -The default value is -.BR -framework . - -.IP FRAMEWORKS -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: -.ES - env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) -.EE - -.IP _FRAMEWORKS -On Mac OS X with gcc, -an automatically-generated construction variable -containing the linker command-line options -for linking with FRAMEWORKS. - -.IP FRAMEWORKSFLAGS -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 $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX, -$FRAMWORKPREFIX and $FRAMEWORKS variables -described above.) - .IP GS The Ghostscript program used to convert PostScript to PDF files. @@ -6022,12 +5330,6 @@ when converting PostScript to PDF files. .IP GSCOM The Ghostscript command line used to convert PostScript to PDF files. -.IP GSCOMSTR -The string displayed when -Ghostscript is used to convert -a PostScript file to a PDF file. -If this is not set, then $GSCOM (the command line) is displayed. - .IP IDLSUFFIXES The list of suffixes of files that will be scanned for IDL implicit dependencies @@ -6075,19 +5377,6 @@ is the construction environment (a dictionary of construction values) in force for this file installation. -.IP INSTALLSTR -The string displayed when a file is -installed into a destination file name. -The default is: -.ES -Install file: "$SOURCE" as "$TARGET" -.EE - -.IP INTEL_C_COMPILER_VERSION -Set by the "intelc" Tool -to the major version number of the Intel C compiler -selected for use. - .IP JAR The Java archive tool. @@ -6100,15 +5389,6 @@ option). .IP JARCOM The command line used to call the Java archive tool. -.IP JARCOMSTR -The string displayed when the Java archive tool -is called -If this is not set, then $JARCOM (the command line) is displayed. - -.ES -env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") -.EE - .IP JARFLAGS General options passed to the Java archive tool. By default this is set to @@ -6132,16 +5412,6 @@ corresponding Java class files. Any options specified in the $JAVACFLAGS construction variable are included on this command line. -.IP JAVACCOMSTR -The string displayed when compiling -a directory tree of Java source files to -corresponding Java class files. -If this is not set, then $JAVACCOM (the command line) is displayed. - -.ES -env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") -.EE - .IP JAVACFLAGS General options that are passed to the Java compiler. @@ -6166,15 +5436,6 @@ from Java classes. Any options specified in the $JAVAHFLAGS construction variable are included on this command line. -.IP JAVAHCOMSTR -The string displayed when C header and stub files -are generated from Java classes. -If this is not set, then $JAVAHCOM (the command line) is displayed. - -.ES -env = Environment(JAVAHCOMSTR = "Generating header/stub file(s) $TARGETS from $SOURCES") -.EE - .IP JAVAHFLAGS General options passed to the C header and stub file generator for Java classes. @@ -6190,66 +5451,9 @@ The LaTeX structured formatter and typesetter. .IP LATEXCOM The command line used to call the LaTeX structured formatter and typesetter. -.IP LATEXCOMSTR -The string displayed when calling -the LaTeX structured formatter and typesetter. -If this is not set, then $LATEXCOM (the command line) is displayed. - -.ES -env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES") -.EE - .IP LATEXFLAGS General options passed to the LaTeX structured formatter and typesetter. -.IP LATEXRETRIES -The maximum number of times that LaTeX -will be re-run if the -.B .log -generated by the $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. - -.IP LATEXSUFFIXES -The list of suffixes of files that will be scanned -for LaTeX implicit dependencies -(\\include or \\import files). -The default list is: - -.ES -[".tex", ".ltx", ".latex"] -.EE - -.IP LDMODULE -The linker for building loadable modules. -By default, this is the same as $SHLINK. - -.IP LDMODULECOM -The command line for building loadable modules. -On Mac OS X, this uses the $LDMODULE, -$LDMODULEFLAGS and $FRAMEWORKSFLAGS variables. -On other systems, this is the same as $SHLINK. - -.IP LDMODULECOMSTR -The string displayed when building loadable modules. -If this is not set, then $LDMODULECOM (the command line) is displayed. - -.IP LDMODULEFLAGS -General user options passed to the linker for building loadable modules. - -.IP LDMODULEPREFIX -The prefix used for loadable module file names. -On Mac OS X, this is null; -on other systems, this is -the same as $SHLIBPREFIX. - -.IP LDMODULESUFFIX -The suffix used for loadable module file names. -On Mac OS X, this is null; -on other systems, this is -the same as $SHLIBSUFFIX. - .IP LEX The lexical analyzer generator. @@ -6260,15 +5464,6 @@ General options passed to the lexical analyzer generator. The command line used to call the lexical analyzer generator to generate a source file. -.IP LEXCOMSTR -The string displayed when generating a source file -using the lexical analyzer generator. -If this is not set, then $LEXCOM (the command line) is displayed. - -.ES -env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") -.EE - .IP _LIBDIRFLAGS An automatically-generated construction variable containing the linker command-line options @@ -6297,7 +5492,7 @@ for specifying libraries to be linked with the resulting target. The value of $_LIBFLAGS is created by appending $LIBLINKPREFIX and $LIBLINKSUFFIX to the beginning and end -of each filename in $LIBS. +of each directory in $LIBS. .IP LIBLINKPREFIX The prefix used to specify a library to link on the linker command line. @@ -6383,7 +5578,7 @@ appending the values of the $LIBLINKPREFIX and $LIBLINKSUFFIX construction variables to the beginning and end -of each filename in $LIBS. +of each directory in $LIBS. Any command lines you define that need the LIBS library list should include $_LIBFLAGS: @@ -6392,26 +5587,6 @@ include $_LIBFLAGS: env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") .EE -.IP -If you add a -File -object to the -LIBS -list, the name of that file will be added to -$_LIBFLAGS, -and thus the link line, as is, without -$LIBLINKPREFIX -or -$LIBLINKSUFFIX. -For example: -.ES -env.Append(LIBS=File('/tmp/mylib.so')) -.EE - -.IP -In all cases, scons will add dependencies from the executable program to -all the libraries in this list. - .IP LIBSUFFIX The suffix used for (static) library file names. A default value is set for each platform @@ -6449,15 +5624,6 @@ for the variable that expands to library search path options. .IP LINKCOM The command line used to link object files into an executable. -.IP LINKCOMSTR -The string displayed when object files -are linked into an executable. -If this is not set, then $LINKCOM (the command line) is displayed. - -.ES -env = Environment(LINKCOMSTR = "Linking $TARGET") -.EE - .IP M4 The M4 macro preprocessor. @@ -6465,31 +5631,7 @@ The M4 macro preprocessor. General options passed to the M4 macro preprocessor. .IP M4COM -The command line used to pass files through the M4 macro preprocessor. - -.IP M4COMSTR -The string displayed when -a file is passed through the M4 macro preprocessor. -If this is not set, then $M4COM (the command line) is displayed. - -.IP MAKEINDEX -The makeindex generator for the TeX formatter and typesetter and the -LaTeX structured formatter and typesetter. - -.IP MAKEINDEXCOM -The command line used to call the makeindex generator for the -TeX formatter and typesetter and the LaTeX structured formatter and -typesetter. - -.IP MAKEINDEXCOMSTR -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 $MAKEINDEXCOM (the command line) is displayed. - -.IP MAKEINDEXFLAGS -General options passed to the makeindex generator for the TeX formatter -and typesetter and the LaTeX structured formatter and typesetter. +The command line used to pass files through the macro preprocessor. .IP MAXLINELENGTH The maximum number of characters allowed on an external command line. @@ -6564,81 +5706,6 @@ For VS7, it is: .IP Where '' is the installed location of Visual Studio. -.IP MSVS_PROJECT_BASE_PATH -The string -placed in a generated Microsoft Visual Studio solution file -as the value of the -.B SccProjectFilePathRelativizedFromConnection0 -and -.B SccProjectFilePathRelativizedFromConnection1 -attributes of the -.B GlobalSection(SourceCodeControl) -section. -There is no default value. - -.IP MSVS_PROJECT_GUID -The string -placed in a generated Microsoft Visual Studio project file -as the value of the -.B ProjectGUID -attribute. -The string is also placed in the -.B SolutionUniqueID -attribute of the -.B GlobalSection(SourceCodeControl) -section of the Microsoft Visual Studio solution file. -There is no default value. - -.IP MSVS_SCC_AUX_PATH -The path name -placed in a generated Microsoft Visual Studio project file -as the value of the -.B SccAuxPath -attribute -if the -.I MSVS_SCC_PROVIDER -construction variable is also set. -There is no default value. - -.IP MSVS_SCC_LOCAL_PATH -The path name -placed in a generated Microsoft Visual Studio project file -as the value of the -.B SccLocalPath -attribute -if the -.I MSVS_SCC_PROVIDER -construction variable is also set. -The path name is also placed in the -.B SccLocalPath0 -and -.B SccLocalPath1 -attributes of the -.B GlobalSection(SourceCodeControl) -section of the Microsoft Visual Studio solution file. -There is no default value. - -.IP MSVS_SCC_PROJECT_NAME -The project name -placed in a generated Microsoft Visual Studio project file -as the value of the -.B SccProjectName -attribute. -There is no default value. - -.IP MSVS_SCC_PROVIDER -The string -placed in a generated Microsoft Visual Studio project file -as the value of the -.B SccProvider -attribute. -The string is also placed in the -.B SccProvider1 -attribute of the -.B GlobalSection(SourceCodeControl) -section of the Microsoft Visual Studio solution file. -There is no default value. - .IP MSVS_USE_MFC_DIRS Tells the MS Visual Studio tool(s) to use the MFC directories in its default paths @@ -6697,36 +5764,17 @@ environment variables are set explictly. Sets the preferred version of MSVS to use. SCons will (by default) select the latest version of MSVS -installed on your machine. -So, if you have version 6 and version 7 (MSVS .NET) installed, -it will prefer version 7. -You can override this by +installed on your machine. So, if you have version 6 and version 7 +(MSVS .NET) installed, it will prefer version 7. You can override this by specifying the .B MSVS_VERSION variable in the Environment initialization, setting it to the appropriate version ('6.0' or '7.0', for example). If the given version isn't installed, tool initialization will fail. -.IP MSVSBUILDCOM -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. - -.IP MSVSCLEANCOM -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. - -.IP MSVSENCODING -The encoding string placed in -a generated Microsoft Visual Studio project file. -The default is encoding -.BR Windows-1252 . - .IP MSVSPROJECTCOM -The action used to generate Microsoft Visual Studio project files. +The action used to generate Microsoft Visual Studio +project and solution files. .IP MSVSPROJECTSUFFIX The suffix used for Microsoft Visual Studio project (DSP) files. @@ -6737,45 +5785,6 @@ and .B .dsp when using earlier versions of Visual Studio. -.IP MSVSREBUILDCOM -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. - -.IP MSVSSCONS -The SCons used in generated Microsoft Visual Studio project files. -The default is the version of SCons being -used to generate the project file. - -.IP MSVSSCONSFLAGS -The SCons flags used in generated Microsoft Visual Studio -project files. - -.IP MSVSSCONSCOM -The default SCons command used in generated Microsoft Visual Studio -project files. - -.IP MSVSSCONSCRIPT -The sconscript file -(that is, -.B SConstruct -or -.B SConscript -file) -that will be invoked by Visual Studio -project files -(through the -.B MSVSSCONSCOM -variable). -The default is the same sconscript file -that contains the call to -.BR MSVSProject () -to build the project file. - -.IP MSVSSOLUTIONCOM -The action used to generate Microsoft Visual Studio solution files. - .IP MSVSSOLUTIONSUFFIX The suffix used for Microsoft Visual Studio solution (DSW) files. The default value is @@ -6785,14 +5794,6 @@ and .B .dsw when using earlier versions of Visual Studio. -.IP MWCW_VERSION -The version number of the MetroWerks CodeWarrior C compiler -to be used. - -.IP MWCW_VERSIONS -A list of installed versions of the MetroWerks CodeWarrior C compiler -on this system. - .IP no_import_lib When set to non-zero, suppresses creation of a corresponding Win32 static import lib by the @@ -6816,11 +5817,6 @@ The Perforce executable. The command line used to fetch source files from Perforce. -.IP P4COMSTR -The string displayed when -fetching a source file from Perforce. -If this is not set, then $P4COM (the command line) is displayed. - .IP P4FLAGS General options that are passed to Perforce. @@ -6836,15 +5832,6 @@ dependencies for the PCH file. Example: env['PCH'] = 'StdAfx.pch' .EE -.IP PCHCOM -The command line used by the -.B PCH -builder to generated a precompiled header. - -.IP PCHCOMSTR -The string displayed when generating a precompiled header. -If this is not set, then $PCHCOM (the command line) is displayed. - .IP PCHSTOP This variable specifies how much of a source file is precompiled. This variable is ignored by tools other than Microsoft Visual C++, or when @@ -6941,11 +5928,6 @@ The suffix used for executable file names. .IP PSCOM The command line used to convert TeX DVI files into a PostScript file. -.IP PSCOMSTR -The string displayed when a TeX DVI file -is converted into a PostScript file. -If this is not set, then $PSCOM (the command line) is displayed. - .IP PSPREFIX The prefix used for PostScript file names. @@ -7044,17 +6026,9 @@ cpp file. .IP QT_MOCFROMCXXCOM Command to generate a moc file from a cpp file. -.IP QT_MOCFROMCXXCOMSTR -The string displayed when generating a moc file from a cpp file. -If this is not set, then $QT_MOCFROMCXXCOM (the command line) is displayed. - .IP QT_MOCFROMHCOM Command to generate a moc file from a header. -.IP QT_MOCFROMHCOMSTR -The string displayed when generating a moc file from a cpp file. -If this is not set, then $QT_MOCFROMHCOM (the command line) is displayed. - .IP QT_MOCFROMHFLAGS Default value is ''. These flags are passed to moc, when moccing a header file. @@ -7069,13 +6043,9 @@ a header. .IP QT_UIC Default value is '$QT_BINPATH/uic'. -.IP QT_UICCOM +.IP QT_UICDECLCOM Command to generate header files from .ui files. -.IP QT_UICCOMSTR -The string displayed when generating header files from .ui files. -If this is not set, then $QT_UICCOM (the command line) is displayed. - .IP QT_UICDECLFLAGS Default value is ''. These flags are passed to uic, when creating a a h file from a .ui file. @@ -7086,6 +6056,9 @@ Default value is ''. Prefix for uic generated header files. .IP QT_UICDECLSUFFIX Default value is '.h'. Suffix for uic generated header files. +.IP QT_UICIMPLCOM +Command to generate cxx files from .ui files. + .IP QT_UICIMPLFLAGS Default value is ''. These flags are passed to uic, when creating a cxx file from a .ui file. @@ -7107,17 +6080,10 @@ The archive indexer. General options passed to the archive indexer. .IP RC -The resource compiler used to build -a Microsoft Visual C++ resource file. +The resource compiler used by the RES builder. .IP RCCOM -The command line used to build -a Microsoft Visual C++ resource file. - -.IP RCCOMSTR -The string displayed when invoking the resource compiler -to build a Microsoft Visual C++ resource file. -If this is not set, then $RCCOM (the command line) is displayed. +The command line used by the RES builder. .IP RCFLAGS The flags passed to the resource compiler by the RES builder. @@ -7138,32 +6104,11 @@ used to fetch source files from RCS. The command line used to fetch (checkout) source files from RCS. -.IP RCS_COCOMSTR -The string displayed when fetching -a source file from RCS. -If this is not set, then $RCS_COCOM -(the command line) is displayed. - .IP RCS_COFLAGS Options that are passed to the $RCS_CO command. -.IP REGSVR -The program used to register DLLs on Windows systems. - -.IP REGSVRCOM -The command line used to register a newly-built DLL file -on Windows systems. -Invoked when the "register=1" -keyword argument is passed to the -.B SharedLibrary -Builder. - -.IP REGSVRCOMSTR -The string displayed when registering a newly-built DLL file. -If this is not set, then $REGSVRCOM (the command line) is displayed. - .IP RDirs -A function that converts a string into a list of Dir instances by +A function that converts a file name into a list of Dir instances by searching the repositories. .IP RMIC @@ -7176,16 +6121,6 @@ from Java classes that contain RMI implementations. Any options specified in the $RMICFLAGS construction variable are included on this command line. -.IP RMICCOMSTR -The string displayed when compiling -stub and skeleton class files -from Java classes that contain RMI implementations. -If this is not set, then $RMICCOM (the command line) is displayed. - -.ES -env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES") -.EE - .IP RMICFLAGS General options passed to the Java RMI stub compiler. @@ -7225,8 +6160,7 @@ construction variable. .IP RPATH 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. +Currently only used in the GNU linker (gnulink) and IRIX linker (sgilink). Ignored on platforms and toolchains that don't support it. Note that the paths added to RPATH are not transformed by @@ -7252,12 +6186,6 @@ The SCCS executable. The command line used to fetch source files from SCCS. -.IP SCCSCOMSTR -The string displayed when fetching -a source file from a CVS repository. -If this is not set, then $SCCSCOM -(the command line) is displayed. - .IP SCCSFLAGS General options that are passed to SCCS. @@ -7267,15 +6195,6 @@ This can be set, for example, to .I -e to check out editable files from SCCS. -.IP SCONS_HOME -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 -.B MSVSSCONS -command line executed -from Microsoft Visual Studio project files. - .IP SHCC The C compiler used for generating shared-library objects. @@ -7285,15 +6204,6 @@ to a shared-library object file. Any options specified in the $SHCCFLAGS and $CPPFLAGS construction variables are included on this command line. -.IP SHCCCOMSTR -The string displayed when a C source file -is compiled to a shared object file. -If this is not set, then $SHCCCOM (the command line) is displayed. - -.ES -env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET") -.EE - .IP SHCCFLAGS Options that are passed to the C compiler to generate shared-library objects. @@ -7307,15 +6217,6 @@ to a shared-library object file. Any options specified in the $SHCXXFLAGS and $CPPFLAGS construction variables are included on this command line. -.IP SHCXXCOMSTR -The string displayed when a C++ source file -is compiled to a shared object file. -If this is not set, then $SHCXXCOM (the command line) is displayed. - -.ES -env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET") -.EE - .IP SHCXXFLAGS Options that are passed to the C++ compiler to generate shared-library objects. @@ -7345,12 +6246,6 @@ You should normally set the $SHFORTRANCOM variable, which specifies the default command line for all Fortran versions. -.IP SHF77COMSTR -The string displayed when a Fortran 77 source file -is compiled to a shared-library object file. -If this is not set, then $SHF77COM or $SHFORTRANCOM -(the command line) is displayed. - .IP SHF77FLAGS Options that are passed to the Fortran 77 compiler to generated shared-library objects. @@ -7390,12 +6285,6 @@ You should normally set the $SHFORTRANCOM variable, which specifies the default command line for all Fortran versions. -.IP SHF90COMSTR -The string displayed when a Fortran 90 source file -is compiled to a shared-library object file. -If this is not set, then $SHF90COM or $SHFORTRANCOM -(the command line) is displayed. - .IP SHF90FLAGS Options that are passed to the Fortran 90 compiler to generated shared-library objects. @@ -7435,12 +6324,6 @@ You should normally set the $SHFORTRANCOM variable, which specifies the default command line for all Fortran versions. -.IP SHF95COMSTR -The string displayed when a Fortran 95 source file -is compiled to a shared-library object file. -If this is not set, then $SHF95COM or $SHFORTRANCOM -(the command line) is displayed. - .IP SHF95FLAGS Options that are passed to the Fortran 95 compiler to generated shared-library objects. @@ -7470,12 +6353,6 @@ The default Fortran compiler used for generating shared-library objects. The command line used to compile a Fortran source file to a shared-library object file. -.IP FORTRANCOMSTR -The string displayed when a Fortran source file -is compiled to a shared-library object file. -If this is not set, then $SHFORTRANCOM -(the command line) is displayed. - .IP SHFORTRANFLAGS Options that are passed to the Fortran compiler to generate shared-library objects. @@ -7497,17 +6374,6 @@ The suffix used for shared library file names. .IP SHLINK The linker for programs that use shared libraries. -.IP SHLINKCOM -The command line used to link programs using shared libaries. - -.IP SHLINKCOMSTR -The string displayed when programs using shared libraries are linked. -If this is not set, then $SHLINKCOM (the command line) is displayed. - -.ES -env = Environment(SHLINKCOMSTR = "Linking shared $TARGET") -.EE - .IP SHLINKFLAGS General user options passed to the linker for programs using shared libraries. Note that this variable should @@ -7597,11 +6463,6 @@ construction variable. The command line used to call the scripting language wrapper and interface generator. -.IP SWIGCOMSTR -The string displayed when calling -the scripting language wrapper and interface generator. -If this is not set, then $SWIGCOM (the command line) is displayed. - .IP SWIGCXXFILESUFFIX The suffix that will be used for intermediate C++ source files generated by @@ -7638,15 +6499,6 @@ The tar archiver. .IP TARCOM The command line used to call the tar archiver. -.IP TARCOMSTR -The string displayed when archiving files -using the tar archiver. -If this is not set, then $TARCOM (the command line) is displayed. - -.ES -env = Environment(TARCOMSTR = "Archiving $TARGET") -.EE - .IP TARFLAGS General options passed to the tar archiver. @@ -7663,38 +6515,15 @@ that may not be set or used in a construction environment. .IP TARSUFFIX The suffix used for tar file names. -.IP TEMPFILEPREFIX -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. - .IP TEX The TeX formatter and typesetter. .IP TEXCOM The command line used to call the TeX formatter and typesetter. -.IP TEXCOMSTR -The string displayed when calling -the TeX formatter and typesetter. -If this is not set, then $TEXCOM (the command line) is displayed. - -.ES -env = Environment(TEXCOMSTR = "Building $TARGET from TeX input $SOURCES") -.EE - .IP TEXFLAGS General options passed to the TeX formatter and typesetter. -.IP TEXINPUTS -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. - .IP TOOLS A list of the names of the Tool specifications that are part of this construction environment. @@ -7719,15 +6548,6 @@ The parser generator. The command line used to call the parser generator to generate a source file. -.IP YACCCOMSTR -The string displayed when generating a source file -using the parser generator. -If this is not set, then $YACCCOM (the command line) is displayed. - -.ES -env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") -.EE - .IP YACCFLAGS General options passed to the parser generator. If $YACCFLAGS contains a \-d option, @@ -7736,34 +6556,6 @@ SCons assumes that the call will also create a .h file or a .hpp file (if the yacc source file ends in a .yy suffix) -.IP YACCHFILESUFFIX -The suffix of the C -header file generated by the parser generator -when the -.B -d -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 -.BR .h . - -.IP YACCHXXFILESUFFIX -The suffix of the C++ -header file generated by the parser generator -when the -.B -d -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 -.BR .hpp . - .IP ZIP The zip compression and file packaging utility. @@ -7772,16 +6564,6 @@ The command line used to call the zip utility, or the internal Python function used to create a zip archive. -.IP ZIPCOMSTR -The string displayed when archiving files -using the zip utility. -If this is not set, then $ZIPCOM -(the command line or internal Python function) is displayed. - -.ES -env = Environment(ZIPCOMSTR = "Zipping $TARGET") -.EE - .IP ZIPCOMPRESSION The .I compression @@ -8032,7 +6814,7 @@ and selects the compiler to be used for the check; the default is "C". .TP -.RI Configure.CheckLib( self ", [" library ", " symbol ", " header ", " language ", " autoadd=1 ]) +.RI Configure.CheckLib( self ", [" library ", " symbol ", " header ", " language ", " autoadd ]) Checks if .I library provides @@ -8322,13 +7104,7 @@ 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. +is the default value of the variable. .I validator is called to validate the value of the variable, and should take three arguments: key, value, and environment @@ -8373,18 +7149,6 @@ the Environment() function: env = Environment(options=opts) .EE -.IP -The text file(s) that were specified -when the Options 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 Save( filename ", " env ) This saves the currently set options into a script file named @@ -8428,30 +7192,12 @@ Help(opts.GenerateHelpText(env)) Help(opts.GenerateHelpText(env, sort=cmp)) .EE -.TP -.RI FormatOptionHelpText( 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. +The text based SConscript file is executed as a Python script, and the +global variables are queried for customizable construction +variables. Example: .ES -def my_format(env, opt, help, default, actual): - fmt = "\n%s: default=%s actual=%s (%s)\n" - return fmt % (opt, default. actual, help) -opts.FormatOptionHelpText = my_format +CC = 'my_cc' .EE To make it more convenient to work with customizable Options, @@ -8541,7 +7287,7 @@ and all input values will be converted to lower case. .TP -.RI ListOption( key ", " help ", " default ", " names ", [", map ]) +.RI ListOption( key ", " help ", " default ", " names ) Return a tuple of arguments to set up an option whose value may be one or more @@ -8565,14 +7311,6 @@ 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 PackageOption( key ", " help ", " default ) @@ -8643,7 +7381,7 @@ which verifies that the specified path is an existing directory; and .BR PathOption.PathIsDirCreate , which verifies that the specified path is a directory, -and will create the specified directory if the path does not exist. +and will create the specified directory if the path exist. You may supply your own .I validator function, @@ -8894,17 +7632,8 @@ 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.) +for information about creating Scanner objects.) .IP target_factory A factory function that the Builder will use @@ -8929,15 +7658,6 @@ env.Append(BUILDERS = {'MakeDirectory':MakeDirectoryBuilder}) env.MakeDirectory('new_directory', []) .EE -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. @@ -9023,6 +7743,12 @@ b = Builder("my_build < $TARGET > $SOURCE", emitter = {'.suf1' : e_suf1, '.suf2' : e_suf2}) .EE +.IP +The +.I generator +and +.I action +arguments must not both be used for the same Builder. .IP multi Specifies whether this builder is allowed to be called multiple times for @@ -9072,13 +7798,6 @@ def g(source, target, env, for_signature): 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 @@ -9222,27 +7941,9 @@ 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. -Examples: .ES Action('$CC -c -o $TARGET $SOURCES') - -# Doesn't print the line being executed. -Action('@build $TARGET $SOURCES') - -# Ignores -Action('-build $TARGET $SOURCES') .EE .\" XXX From Gary Ruben, 23 April 2002: @@ -9432,32 +8133,6 @@ a = Action("build < ${SOURCE.file} > ${TARGET.file}", chdir=1) .EE -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 -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 - .SS Miscellaneous Action Functions .B scons @@ -9516,7 +8191,7 @@ that env = Environment(TMPBUILD = '/tmp/builddir') env.Command('foo.out', 'foo.in', [Mkdir('$TMPBUILD'), - Copy('$TMPBUILD', '${SOURCE.dir}') + Copy('${SOURCE.dir}', '$TMPBUILD') "cd $TMPBUILD && make", Delete('$TMPBUILD')]) .EE @@ -9770,7 +8445,7 @@ ${TARGET.filebase} => file ${TARGET.suffix} => .x ${TARGET.abspath} => /top/dir/sub/dir/file.x -SConscript('src/SConscript', build_dir='sub/dir') +BuildDir('sub/dir','src') $SOURCE => sub/dir/file.x ${SOURCE.srcpath} => src/file.x ${SOURCE.srcdir} => src @@ -10022,49 +8697,6 @@ 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_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, @@ -10312,8 +8944,6 @@ env['BUILDERS]['PDFBuilder'] = bld .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): @@ -10386,32 +9016,36 @@ subdirectory/SConscript: .SS Building Multiple Variants From the Same Source -Use the build_dir keyword argument to -the SConscript function to establish +Use the BuildDir() method to establish one or more separate build directories for -a given source directory: +a given source directory, +then use the SConscript() method +to specify the SConscript files +in the build directories: .ES SConstruct: - cppdefines = ['FOO'] - Export("cppdefines") - SConscript('src/SConscript', build_dir='foo') + ccflags = '-DFOO' + Export("ccflags") + BuildDir('foo', 'src') + SConscript('foo/SConscript') - cppdefines = ['BAR'] - Export("cppdefines") - SConscript('src/SConscript', build_dir='bar') + ccflags = '-DBAR' + Export("ccflags") + BuildDir('bar', 'src') + SConscript('bar/SConscript') src/SConscript: - Import("cppdefines") - env = Environment(CPPDEFINES = cppdefines) + Import("ccflags") + env = Environment(CCFLAGS = ccflags) 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. +to set the "ccflags" variable to a different +value for each variant build. .SS Hierarchical Build of Two Libraries Linked With a Program diff --git a/src/CHANGES.txt b/src/CHANGES.txt index cdf7e917..5a4062fa 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -10,16 +10,6 @@ RELEASE 0.97 - XXX - 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. - - - Fix the intelc.py Tool module to not throw an exception if the - only installed version is something other than ia32. - From Chad Austin: - Allow Help() to be called multiple times, appending to the help @@ -28,37 +18,6 @@ RELEASE 0.97 - XXX - Allow Tools found on a toolpath to import Python modules from their local directory. - - 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. - - - Add a separate MSVSSolution() Builder, with support for the - following new construction variables: $MSVSBUILDCOM, $MSVSCLEANCOM, - $MSVSENCODING, $MSVSREBUILDCOM, $MSVSSCONS, $MSVSSCONSCOM, - $MSVSSCONSFLAGS, $MSVSSCONSCRIPT and $MSVSSOLUTIONCOM. - - From Timothee Besset: - - - Add support for Objective C/C++ .m and .mm file suffixes (for - Mac OS X). - From Steve Christensen: - Handle exceptions from Python functions as build actions. @@ -66,22 +25,6 @@ RELEASE 0.97 - XXX - Add a set of canned PathOption validators: PathExists (the default), PathIsFile, PathIsDir and PathIsDirCreate. - Charles Crain - - - Fix the PharLap linkloc.py module to use target+source arguments - when calling env.subst(). - - From Matthew Doar: - - - Add support for .lex and .yacc file suffixes for Lex and Yacc files. - - 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 Eric Frias: - Huge performance improvement: wrap the tuples representing an @@ -94,25 +37,6 @@ RELEASE 0.97 - XXX check for the build engine in the parent directory of the Python library directory (/usr/lib64 instead of /usr/lib). - From Ralph W. Grosse-Kunstleve and Patrick Mezard: - - - Remove unneceesary (and incorrect) SCons.Util strings on some function - calls in SCons.Util. - - 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 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 @@ -176,291 +100,16 @@ RELEASE 0.97 - XXX - 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. - - - 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. - - - 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. - - From Chen Lee: - - - Handle Visual Studio project and solution files in Unicode. - - 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 Sanjoy Mahajan: - - - Correct TeX-related command lines to just $SOURCE, not $SOURCES - - - 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 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 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 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 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. - 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 Leanid Nazdrynau: - - - Fix the Java parser's handling of backslashes in strings. - - - 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 Christian Neeb: - Fix the Java parser's handling of string definitions to avoid ignoring @@ -473,20 +122,6 @@ RELEASE 0.97 - XXX value directory; avoiding slowing substitution logic when there's no '$' in the string. - From Jan Nieuwenhuizen: - - - Fix a problem with interpreting quoted argument lists on command lines. - - 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. - - - Add /sw/bin to the default execution PATH on Mac OS X. - From Gary Oberbrunner: - Add an Environment.Dump() method to print the contents of a @@ -511,65 +146,11 @@ RELEASE 0.97 - XXX - 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. - - - Add support for Intel C++ beta 9.0 (both 32 and 64 bit versions). - - - Document the new $FRAMEWORK* variables for 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 Chris Pawling: - Have the linkloc tool use $MSVS_VERSION to select the Microsoft Visual Studio version to use. - 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 Asfand Yar Qazi: - - - Add /opt/bin to the default execution PATH on all POSIX platforms - (between /usr/local/bin and /bin). - From Kevin Quick: - Fix the Builder name returned from ListBuilders and other instances @@ -637,122 +218,6 @@ RELEASE 0.97 - XXX 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 Christoph Schulz: - - - Add support for $CONFIGUREDIR and $CONFIGURELOG variables to control - the directory and logs for configuration tests. - - - Add support for a $INSTALLSTR variable. - - 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 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 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. - - - Add use of $CPPDEFINES to $RCCOM (resource file compilation) on MinGW. - - 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 Greg Ward: - - - Fix a misplaced line in the man page. - From Christoph Wiedemann: - Add an Environment.SetDefault() method that only sets values if @@ -799,17 +264,6 @@ RELEASE 0.97 - XXX - 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 - XXX diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 90839855..6afff589 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -126,7 +126,6 @@ import SCons.Action from SCons.Debug import logInstanceCreation from SCons.Errors import InternalError, UserError import SCons.Executor -import SCons.Node import SCons.Node.FS import SCons.Util import SCons.Warnings @@ -152,9 +151,6 @@ class DictCmdGenerator(SCons.Util.Selector): self[suffix] = action def __call__(self, target, source, env, for_signature): - if not source: - return [] - ext = None for src in map(str, source): my_ext = SCons.Util.splitext(src)[1] @@ -225,7 +221,6 @@ class OverrideWarner(UserDict.UserDict): """ 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: @@ -246,15 +241,12 @@ def Builder(**kw): 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']) + kw['action'] = SCons.Action.CommandGenerator(kw['generator']) del kw['generator'] - elif kw.has_key('action'): - if SCons.Util.is_Dict(kw['action']): - composite = DictCmdGenerator(kw['action']) - kw['action'] = SCons.Action.CommandGeneratorAction(composite) - kw['src_suffix'] = composite.src_suffixes() - else: - kw['action'] = SCons.Action.Action(kw['action']) + elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']): + composite = DictCmdGenerator(kw['action']) + kw['action'] = SCons.Action.CommandGenerator(composite) + kw['src_suffix'] = composite.src_suffixes() if kw.has_key('emitter'): emitter = kw['emitter'] @@ -282,10 +274,9 @@ def Builder(**kw): return ret -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. +def _init_nodes(builder, env, overrides, executor_kw, tlist, slist): + """Initialize lists of target and source nodes with all of + the proper Builder information. """ # First, figure out if there are any errors in the way the targets @@ -295,17 +286,22 @@ def _node_errors(builder, env, tlist, slist): raise UserError, "Multiple ways to build the same target were specified for: %s" % str(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) + t_contents = t.builder.action.get_contents(tlist, slist, t.env) + contents = t.builder.action.get_contents(tlist, slist, env) if t_contents == contents: SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, - "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), action.genstring(tlist, slist, t.env))) + "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), t.builder.action.strfunction(tlist, slist, t.env))) else: raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t) + elif t.overrides != overrides: + raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t) + + elif builder.target_scanner and t.target_scanner and builder.target_scanner != t.target_scanner: + raise UserError, "Two different scanners were specified for the same target: %s"%str(t) + if builder.multi: if t.builder != builder: if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder: @@ -321,6 +317,52 @@ def _node_errors(builder, env, tlist, slist): 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)) + # 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 + if builder.multi: + try: + executor = tlist[0].get_executor(create = 0) + except AttributeError: + pass + else: + executor.add_sources(slist) + if executor is None: + if not builder.action: + raise UserError, "Builder %s must have an action to build %s."%(builder.get_name(env or builder.env), map(str,tlist)) + executor = SCons.Executor.Executor(builder.action, + env or builder.env, + [builder.overrides, overrides], + tlist, + slist, + executor_kw) + + # Now set up the relevant information in the target Nodes themselves. + for t in tlist: + t.overrides = overrides + t.cwd = SCons.Node.FS.default_fs.getcwd() + t.builder_set(builder) + t.env_set(env) + t.add_source(slist) + t.set_executor(executor) + if builder.target_scanner: + t.target_scanner = builder.target_scanner + if t.source_scanner is None: + t.source_scanner = builder.source_scanner + + # Add backup source scanners from the environment to the source + # nodes. This may not be necessary if the node will have a real + # source scanner added later (which is why these are the "backup" + # source scanners, not the real ones), but because source nodes may + # be used multiple times for different targets, it ends up being + # more efficient to do this calculation once here, as opposed to + # delaying it until later when we potentially have to calculate it + # over and over and over. + for s in slist: + if s.source_scanner is None and s.backup_source_scanner is None: + s.backup_source_scanner = env.get_scanner(s.scanner_key()) + class EmitterProxy: """This is a callable class that can act as a Builder emitter. It holds on to a string that @@ -356,15 +398,12 @@ class BuilderBase: nodes (files) from input nodes (files). """ - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - def __init__(self, action = None, prefix = '', suffix = '', src_suffix = '', - target_factory = None, - source_factory = None, + target_factory = SCons.Node.FS.default_fs.File, + source_factory = SCons.Node.FS.default_fs.File, target_scanner = None, source_scanner = None, emitter = None, @@ -375,14 +414,15 @@ class BuilderBase: chdir = _null, is_explicit = 1, **overrides): - if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') - self.action = action + if __debug__: logInstanceCreation(self, 'BuilderBase') + self.action = SCons.Action.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.suffix = suffix self.env = env self.single_source = single_source if overrides.has_key('overrides'): @@ -398,7 +438,6 @@ class BuilderBase: del overrides['scanner'] self.overrides = overrides - self.set_suffix(suffix) self.set_src_suffix(src_suffix) self.target_factory = target_factory @@ -431,7 +470,7 @@ class BuilderBase: try: index = env['BUILDERS'].values().index(self) return env['BUILDERS'].keys()[index] - except (AttributeError, KeyError, TypeError, ValueError): + except (AttributeError, KeyError, ValueError): try: return self.name except AttributeError: @@ -451,25 +490,7 @@ class BuilderBase: return [path[:-len(suf)], path[-len(suf):]] return SCons.Util.splitext(path) - def get_single_executor(self, env, tlist, slist, executor_kw): - if not self.action: - raise UserError, "Builder %s must have an action to build %s."%(self.get_name(env or self.env), map(str,tlist)) - return self.action.get_executor(env or self.env, - [], # env already has overrides - tlist, - slist, - executor_kw) - - def get_multi_executor(self, env, tlist, slist, executor_kw): - try: - executor = tlist[0].get_executor(create = 0) - except (AttributeError, IndexError): - return self.get_single_executor(env, tlist, slist, executor_kw) - else: - executor.add_sources(slist) - return executor - - def _create_nodes(self, env, target = None, source = None): + def _create_nodes(self, env, overwarn, target = None, source = None): """Create and return lists of target and source nodes. """ def _adjustixes(files, pre, suf): @@ -485,13 +506,14 @@ class BuilderBase: result.append(f) return result - src_suf = self.get_src_suffix(env) + overwarn.warn() - target_factory = env.get_factory(self.target_factory) - source_factory = env.get_factory(self.source_factory) + env = env.Override(overwarn.data) + + src_suf = self.get_src_suffix(env) source = _adjustixes(source, None, src_suf) - slist = env.arg2nodes(source, source_factory) + slist = env.arg2nodes(source, self.source_factory) pre = self.get_prefix(env, slist) suf = self.get_suffix(env, slist) @@ -501,14 +523,11 @@ class BuilderBase: 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) ] + splitext = lambda S,self=self,env=env: self.splitext(S,env) + tlist = [ t_from_s(pre, suf, splitext) ] else: target = _adjustixes(target, pre, suf) - tlist = env.arg2nodes(target, target_factory) + tlist = env.arg2nodes(target, self.target_factory) if self.emitter: # The emitter is going to do str(node), but because we're @@ -519,88 +538,62 @@ class BuilderBase: new_targets = [] for t in tlist: if not t.is_derived(): - t.builder_set(self) + t.builder = self new_targets.append(t) 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 + # new targets, so that _init_nodes() 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) + t.builder = None # Have to call arg2nodes yet again, since it is legal for # emitters to spit out strings as well as Node instances. - slist = env.arg2nodes(source, source_factory) - tlist = env.arg2nodes(target, target_factory) + slist = env.arg2nodes(source, self.source_factory) + tlist = env.arg2nodes(target, self.target_factory) 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.single_source and len(source) > 1 and target is None: + def _execute(self, env, target=None, source=_null, overwarn={}, executor_kw={}): + if source is _null: + source = target + target = None + + if(self.single_source and + SCons.Util.is_List(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)) + for k in range(len(source)): + t = self._execute(env, target[k], source[k], overwarn) + if SCons.Util.is_List(t): + result.extend(t) + else: + result.append(t) return result - - overwarn.warn() - tlist, slist = self._create_nodes(env, target, source) + tlist, slist = self._create_nodes(env, overwarn, target, source) if len(tlist) == 1: builder = self else: builder = ListBuilder(self, env, tlist) + _init_nodes(builder, env, overwarn.data, executor_kw, tlist, slist) - # Check for errors with the specified target/source lists. - _node_errors(builder, env, tlist, slist) + return tlist - # The targets are fine, so find or make the appropriate Executor to - # build this particular list of targets from this particular list of - # sources. - if builder.multi: - get_executor = builder.get_multi_executor - else: - get_executor = builder.get_single_executor - executor = get_executor(env, tlist, slist, executor_kw) - - # Now set up the relevant information in the target Nodes themselves. - for t in tlist: - t.cwd = env.fs.getcwd() - t.builder_set(builder) - t.env_set(env) - t.add_source(slist) - t.set_executor(executor) - t.set_explicit(builder.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. + def __call__(self, env, target=None, source=_null, chdir=_null, **kw): if chdir is _null: ekw = self.executor_kw else: ekw = self.executor_kw.copy() ekw['chdir'] = chdir - if kw: - 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): @@ -614,29 +607,24 @@ class BuilderBase: 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) + else: + suffix = self.adjust_suffix(suffix) return env.subst(suffix) def src_suffixes(self, env): - "__cacheable__" - return map(lambda x, s=self, e=env: e.subst(x), self.src_suffix) + return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)), + self.src_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) + self.src_suffix = src_suffix def get_src_suffix(self, env): """Get the first src_suffix in the list of src_suffixes.""" @@ -662,21 +650,13 @@ class BuilderBase: """ self.emitter[suffix] = emitter -if SCons.Memoize.use_old_memoization(): - _Base = BuilderBase - class BuilderBase(SCons.Memoize.Memoizer, _Base): - "Cache-backed version of BuilderBase" - def __init__(self, *args, **kw): - apply(_Base.__init__, (self,)+args, kw) - SCons.Memoize.Memoizer.__init__(self) - class ListBuilder(SCons.Util.Proxy): """A Proxy to support building an array of targets (for example, foo.o and foo.h from foo.y) from a single Action execution. """ def __init__(self, builder, env, tlist): - if __debug__: logInstanceCreation(self, 'Builder.ListBuilder') + if __debug__: logInstanceCreation(self) SCons.Util.Proxy.__init__(self, builder) self.builder = builder self.target_scanner = builder.target_scanner @@ -712,13 +692,13 @@ class MultiStepBuilder(BuilderBase): prefix = '', suffix = '', src_suffix = '', - target_factory = None, - source_factory = None, + target_factory = SCons.Node.FS.default_fs.File, + source_factory = SCons.Node.FS.default_fs.File, target_scanner = None, source_scanner = None, emitter=None, single_source=0): - if __debug__: logInstanceCreation(self, 'Builder.MultiStepBuilder') + if __debug__: logInstanceCreation(self) BuilderBase.__init__(self, action, prefix, suffix, src_suffix, target_factory, source_factory, target_scanner, source_scanner, emitter, @@ -726,60 +706,50 @@ class MultiStepBuilder(BuilderBase): if not SCons.Util.is_List(src_builder): src_builder = [ src_builder ] self.src_builder = src_builder + self.sdict = {} + self.cached_src_suffixes = {} # source suffixes keyed on id(env) - def _get_sdict(self, env): - "__cacheable__" - sdict = {} - for bld in self.src_builder: - if SCons.Util.is_String(bld): - try: - bld = env['BUILDERS'][bld] - except KeyError: - continue - for suf in bld.src_suffixes(env): - sdict[suf] = bld - return sdict - - def _execute(self, env, target, source, overwarn={}, executor_kw={}): - # We now assume that target and source are lists or None. - source_factory = env.get_factory(self.source_factory) - slist = env.arg2nodes(source, source_factory) + def _execute(self, env, target = None, source = _null, overwarn={}, executor_kw={}): + if source is _null: + source = target + target = None + + slist = env.arg2nodes(source, self.source_factory) final_sources = [] - sdict = self._get_sdict(env) + try: + sdict = self.sdict[id(env)] + except KeyError: + sdict = {} + self.sdict[id(env)] = sdict + for bld in self.src_builder: + if SCons.Util.is_String(bld): + try: + bld = env['BUILDERS'][bld] + except KeyError: + continue + for suf in bld.src_suffixes(env): + sdict[suf] = bld src_suffixes = self.src_suffixes(env) - lengths_dict = {} - for l in map(len, src_suffixes): - lengths_dict[l] = None - lengths = lengths_dict.keys() - - def match_src_suffix(node, src_suffixes=src_suffixes, lengths=lengths): - node_suffixes = map(lambda l, n=node: n.name[-l:], lengths) - for suf in src_suffixes: - if suf in node_suffixes: - return suf - return None - for snode in slist: - match_suffix = match_src_suffix(snode) - if match_suffix: - try: - bld = sdict[match_suffix] - except KeyError: - final_sources.append(snode) - else: - tlist = bld._execute(env, None, [snode], 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: - tlist = filter(match_src_suffix, tlist) - final_sources.extend(tlist) - else: + for srcsuf in src_suffixes: + if str(snode)[-len(srcsuf):] == srcsuf and sdict.has_key(srcsuf): + tgt = sdict[srcsuf]._execute(env, None, snode, 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(tgt) > 1: + tgt = filter(lambda x, self=self, suf=src_suffixes, e=env: + self.splitext(SCons.Util.to_String(x),e)[1] in suf, + tgt) + final_sources.extend(tgt) + snode = None + break + if snode: final_sources.append(snode) - + return BuilderBase._execute(self, env, target, final_sources, overwarn) def get_src_builders(self, env): @@ -802,12 +772,15 @@ class MultiStepBuilder(BuilderBase): def src_suffixes(self, env): """Return a list of the src_suffix attributes for all src_builders of this Builder. - __cacheable__ """ - suffixes = BuilderBase.src_suffixes(self, env) - for builder in self.get_src_builders(env): - suffixes.extend(builder.src_suffixes(env)) - return suffixes + try: + return self.cached_src_suffixes[id(env)] + except KeyError: + suffixes = BuilderBase.src_suffixes(self, env) + for builder in self.get_src_builders(env): + suffixes.extend(builder.src_suffixes(env)) + self.cached_src_suffixes[id(env)] = suffixes + return suffixes class CompositeBuilder(SCons.Util.Proxy): """A Builder Proxy whose main purpose is to always have @@ -816,7 +789,7 @@ class CompositeBuilder(SCons.Util.Proxy): """ def __init__(self, builder, cmdgen): - if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') + if __debug__: logInstanceCreation(self) SCons.Util.Proxy.__init__(self, builder) # cmdgen should always be an instance of DictCmdGenerator. diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 233336c7..2075bf0e 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -34,7 +34,6 @@ import os.path import sys import types import unittest -import UserList import TestCmd @@ -55,9 +54,6 @@ 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() @@ -75,7 +71,6 @@ class Environment: 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 @@ -87,14 +82,6 @@ class Environment: except IndexError: pass return self.d.get(s, s) - def subst_target_source(self, string, raw=0, target=None, - source=None, dict=None, conv=None): - return SCons.Util.scons_subst(string, self, raw, target, - source, dict, conv) - def subst_list(self, string, raw=0, target=None, - source=None, dict=None, conv=None): - return SCons.Util.scons_subst_list(string, self, raw, target, - source, dict, conv) def arg2nodes(self, args, factory): global env_arg2nodes_called env_arg2nodes_called = 1 @@ -106,8 +93,6 @@ class Environment: a = factory(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): @@ -127,7 +112,6 @@ class Environment: 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) @@ -141,32 +125,23 @@ class Environment: 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 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.source_scanner = None + self.backup_source_scanner = None 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 + return not self.builder is None and self.builder.is_explicit def env_set(self, env, safe=0): self.env = env def add_source(self, source): @@ -231,11 +206,8 @@ class BuilderTestCase(unittest.TestCase): target_factory=MyNode, source_factory=MyNode) - tgt = builder(env, source=[]) - assert tgt == [], tgt - - n1 = MyNode("n1") - n2 = MyNode("n2") + n1 = MyNode("n1"); + n2 = MyNode("n2"); builder(env, target = n1, source = n2) assert env_arg2nodes_called assert n1.env == env, n1.env @@ -244,48 +216,15 @@ class BuilderTestCase(unittest.TestCase): 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 + target = builder(env, target = 'n3', source = 'n4')[0] 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] + target = builder(env, target = 'n4 n5', source = ['n6 n7'])[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] + target = builder(env, target = ['n8 n9'], source = 'n10 n11')[0] assert target.name == 'n8 n9' assert target.sources[0].name == 'n10 n11' @@ -315,7 +254,7 @@ class BuilderTestCase(unittest.TestCase): n20 = MyNode_without_target_from_source('n20') flag = 0 try: - target = builder(env, None, source=n20) + target = builder(env, source=n20) except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown if a source node can't create a target." @@ -325,7 +264,7 @@ class BuilderTestCase(unittest.TestCase): source_factory=MyNode, prefix='p-', suffix='.s') - target = builder(env, None, source='n21')[0] + target = builder(env, source='n21')[0] assert target.name == 'p-n21.s', target builder = SCons.Builder.Builder(misspelled_action="foo", @@ -392,29 +331,6 @@ class BuilderTestCase(unittest.TestCase): builder = SCons.Builder.Builder(generator=generator) assert builder.action.generator == generator - def test_get_name(self): - """Test the get_name() method - """ - - def test_get_single_executor(self): - """Test the get_single_executor() method - """ - b = SCons.Builder.Builder(action='foo') - x = b.get_single_executor({}, [], [], {}) - assert not x is None, x - - def test_get_multi_executor(self): - """Test the get_multi_executor() method - """ - b = SCons.Builder.Builder(action='foo', multi=1) - t1 = MyNode('t1') - s1 = MyNode('s1') - s2 = MyNode('s2') - x1 = b.get_multi_executor({}, [t1], [s1], {}) - t1.executor = x1 - x2 = b.get_multi_executor({}, [t1], [s2], {}) - assert x1 is x2, "%s is not %s" % (repr(x1), repr(x2)) - def test_cmp(self): """Test simple comparisons of Builder objects """ @@ -496,10 +412,10 @@ class BuilderTestCase(unittest.TestCase): 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] + tgt = builder(env, source = 'src3')[0] assert tgt.path == 'libsrc3', \ "Target has unexpected name: %s" % tgt.path - tgt = builder(env, target = None, source = 'lib/src4')[0] + tgt = builder(env, 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] @@ -523,35 +439,19 @@ class BuilderTestCase(unittest.TestCase): '$FOO' : 'foo-', '.zzz' : my_emit}, action = '') - tgt = builder(my_env, target = None, source = 'f1')[0] + tgt = builder(my_env, source = 'f1')[0] assert tgt.path == 'default-f1', tgt.path - tgt = builder(my_env, target = None, source = 'f2.c')[0] + tgt = builder(my_env, source = 'f2.c')[0] assert tgt.path == 'default-f2', tgt.path - tgt = builder(my_env, target = None, source = 'f3.in')[0] + tgt = builder(my_env, source = 'f3.in')[0] assert tgt.path == 'out-f3', tgt.path - tgt = builder(my_env, target = None, source = 'f4.x')[0] + tgt = builder(my_env, source = 'f4.x')[0] assert tgt.path == 'y-f4', tgt.path - tgt = builder(my_env, target = None, source = 'f5.foo')[0] + tgt = builder(my_env, source = 'f5.foo')[0] assert tgt.path == 'foo-f5', tgt.path - tgt = builder(my_env, target = None, source = 'f6.zzz')[0] + tgt = builder(my_env, 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 @@ -592,7 +492,7 @@ class BuilderTestCase(unittest.TestCase): b6 = SCons.Builder.Builder(action = '', src_suffix='_src.a', suffix='.b') - tgt = b6(env, target=None, source='foo_src.a') + tgt = b6(env, source='foo_src.a') assert str(tgt[0]) == 'foo.b', str(tgt[0]) b7 = SCons.Builder.Builder(action = '', @@ -601,16 +501,16 @@ class BuilderTestCase(unittest.TestCase): b8 = SCons.Builder.Builder(action = '', src_builder=b7, suffix='.c') - tgt = b8(env, target=None, source='foo_source.a') + tgt = b8(env, 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) + src = SCons.Node.FS.default_fs.File('foo_source.a') + tgt = b8(env, 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') + tgt = b9(env, source='foo_altsrc.b') assert str(tgt[0]) == 'foo.c', str(tgt[0]) def test_suffix(self): @@ -630,7 +530,7 @@ class BuilderTestCase(unittest.TestCase): 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] + tgt = builder(env, source = 'src5')[0] assert tgt.path == 'src5.o', \ "Target has unexpected name: %s" % tgt.path @@ -651,17 +551,17 @@ class BuilderTestCase(unittest.TestCase): '$BAR' : '.new', '.zzz' : my_emit}, action='') - tgt = builder(my_env, target = None, source = 'f1')[0] + tgt = builder(my_env, source = 'f1')[0] assert tgt.path == 'f1.default', tgt.path - tgt = builder(my_env, target = None, source = 'f2.c')[0] + tgt = builder(my_env, source = 'f2.c')[0] assert tgt.path == 'f2.default', tgt.path - tgt = builder(my_env, target = None, source = 'f3.in')[0] + tgt = builder(my_env, source = 'f3.in')[0] assert tgt.path == 'f3.out', tgt.path - tgt = builder(my_env, target = None, source = 'f4.x')[0] + tgt = builder(my_env, source = 'f4.x')[0] assert tgt.path == 'f4.y', tgt.path - tgt = builder(my_env, target = None, source = 'f5.bar')[0] + tgt = builder(my_env, source = 'f5.bar')[0] assert tgt.path == 'f5.new', tgt.path - tgt = builder(my_env, target = None, source = 'f6.zzz')[0] + tgt = builder(my_env, source = 'f6.zzz')[0] assert tgt.path == 'f6.emit', tgt.path def test_single_source(self): @@ -689,7 +589,7 @@ class BuilderTestCase(unittest.TestCase): tgt.prepare() tgt.build() assert env['CNT'][0] == 2 - tgts = builder(env, None, infiles[2:4]) + tgts = builder(env, infiles[2:4]) for t in tgts: t.prepare() tgts[0].build() tgts[1].build() @@ -721,11 +621,7 @@ class BuilderTestCase(unittest.TestCase): env = Environment() builder = SCons.Builder.Builder(action = function2) - - tgts = builder(env, source=[]) - assert tgts == [], tgts - - tgts = builder(env, target = [outfile, outfile2], source = infile) + tgts = builder(env, target = [outfile, outfile2], source = 'foo') for t in tgts: t.prepare() try: @@ -749,7 +645,7 @@ class BuilderTestCase(unittest.TestCase): return 1 builder = SCons.Builder.Builder(action = function3) - tgts = builder(env, target = [sub1_out, sub2_out], source = infile) + tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo') for t in tgts: t.prepare() try: @@ -769,29 +665,23 @@ class BuilderTestCase(unittest.TestCase): builder1 = SCons.Builder.Builder(action='foo', src_suffix='.bar', suffix='.foo') - builder2 = SCons.Builder.MultiStepBuilder(action=MyAction('act'), + builder2 = SCons.Builder.MultiStepBuilder(action='bar', src_builder = builder1, src_suffix = '.foo') - tgt = builder2(env, source=[]) - assert tgt == [], tgt - tgt = builder2(env, target='baz', source=['test.bar', 'test2.foo', 'test3.txt'])[0] - s = str(tgt) - assert s == 'baz', s - s = map(str, tgt.sources) - assert s == ['test.foo', 'test2.foo', 'test3.txt'], 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 + assert str(tgt.sources[0]) == 'test.foo', str(tgt.sources[0]) + assert str(tgt.sources[0].sources[0]) == 'test.bar', \ + str(tgt.sources[0].sources[0]) + assert str(tgt.sources[1]) == 'test2.foo', str(tgt.sources[1]) + assert str(tgt.sources[2]) == 'test3.txt', str(tgt.sources[2]) + + tgt = builder2(env, 'aaa.bar')[0] + assert str(tgt) == 'aaa', str(tgt) + assert str(tgt.sources[0]) == 'aaa.foo', str(tgt.sources[0]) + assert str(tgt.sources[0].sources[0]) == 'aaa.bar', \ + str(tgt.sources[0].sources[0]) builder3 = SCons.Builder.MultiStepBuilder(action = 'foo', src_builder = 'xyzzy', @@ -801,23 +691,21 @@ class BuilderTestCase(unittest.TestCase): builder4 = SCons.Builder.Builder(action='bld4', src_suffix='.i', suffix='_wrap.c') - builder5 = SCons.Builder.MultiStepBuilder(action=MyAction('act'), + builder5 = SCons.Builder.MultiStepBuilder(action='bld5', src_builder=builder4, suffix='.obj', src_suffix='.c') - builder6 = SCons.Builder.MultiStepBuilder(action=MyAction('act'), + builder6 = SCons.Builder.MultiStepBuilder(action='bld6', 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 + assert str(tgt) == 'test.exe', str(tgt) + assert str(tgt.sources[0]) == 'test_wrap.obj', str(tgt.sources[0]) + assert str(tgt.sources[0].sources[0]) == 'test_wrap.c', \ + str(tgt.sources[0].sources[0]) + assert str(tgt.sources[0].sources[0].sources[0]) == 'test.i', \ + str(tgt.sources[0].sources[0].sources[0]) def test_CompositeBuilder(self): """Testing CompositeBuilder class.""" @@ -829,21 +717,15 @@ class BuilderTestCase(unittest.TestCase): '.bar' : func_action, '$BAR_SUFFIX' : func_action, '$FOO_SUFFIX' : func_action }) - - tgt = builder(env, source=[]) - assert tgt == [], tgt assert isinstance(builder, SCons.Builder.CompositeBuilder) assert isinstance(builder.action, SCons.Action.CommandGeneratorAction) - 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 - flag = 0 tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0] try: @@ -970,8 +852,8 @@ class BuilderTestCase(unittest.TestCase): 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 + assert tgt.target_scanner == tscan, tgt.target_scanner + assert tgt.source_scanner == sscan, tgt.source_scanner builder1 = SCons.Builder.Builder(action='foo', src_suffix='.bar', @@ -981,8 +863,8 @@ class BuilderTestCase(unittest.TestCase): 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 + assert tgt.target_scanner == tscan, tgt.target_scanner + assert tgt.source_scanner == tscan, tgt.source_scanner def test_actual_scanner(self): """Test usage of actual Scanner objects.""" @@ -1008,84 +890,28 @@ class BuilderTestCase(unittest.TestCase): 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() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) - - # 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] + assert tgt.target_scanner != scanner, tgt.target_scanner + assert src.source_scanner is None, src.source_scanner + assert src.backup_source_scanner is None, src.backup_source_scanner + + # Later use of the same source file with an environment that + # has a scanner must still set the scanner. + env2 = Environment() + env2.scanner = scanner + tgt = builder(env2, 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() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) - - # 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() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) - - # 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() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) - - + assert tgt.target_scanner != scanner, tgt.target_scanner + assert src.source_scanner is None, src.source_scanner + assert src.backup_source_scanner == scanner, src.backup_source_scanner def test_Builder_API(self): """Test Builder interface. @@ -1095,8 +921,8 @@ class BuilderTestCase(unittest.TestCase): forms of component specifications.""" builder = SCons.Builder.Builder() - env = Environment(BUILDERS={'Bld':builder}) + env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) @@ -1111,34 +937,23 @@ class BuilderTestCase(unittest.TestCase): assert r == ['foo'], 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) + builder.set_src_suffix('.foo') + r = builder.get_src_suffix(env) assert r == '.foo', r - r = bld.src_suffixes(env) + r = builder.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) + builder.set_src_suffix(['.foo', '.bar']) + r = builder.get_src_suffix(env) assert r == '.foo', r - r = bld.src_suffixes(env) + r = builder.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) + builder.set_src_suffix(['.bar', '.foo']) + r = builder.get_src_suffix(env) assert r == '.bar', r - r = bld.src_suffixes(env) + r = builder.src_suffixes(env) assert r == ['.bar', '.foo'], r # adjust_suffix normalizes the suffix, adding a `.' if needed @@ -1211,8 +1026,8 @@ class BuilderTestCase(unittest.TestCase): assert r == '.D', r builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={}) - env = Environment(BUILDERS={'Bld':builder}) + env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) @@ -1228,10 +1043,7 @@ class BuilderTestCase(unittest.TestCase): # 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) @@ -1245,9 +1057,6 @@ class BuilderTestCase(unittest.TestCase): 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) @@ -1375,11 +1184,11 @@ class BuilderTestCase(unittest.TestCase): '.4b':emit4b}, target_factory=MyNode, source_factory=MyNode) - tgt = builder4(env, None, source='aaa.4a')[0] + tgt = builder4(env, source='aaa.4a')[0] assert str(tgt) == 'emit4a-aaa', str(tgt) - tgt = builder4(env, None, source='bbb.4b')[0] + tgt = builder4(env, source='bbb.4b')[0] assert str(tgt) == 'emit4b-bbb', str(tgt) - tgt = builder4(env, None, source='ccc.4c')[0] + tgt = builder4(env, source='ccc.4c')[0] assert str(tgt) == 'ccc', str(tgt) def emit4c(target, source, env): @@ -1387,7 +1196,7 @@ class BuilderTestCase(unittest.TestCase): target = map(lambda x: 'emit4c-' + x[:-3], source) return (target, source) builder4.add_emitter('.4c', emit4c) - tgt = builder4(env, None, source='ccc.4c')[0] + tgt = builder4(env, source='ccc.4c')[0] assert str(tgt) == 'emit4c-ccc', str(tgt) # Test a list of emitter functions. @@ -1432,43 +1241,43 @@ class BuilderTestCase(unittest.TestCase): env = Environment() b = SCons.Builder.Builder(action='foo', suffix='.o') - tgt = b(env, None, 'aaa')[0] + tgt = b(env, '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] + tgt = b(env, '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] + tgt = b(env, '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] + tgt = b(env, ['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] + tgt = b(env, 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] + tgt = b(env, 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] + tgt = b(env, 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] + tgt = b(env, 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) @@ -1476,7 +1285,7 @@ class BuilderTestCase(unittest.TestCase): 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] + tgt = b(env, 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) @@ -1507,31 +1316,18 @@ class BuilderTestCase(unittest.TestCase): 'B2': b2, 'B3': b3, 'B4': b4}) - # With no name, get_name will return the class. Allow - # for caching... - b6_names = [ - 'SCons.Builder.BuilderBase', - "", - '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 b6.get_name(env) == 'SCons.Builder.BuilderBase', 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) + assert b6.get_name(env2) == 'SCons.Builder.BuilderBase', b6.get_name(env2) for B in b3.get_src_builders(env): assert B.get_name(env) == 'bldr1' @@ -1540,8 +1336,7 @@ class BuilderTestCase(unittest.TestCase): tgts = b1(env, target = [outfile, outfile2], source='moo') for t in tgts: - name = t.builder.get_name(env) - assert name == 'ListBuilder(bldr1)', name + assert t.builder.get_name(env) == 'ListBuilder(bldr1)' # The following are not symbolically correct, because the # ListBuilder was only created on behalf of env, so it # would probably be OK if better correctness diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 810ede73..883b82c9 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -5,8 +5,9 @@ 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. +This initializes a "default_fs" Node with an FS at the current directory +for its own purposes, and for use by scripts or modules looking for the +canonical default. """ @@ -49,14 +50,9 @@ from SCons.Debug import logInstanceCreation import SCons.Errors import SCons.Node import SCons.Sig.MD5 -import SCons.Subst import SCons.Util import SCons.Warnings -# 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 @@ -147,7 +143,7 @@ def LinkFunc(target, source, env): src = source[0].abspath dest = target[0].abspath dir, file = os.path.split(dest) - if dir and not target[0].fs.isdir(dir): + if dir and not os.path.isdir(dir): os.makedirs(dir) if not Link_Funcs: # Set a default order of link functions. @@ -157,13 +153,7 @@ def LinkFunc(target, source, env): try: func(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. + except OSError: if func == Link_Funcs[-1]: # exception of the last link method (copy) are fatal raise @@ -186,8 +176,9 @@ Unlink = SCons.Action.Action(UnlinkFunc, None) def MkdirFunc(target, source, env): t = target[0] - if not t.exists(): - t.fs.mkdir(t.abspath) + p = t.abspath + if not t.fs.exists(p): + t.fs.mkdir(p) return 0 Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) @@ -247,12 +238,10 @@ def CachePushFunc(target, source, env): fs.rename(tempfile, cachefile) st = fs.stat(t.path) fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - except (IOError, OSError): - # 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. + except OSError: + # It's possible someone else tried writing the file at the same + # time we did. Print a warning but don't stop the build, since + # it doesn't affect the correctness of the build. SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, "Unable to copy %s to cache. Cache file is %s" % (str(target), cachefile)) @@ -274,8 +263,7 @@ def get_DefaultSCCSBuilder(): 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, + DefaultSCCSBuilder = SCons.Builder.Builder(action = '$SCCSCOM', env = None, name = "DefaultSCCSBuilder") return DefaultSCCSBuilder @@ -286,12 +274,49 @@ def get_DefaultRCSBuilder(): 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, + DefaultRCSBuilder = SCons.Builder.Builder(action = '$RCS_COCOM', env = None, name = "DefaultRCSBuilder") return DefaultRCSBuilder +# +class ParentOfRoot: + """ + An instance of this class is used as the parent of the root of a + filesystem (POSIX) or drive (Win32). This isn't actually a node, + but it looks enough like one so that we don't have to have + special purpose code everywhere to deal with dir being None. + This class is an instance of the Null object pattern. + """ + def __init__(self): + self.abspath = '' + self.path = '' + self.name='' + self.duplicate=0 + self.srcdir=None + self.build_dirs=[] + + def is_under(self, dir): + return 0 + + def up(self): + return None + + def getRepositories(self): + return [] + + def get_dir(self): + return None + + def src_builder(self): + return _null + + def entry_abspath(self, name): + return name + + def entry_path(self, name): + return name + # 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: @@ -301,96 +326,31 @@ 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): - path = node.abspath - if predicate(path): - raise TypeError, errorfmt % path - -def ignore_diskcheck_match(node, predicate, errorfmt): - pass - -def do_diskcheck_rcs(node, name): - try: - rcs_dir = node.rcs_dir - except AttributeError: - rcs_dir = node.rcs_dir = node.Dir('RCS') - return rcs_dir.entry_exists_on_disk(name+',v') - -def ignore_diskcheck_rcs(node, name): - return None - -def do_diskcheck_sccs(node, name): - try: - sccs_dir = node.sccs_dir - except AttributeError: - sccs_dir = node.sccs_dir = node.Dir('SCCS') - return sccs_dir.entry_exists_on_disk('s.'+name) - -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(), + return SCons.Util.SpecialAttrWrapper(entry.get_abspath(), entry.name + "_abspath") def __get_filebase(self): name = self.get().name - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0], + return SCons.Util.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], + return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[1], name + "_suffix") def __get_file(self): name = self.get().name - return SCons.Subst.SpecialAttrWrapper(name, name + "_file") + return SCons.Util.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], + return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], entry.name + "_base") def __get_posix_path(self): @@ -401,7 +361,7 @@ class EntryProxy(SCons.Util.Proxy): else: entry = self.get() r = string.replace(entry.get_path(), os.sep, '/') - return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") + return SCons.Util.SpecialAttrWrapper(r, entry.name + "_posix") def __get_win32_path(self): """Return the path with \ as the path separator, @@ -411,7 +371,7 @@ class EntryProxy(SCons.Util.Proxy): else: entry = self.get() r = string.replace(entry.get_path(), os.sep, '\\') - return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_win32") + return SCons.Util.SpecialAttrWrapper(r, entry.name + "_win32") def __get_srcnode(self): return EntryProxy(self.get().srcnode()) @@ -457,12 +417,6 @@ class EntryProxy(SCons.Util.Proxy): except AttributeError: entry = self.get() classname = string.split(str(entry.__class__), '.')[-1] - if classname[-2:] == "'>": - # new-style classes report their name as: - # "" - # instead of the classic classes: - # "something" - classname = classname[:-2] raise AttributeError, "%s instance '%s' has no attribute '%s'" % (classname, entry.name, name) return attr @@ -491,6 +445,7 @@ class Base(SCons.Node.Node): self.name = name self.fs = fs + self.relpath = {self : '.'} assert directory, "A directory must be provided" @@ -499,11 +454,6 @@ class Base(SCons.Node.Node): self.path = name else: self.path = directory.entry_path(name) - if directory.tpath == '.': - self.tpath = name - else: - self.tpath = 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 @@ -513,15 +463,26 @@ class Base(SCons.Node.Node): """Completely clear a Node.FS.Base object of all its cached state (so that it can be re-evaluated by interfaces that do continuous integration builds). - __cache_reset__ """ SCons.Node.Node.clear(self) + try: + delattr(self, '_exists') + except AttributeError: + pass + try: + delattr(self, '_rexists') + except AttributeError: + pass + try: + delattr(self, '_str_val') + except AttributeError: + pass + self.relpath = {self : '.'} def get_dir(self): return self.dir def get_suffix(self): - "__cacheable__" return SCons.Util.splitext(self.name)[1] def rfile(self): @@ -530,61 +491,33 @@ class Base(SCons.Node.Node): 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() - - def _save_str(self): - "__cacheable__" - return self._get_str() - - def _get_str(self): - if self.duplicate or self.is_derived(): - return self.get_path() - return self.srcnode().get_path() + try: + return self._str_val + except AttributeError: + global Save_Strings + if self.duplicate or self.is_derived(): + str_val = self.get_path() + else: + str_val = self.srcnode().get_path() + if Save_Strings: + self._str_val = str_val + return str_val rstr = __str__ - def stat(self): - "__cacheable__" - try: return self.fs.stat(self.abspath) - except os.error: return None - def exists(self): - "__cacheable__" - return not self.stat() is None + try: + return self._exists + except AttributeError: + self._exists = self.fs.exists(self.abspath) + return self._exists def rexists(self): - "__cacheable__" - 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 not st is None and stat.S_ISDIR(st[stat.ST_MODE]) - - def isfile(self): - st = self.stat() - return not st is 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 + try: + return self._rexists + except AttributeError: + self._rexists = self.rfile().exists() + return self._rexists def is_under(self, dir): if self is dir: @@ -598,33 +531,40 @@ class Base(SCons.Node.Node): def srcnode(self): """If this node is in a build path, return the node corresponding to its source file. Otherwise, return - ourself. - __cacheable__""" - dir=self.dir - name=self.name - while dir: - if dir.srcdir: - srcnode = self.fs.Entry(name, dir.srcdir, - klass=self.__class__) - return srcnode - name = dir.name + os.sep + name - dir = dir.up() - return self + ourself.""" + try: + return self._srcnode + except AttributeError: + dir=self.dir + name=self.name + while dir: + if dir.srcdir: + self._srcnode = self.fs.Entry(name, dir.srcdir, + klass=self.__class__) + return self._srcnode + name = dir.name + os.sep + name + dir=dir.get_dir() + self._srcnode = self + return self._srcnode 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) - + try: + return self.relpath[dir] + except KeyError: + path_elems = [] + d = self + while d != dir and not isinstance(d, ParentOfRoot): + path_elems.append(d.name) + d = d.dir + path_elems.reverse() + ret = string.join(path_elems, os.sep) + self.relpath[dir] = ret + return ret + def set_src_builder(self, builder): """Set the source code builder for this node.""" self.sbuilder = builder @@ -671,19 +611,6 @@ class Entry(Base): time comes, and then call the same-named method in the transformed class.""" - def diskcheck_match(self): - pass - - def disambiguate(self): - if self.isdir(): - self.__class__ = Dir - self._morph() - 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.""" @@ -692,10 +619,11 @@ class Entry(Base): self.clear() return File.rfile(self) - def get_found_includes(self, env, scanner, path): + def get_found_includes(self, env, scanner, target): """If we're looking for included files, it's because this Entry is really supposed to be a File itself.""" - return self.disambiguate().get_found_includes(env, scanner, path) + node = self.rfile() + return node.get_found_includes(env, scanner, target) def scanner_key(self): return self.get_suffix() @@ -706,39 +634,51 @@ class Entry(Base): Since this should return the real contents from the file system, we check to see into what sort of subclass we should morph this Entry.""" - if self.isfile(): + if self.fs.isfile(self.abspath): self.__class__ = File self._morph() - return self.get_contents() - if self.isdir(): + return File.get_contents(self) + if self.fs.isdir(self.abspath): self.__class__ = Dir self._morph() - return self.get_contents() - if self.islink(): + return Dir.get_contents(self) + if self.fs.islink(self.abspath): return '' # avoid errors for dangling symlinks raise AttributeError - def rel_path(self, other): - return self.disambiguate().rel_path(other) - 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() + if self.fs.isdir(self.abspath): + self.__class__ = Dir + self._morph() + return Dir.exists(self) + else: + self.__class__ = File + self._morph() + self.clear() + return File.exists(self) def calc_signature(self, calc=None): """Return the Entry's calculated signature. Check the file system to see what we should turn into first. Assume a file if there's no directory.""" - return self.disambiguate().calc_signature(calc) + if self.fs.isdir(self.abspath): + self.__class__ = Dir + self._morph() + return Dir.calc_signature(self, calc) + else: + self.__class__ = File + self._morph() + self.clear() + return File.calc_signature(self, calc) def must_be_a_Dir(self): """Called to make sure a Node is a Dir. Since we're an Entry, we can morph into one.""" self.__class__ = Dir self._morph() - return self # This is for later so we can differentiate between Entry the class and Entry # the method of the FS class. @@ -746,10 +686,6 @@ _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 @@ -774,16 +710,12 @@ class LocalFS: 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): @@ -804,17 +736,12 @@ class LocalFS: if hasattr(os, 'symlink'): def islink(self, path): return os.path.islink(path) + def exists_or_islink(self, path): + return os.path.exists(path) or os.path.islink(path) else: def islink(self, path): return 0 # no symlinks - -if SCons.Memoize.use_old_memoization(): - _FSBase = LocalFS - class LocalFS(SCons.Memoize.Memoizer, _FSBase): - def __init__(self, *args, **kw): - apply(_FSBase.__init__, (self,)+args, kw) - SCons.Memoize.Memoizer.__init__(self) - + exists_or_islink = exists #class RemoteFS: # # Skeleton for the obvious methods we might need from the @@ -826,7 +753,6 @@ if SCons.Memoize.use_old_memoization(): class FS(LocalFS): - def __init__(self, path = None): """Initialize the Node.FS subsystem. @@ -836,38 +762,33 @@ class FS(LocalFS): The path argument must be a valid absolute path. """ - if __debug__: logInstanceCreation(self, 'Node.FS') + if __debug__: logInstanceCreation(self) + self.Top = None + if path == None: + self.pathTop = os.getcwd() + else: + self.pathTop = path self.Root = {} self.SConstruct_dir = None self.CachePath = None self.cache_force = None self.cache_show = None - self.max_drift = default_max_drift - if path is None: - self.pathTop = os.getcwd() - else: - self.pathTop = path + def set_toplevel_dir(self, path): + assert not self.Top, "You can only set the top-level path on an FS object that has not had its File, Dir, or Entry methods called yet." + self.pathTop = path - self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop)) - self.Top.path = '.' - self.Top.tpath = '.' - self._cwd = self.Top - - def clear_cache(self): - "__cache_reset__" - pass - 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 __setTopLevelDir(self): + if not self.Top: + self.Top = self.__doLookup(Dir, os.path.normpath(self.pathTop)) + self.Top.path = '.' + self._cwd = self.Top + def getcwd(self): + self.__setTopLevelDir() return self._cwd def __checkClass(self, node, klass): @@ -880,20 +801,18 @@ class FS(LocalFS): raise TypeError, "Tried to lookup %s '%s' as a %s." % \ (node.__class__.__name__, node.path, klass.__name__) - def _doLookup(self, fsclass, name, directory = None, create = 1): + def __doLookup(self, fsclass, name, directory = None, create = 1): """This method differs from the File and Dir factory methods in one important way: the meaning of the directory parameter. In this method, if directory is None or not supplied, the supplied name is expected to be an absolute path. If you try to look up a relative path with directory=None, then an AssertionError will be - raised. - __cacheable__""" + raised.""" if not name: - # This is a stupid hack to compensate for the fact that - # the POSIX and Win32 versions of os.path.normpath() behave - # differently in older versions of Python. In particular, - # in POSIX: + # This is a stupid hack to compensate for the fact + # that the POSIX and Win32 versions of os.path.normpath() + # behave differently. In particular, in POSIX: # os.path.normpath('./') == '.' # in Win32 # os.path.normpath('./') == '' @@ -902,81 +821,76 @@ class FS(LocalFS): # This is a definite bug in the Python library, but we have # to live with it. name = '.' - path_orig = string.split(name, os.sep) - path_norm = string.split(_my_normcase(name), os.sep) - - first_orig = path_orig.pop(0) # strip first element - first_norm = path_norm.pop(0) # strip first element - - drive, path_first = os.path.splitdrive(first_orig) - if path_first: - path_orig = [ path_first, ] + path_orig - path_norm = [ _my_normcase(path_first), ] + path_norm - else: - drive = _my_normcase(drive) + path_comp = string.split(name, os.sep) + drive, path_first = os.path.splitdrive(path_comp[0]) + if not path_first: # Absolute path + drive = _my_normcase(drive) try: directory = self.Root[drive] except KeyError: if not create: raise SCons.Errors.UserError - directory = RootDir(drive, self) + directory = RootDir(drive, ParentOfRoot(), self) self.Root[drive] = directory + path_comp = path_comp[1:] + else: + path_comp = [ path_first, ] + path_comp[1:] - if not path_orig: - return directory - - last_orig = path_orig.pop() # strip last element - last_norm = path_norm.pop() # strip last element + if not path_comp: + path_comp = [''] # Lookup the directory - for orig, norm in map(None, path_orig, path_norm): - try: - entries = directory.entries - except AttributeError: - # We tried to look up the entry in either an Entry or - # a File. Give whatever it is a chance to do what's - # appropriate: morph into a Dir or raise an exception. - directory.must_be_a_Dir() - entries = directory.entries + for path_name in path_comp[:-1]: + path_norm = _my_normcase(path_name) try: - directory = entries[norm] + d = directory.entries[path_norm] except KeyError: if not create: raise SCons.Errors.UserError - d = Dir(orig, directory, self) - - # Check the file system (or not, as configured) to make - # sure there isn't already a file there. - d.diskcheck_match() - - directory.entries[norm] = d - directory.add_wkid(d) + # look at the actual filesystem and make sure there isn't + # a file already there + path = directory.entry_path(path_name) + if self.isfile(path): + raise TypeError, \ + "File %s found where directory expected." % path + + dir_temp = Dir(path_name, directory, self) + directory.entries[path_norm] = dir_temp + directory.add_wkid(dir_temp) + directory = dir_temp + else: + d.must_be_a_Dir() directory = d - directory.must_be_a_Dir() - + entry_norm = _my_normcase(path_comp[-1]) try: - e = directory.entries[last_norm] + e = directory.entries[entry_norm] except KeyError: if not create: raise SCons.Errors.UserError - result = fsclass(last_orig, directory, self) - - # Check the file system (or not, as configured) to make - # sure there isn't already a directory at the path on - # disk where we just created a File node, and vice versa. - result.diskcheck_match() - - directory.entries[last_norm] = result + # make sure we don't create File nodes when there is actually + # a directory at that path on the disk, and vice versa + path = directory.entry_path(path_comp[-1]) + if fsclass == File: + if self.isdir(path): + raise TypeError, \ + "Directory %s found where file expected." % path + elif fsclass == Dir: + if self.isfile(path): + raise TypeError, \ + "File %s found where directory expected." % path + + result = fsclass(path_comp[-1], directory, self) + directory.entries[entry_norm] = result directory.add_wkid(result) else: result = self.__checkClass(e, fsclass) return result - def _transformPath(self, name, directory): + def __transformPath(self, name, directory): """Take care of setting up the correct top-level directory, usually in preparation for a call to doLookup(). @@ -986,6 +900,7 @@ class FS(LocalFS): If directory is None, and name is a relative path, then the same applies. """ + self.__setTopLevelDir() if name and name[0] == '#': directory = self.Top name = name[1:] @@ -1003,6 +918,7 @@ class FS(LocalFS): If change_os_dir is true, we will also change the "real" cwd to match. """ + self.__setTopLevelDir() curr=self._cwd try: if not dir is None: @@ -1029,8 +945,8 @@ class FS(LocalFS): else: if directory and not isinstance(directory, Dir): directory = self.Dir(directory) - name, directory = self._transformPath(name, directory) - return self._doLookup(klass, name, directory, create) + name, directory = self.__transformPath(name, directory) + return self.__doLookup(klass, name, directory, create) def File(self, name, directory = None, create = 1): """Lookup or create a File node with the specified name. If @@ -1062,10 +978,13 @@ class FS(LocalFS): """Link the supplied build directory to the source directory for purposes of building files.""" + self.__setTopLevelDir() if not isinstance(src_dir, SCons.Node.Node): src_dir = self.Dir(src_dir) if not isinstance(build_dir, SCons.Node.Node): build_dir = self.Dir(build_dir) + if not src_dir.is_under(self.Top): + raise SCons.Errors.UserError, "Source directory must be under top of build tree." if src_dir.is_under(build_dir): raise SCons.Errors.UserError, "Source directory cannot be under build directory." if build_dir.srcdir: @@ -1079,51 +998,134 @@ class FS(LocalFS): for d in dirs: if not isinstance(d, SCons.Node.Node): d = self.Dir(d) + self.__setTopLevelDir() self.Top.addRepository(d) - def Rfindalldirs(self, pathlist, cwd): - """__cacheable__""" + def Rsearch(self, path, clazz=_classEntry, cwd=None): + """Search for something in a Repository. Returns the first + one found in the list, or None if there isn't one.""" + if isinstance(path, SCons.Node.Node): + return path + else: + name, d = self.__transformPath(path, cwd) + n = self.__doLookup(clazz, name, d) + if n.exists(): + return n + if isinstance(n, Dir): + # If n is a Directory that has Repositories directly + # attached to it, then any of those is a valid Repository + # path. Return the first one that exists. + reps = filter(lambda x: x.exists(), n.getRepositories()) + if len(reps): + return reps[0] + d = n.get_dir() + name = n.name + # Search repositories of all directories that this file is under. + while d: + for rep in d.getRepositories(): + try: + rnode = self.__doLookup(clazz, name, rep) + # Only find the node if it exists and it is not + # a derived file. If for some reason, we are + # explicitly building a file IN a Repository, we + # don't want it to show up in the build tree. + # This is usually the case with BuildDir(). + # We only want to find pre-existing files. + if rnode.exists() and \ + (isinstance(rnode, Dir) or not rnode.is_derived()): + return rnode + except TypeError: + pass # Wrong type of node. + # Prepend directory name + name = d.name + os.sep + name + # Go up one directory + d = d.get_dir() + return None + + def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None): + """Search for a list of somethings in the Repository list.""" + ret = [] if SCons.Util.is_String(pathlist): pathlist = string.split(pathlist, os.pathsep) if not SCons.Util.is_List(pathlist): pathlist = [pathlist] - result = [] for path in filter(None, pathlist): if isinstance(path, SCons.Node.Node): - result.append(path) - continue - path, dir = self._transformPath(path, cwd) - dir = dir.Dir(path) - result.extend(dir.get_all_rdirs()) - return result + ret.append(path) + else: + name, d = self.__transformPath(path, cwd) + n = self.__doLookup(clazz, name, d) + if not must_exist or n.exists(): + ret.append(n) + if isinstance(n, Dir): + # If this node is a directory, then any repositories + # attached to this node can be repository paths. + ret.extend(filter(lambda x, me=must_exist, clazz=clazz: isinstance(x, clazz) and (not me or x.exists()), + n.getRepositories())) + + d = n.get_dir() + name = n.name + # Search repositories of all directories that this file + # is under. + while d: + for rep in d.getRepositories(): + try: + rnode = self.__doLookup(clazz, name, rep) + # Only find the node if it exists (or + # must_exist is zero) and it is not a + # derived file. If for some reason, we + # are explicitly building a file IN a + # Repository, we don't want it to show up in + # the build tree. This is usually the case + # with BuildDir(). We only want to find + # pre-existing files. + if (not must_exist or rnode.exists()) and \ + (not rnode.is_derived() or isinstance(rnode, Dir)): + ret.append(rnode) + except TypeError: + pass # Wrong type of node. + # Prepend directory name + name = d.name + os.sep + name + # Go up one directory + d = d.get_dir() + return ret def CacheDir(self, path): self.CachePath = path - def build_dir_target_climb(self, orig, dir, tail): + def build_dir_target_climb(self, dir, tail): """Create targets in corresponding build directories Climb the directory tree, and look up path names relative to any linked build directories we find. - __cacheable__ """ targets = [] message = None - fmt = "building associated BuildDir targets: %s" - start_dir = dir while dir: for bd in dir.build_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)) + message = "building associated BuildDir targets: %s" % string.join(map(str, targets)) return targets, message +class DummyExecutor: + """Dummy executor class returned by Dir nodes to bamboozle SCons + into thinking we are an actual derived node, where our sources are + our directory entries.""" + def cleanup(self): + pass + def get_raw_contents(self): + return '' + def get_contents(self): + return '' + def get_timestamp(self): + return 0 + def get_build_env(self): + return None + class Dir(Base): """A class for directories in a file system. """ @@ -1139,8 +1141,7 @@ class Dir(Base): 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. - __cache_reset__""" + signatures for calculating whether they're current.""" self.repositories = [] self.srcdir = None @@ -1149,23 +1150,11 @@ class Dir(Base): self.entries['.'] = self self.entries['..'] = self.dir self.cwd = self + self.builder = get_MkdirBuilder() self.searched = 0 self._sconsign = None self.build_dirs = [] - # 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.fs.isfile, - "File %s found where directory expected.") - - def disambiguate(self): - return self - 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 @@ -1176,11 +1165,30 @@ class Dir(Base): if node != self and isinstance(node, Dir): node.__clearRepositoryCache(duplicate) else: - node.clear() try: del node._srcreps except AttributeError: pass + try: + del node._rfile + except AttributeError: + pass + try: + del node._rexists + except AttributeError: + pass + try: + del node._exists + except AttributeError: + pass + try: + del node._srcnode + except AttributeError: + pass + try: + del node._str_val + except AttributeError: + pass if duplicate != None: node.duplicate=duplicate @@ -1209,56 +1217,32 @@ class Dir(Base): srcdir.build_dirs.append(self) def getRepositories(self): - """Returns a list of repositories for this directory. - __cacheable__""" + """Returns a list of repositories for this directory.""" if self.srcdir and not self.duplicate: - return self.srcdir.get_all_rdirs() + self.repositories + try: + return self._srcreps + except AttributeError: + self._srcreps = self.fs.Rsearchall(self.srcdir.path, + clazz=Dir, + must_exist=0, + cwd=self.fs.Top) \ + + self.repositories + return self._srcreps return self.repositories - def get_all_rdirs(self): - """__cacheable__""" - result = [self] - fname = '.' - dir = self - while dir: - for rep in dir.getRepositories(): - result.append(rep.Dir(fname)) - fname = dir.name + os.sep + fname - dir = dir.up() - return result - def addRepository(self, dir): - if dir != self and not dir in self.repositories: + if not dir in self.repositories and dir != self: self.repositories.append(dir) - dir.tpath = '.' self.__clearRepositoryCache() def up(self): return self.entries['..'] - def rel_path(self, other): - """Return a path to "other" relative to this directory. - __cacheable__""" - if isinstance(other, Dir): - name = [] + def root(self): + if not self.entries['..']: + return self else: - try: - name = [other.name] - other = other.dir - except AttributeError: - return str(other) - if self is other: - return name and name[0] or '.' - i = 0 - for x, y in map(None, self.path_elements, other.path_elements): - if not x is y: - break - i = i + 1 - path_elems = ['..']*(len(self.path_elements)-i) \ - + map(lambda n: n.name, other.path_elements[i:]) \ - + name - - return string.join(path_elems, os.sep) + return self.entries['..'].root() def scan(self): if not self.implicit is None: @@ -1266,33 +1250,19 @@ class Dir(Base): self.implicit = [] self.implicit_dict = {} self._children_reset() - - dont_scan = lambda k: k not in ['.', '..', '.sconsign'] - deps = filter(dont_scan, self.entries.keys()) - # keys() is going to give back the entries in an internal, - # unsorted order. Sort 'em so the order is deterministic. - deps.sort() - entries = map(lambda n, e=self.entries: e[n], deps) - - self._add_child(self.implicit, self.implicit_dict, entries) - - 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. - __cacheable__""" - if not scanner: - return [] - # Clear cached info for this Node. 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) + try: + for filename in self.fs.listdir(self.abspath): + if filename != '.sconsign': + self.Entry(filename) + except OSError: + # Directory does not exist. No big deal + pass + keys = filter(lambda k: k != '.' and k != '..', self.entries.keys()) + kids = map(lambda x, s=self: s.entries[x], keys) + def c(one, two): + return cmp(one.abspath, two.abspath) + kids.sort(c) + self._add_child(self.implicit, self.implicit_dict, kids) def build(self, **kw): """A null "builder" for directories.""" @@ -1300,36 +1270,6 @@ class Dir(Base): if not self.builder is 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: - 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 not self.builder is MkdirBuilder and self.has_builder() @@ -1337,7 +1277,7 @@ class Dir(Base): def alter_targets(self): """Return any corresponding targets in a build directory. """ - return self.fs.build_dir_target_climb(self, self, []) + return self.fs.build_dir_target_climb(self, []) def scanner_key(self): """A directory does not get scanned.""" @@ -1349,13 +1289,10 @@ class Dir(Base): for kid in self.children(): contents.write(kid.get_contents()) return contents.getvalue() - + def prepare(self): pass - def do_duplicate(self, src): - pass - def current(self, calc=None): """If all of our children were up-to-date, then this directory was up-to-date, too.""" @@ -1373,16 +1310,15 @@ class Dir(Base): return 0 def rdir(self): - "__cacheable__" - 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 + try: + return self._rdir + except AttributeError: + self._rdir = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top) + if n: + self._rdir = n + return self._rdir def sconsign(self): """Return the .sconsign file info for this directory, @@ -1413,102 +1349,10 @@ class Dir(Base): def entry_path(self, name): return self.path + os.sep + name - def entry_tpath(self, name): - return self.tpath + os.sep + name - def must_be_a_Dir(self): """Called to make sure a Node is a Dir. Since we're already one, this is a no-op for us.""" - return self - - def entry_exists_on_disk(self, name): - """__cacheable__""" - 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] = 1 - self.on_disk_entries = d - return d.has_key(_my_normcase(name)) - - def srcdir_list(self): - """__cacheable__""" - result = [] - - dirname = '.' - dir = self - while dir: - if dir.srcdir: - d = dir.srcdir.Dir(dirname) - if d.is_under(dir): - # Shouldn't source from something in the build path: - # build_dir is probably under src_dir, in which case - # we are reflecting. - break - result.append(d) - dirname = dir.name + os.sep + dirname - dir = dir.up() - - return result - - def srcdir_duplicate(self, name): - for dir in self.srcdir_list(): - if dir.entry_exists_on_disk(name): - srcnode = dir.File(name) - if self.duplicate: - node = self.File(name) - node.do_duplicate(srcnode) - return node - else: - return srcnode - return None - - def srcdir_find_file(self, filename): - """__cacheable__""" - def func(node): - if (isinstance(node, File) or isinstance(node, Entry)) and \ - (node.is_derived() or node.is_pseudo_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: - return node, self - - 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: - return File(filename, self, self.fs), srcdir - - return None, None - - def dir_on_disk(self, name): - if self.entry_exists_on_disk(name): - try: return self.Dir(name) - except TypeError: pass - return None - - 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 - return self.srcdir_duplicate(name) + pass class RootDir(Dir): """A class for the root directory of a file system. @@ -1518,108 +1362,30 @@ class RootDir(Dir): add a separator when creating the path names of entries within this directory. """ - def __init__(self, name, fs): + def __init__(self, name, directory, 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.path = '' - self.tpath = '' - self.path_elements = [] - self.duplicate = 0 - 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. - self.abspath = name + os.sep - self.path = name + os.sep - self.tpath = name + os.sep + Base.__init__(self, name, directory, fs) + self.path = self.path + os.sep + self.abspath = self.abspath + os.sep self._morph() - def __str__(self): - return self.abspath - def entry_abspath(self, name): return self.abspath + 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 NodeInfo(SCons.Node.NodeInfo): +class BuildInfo: + bsig = None def __cmp__(self, other): - try: return cmp(self.bsig, other.bsig) - except AttributeError: return 1 - def update(self, node): - self.timestamp = node.get_timestamp() - self.size = node.getsize() - -class BuildInfo(SCons.Node.BuildInfo): - def __init__(self, node): - SCons.Node.BuildInfo.__init__(self, node) - self.node = node - def convert_to_sconsign(self): - """Convert this BuildInfo object for writing to a .sconsign file - - We hung onto the node that we refer to so that we can translate - the lists of bsources, bdepends and bimplicit Nodes into strings - relative to the node, but we don't want to write out that Node - itself to the .sconsign file, so we delete the attribute in - preparation. - """ - rel_path = self.node.rel_path - delattr(self, 'node') - for attr in ['bsources', 'bdepends', 'bimplicit']: - try: - val = getattr(self, attr) - except AttributeError: - pass - else: - setattr(self, attr, map(rel_path, val)) - def convert_from_sconsign(self, dir, name): - """Convert a newly-read BuildInfo object for in-SCons use - - An on-disk BuildInfo comes without a reference to the node - for which it's intended, so we have to convert the arguments - and add back a self.node attribute. The bsources, bdepends and - bimplicit lists all come from disk as paths relative to that node, - so convert them to actual Nodes for use by the rest of SCons. - """ - self.node = dir.Entry(name) - Entry_func = self.node.dir.Entry - for attr in ['bsources', 'bdepends', 'bimplicit']: - try: - val = getattr(self, attr) - except AttributeError: - pass - else: - setattr(self, attr, map(Entry_func, val)) + try: + return cmp(self.bsig, other.bsig) + except AttributeError: + return 1 class File(Base): """A class for files in a file system. """ - def diskcheck_match(self): - diskcheck_match(self, self.fs.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) @@ -1635,11 +1401,6 @@ class File(Base): the SConscript directory of this file.""" return self.fs.Dir(name, self.cwd) - def Dirs(self, pathlist): - """Create a list of directories relative to the SConscript - directory of this file.""" - return map(lambda p, s=self: s.Dir(p), pathlist) - def File(self, name): """Create a file node named 'name' relative to the SConscript directory of this file.""" @@ -1647,16 +1408,25 @@ class File(Base): def RDirs(self, pathlist): """Search for a list of directories in the Repository list.""" - return self.fs.Rfindalldirs(pathlist, self.cwd) + return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0, + cwd=self.cwd) + + 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. __cache_reset__""" + """Turn a file system node into a File object.""" self.scanner_paths = {} + self.found_includes = {} if not hasattr(self, '_local'): self._local = 0 - def disambiguate(self): - return self + def root(self): + return self.dir.root() def scanner_key(self): return self.get_suffix() @@ -1668,7 +1438,7 @@ class File(Base): def get_timestamp(self): if self.rexists(): - return self.rfile().getmtime() + return self.fs.getmtime(self.rfile().abspath) else: return 0 @@ -1678,49 +1448,101 @@ class File(Base): # in one build (SConstruct file) is a source in a different build. # See test/chained-build.py for the use case. entry = self.get_stored_info() - entry.merge(obj) + for key, val in obj.__dict__.items(): + entry.__dict__[key] = val self.dir.sconsign().set_entry(self.name, entry) def get_stored_info(self): - "__cacheable__" try: stored = self.dir.sconsign().get_entry(self.name) - except (KeyError, OSError): - return self.new_binfo() - else: - if not hasattr(stored, 'ninfo'): - # Transition: The .sconsign file entry has no NodeInfo - # object, which means it's a slightly older BuildInfo. - # Copy over the relevant attributes. - ninfo = stored.ninfo = self.new_ninfo() - for attr in ninfo.__dict__.keys(): - try: - setattr(ninfo, attr, getattr(stored, attr)) - except AttributeError: - pass - return stored + if isinstance(stored, BuildInfo): + return stored + # The stored build information isn't a BuildInfo object. + # This probably means it's an old SConsignEntry from SCons + # 0.95 or before. The relevant attribute names are the same, + # though, so just copy the attributes over to an object of + # the correct type. + binfo = BuildInfo() + for key, val in stored.__dict__.items(): + setattr(binfo, key, val) + return binfo + except: + return BuildInfo() def get_stored_implicit(self): binfo = self.get_stored_info() - try: return binfo.bimplicit - except AttributeError: return None - - def rel_path(self, other): - return self.dir.rel_path(other) + try: + return binfo.bimplicit + except AttributeError: + return None - def get_found_includes(self, env, scanner, path): + def get_found_includes(self, env, scanner, target): """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. - __cacheable__""" + Cache results so we only scan the file once regardless of + how many times this information is requested.""" if not scanner: return [] - return scanner(self, env, path) + + try: + path = target.scanner_paths[scanner] + except AttributeError: + # The target had no scanner_paths attribute, which means + # it's an Alias or some other node that's not actually a + # file. In that case, back off and use the path for this + # node itself. + try: + path = self.scanner_paths[scanner] + except KeyError: + path = scanner.path(env, self.cwd) + self.scanner_paths[scanner] = path + except KeyError: + path = scanner.path(env, target.cwd) + target.scanner_paths[scanner] = path + + try: + includes = self.found_includes[path] + except KeyError: + includes = scanner(self, env, path) + self.found_includes[path] = includes + + return includes def _createDir(self): # ensure that the directories for this node are # created. - self.dir._create() + + listDirs = [] + parent=self.dir + while parent: + if parent.exists(): + break + listDirs.append(parent) + p = parent.up() + if isinstance(p, ParentOfRoot): + 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) + # 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. + try: + delattr(dirnode, '_exists') + except AttributeError: + pass + try: + delattr(dirnode, '_rexists') + except AttributeError: + pass + except OSError: + pass def retrieve_from_cache(self): """Try to retrieve the node's content from a cache @@ -1763,18 +1585,25 @@ class File(Base): return None def built(self): - """Called just after this node is successfully built. - __cache_reset__""" + """Called just after this node is sucessfully built.""" # Push this file out to cache before the superclass Node.built() # method has a chance to clear the build signature, which it # will do if this file has a source scanner. - if self.fs.CachePath and self.exists(): + if self.fs.CachePath and self.fs.exists(self.path): CachePush(self, [], None) - self.fs.clear_cache() SCons.Node.Node.built(self) + self.found_includes = {} + try: + delattr(self, '_exists') + except AttributeError: + pass + try: + delattr(self, '_rexists') + except AttributeError: + pass def visited(self): - if self.fs.CachePath and self.fs.cache_force and self.exists(): + if self.fs.CachePath and self.fs.cache_force and self.fs.exists(self.path): CachePush(self, None, None) def has_src_builder(self): @@ -1796,12 +1625,19 @@ class File(Base): else: scb = self.dir.src_builder() if scb is _null: - if diskcheck_sccs(self.dir, self.name): + scb = None + dir = self.dir.path + sccspath = os.path.join('SCCS', 's.' + self.name) + if dir != '.': + sccspath = os.path.join(dir, sccspath) + if self.fs.exists(sccspath): scb = get_DefaultSCCSBuilder() - elif diskcheck_rcs(self.dir, self.name): - scb = get_DefaultRCSBuilder() else: - scb = None + rcspath = os.path.join('RCS', self.name + ',v') + if dir != '.': + rcspath = os.path.join(dir, rcspath) + if os.path.exists(rcspath): + scb = get_DefaultRCSBuilder() if scb is not None: self.builder_set(scb) self.sbuilder = scb @@ -1812,16 +1648,11 @@ class File(Base): """ if self.is_derived(): return [], None - return self.fs.build_dir_target_climb(self, self.dir, [self.name]) + return self.fs.build_dir_target_climb(self.dir, [self.name]) def is_pseudo_derived(self): - "__cacheable__" return self.has_src_builder() - - def _rmv_existing(self): - '__cache_reset__' - Unlink(self, [], None) - + def prepare(self): """Prepare for this file to be created.""" SCons.Node.Node.prepare(self) @@ -1829,7 +1660,11 @@ class File(Base): if self.get_state() != SCons.Node.up_to_date: if self.exists(): if self.is_derived() and not self.precious: - self._rmv_existing() + Unlink(self, [], None) + try: + delattr(self, '_exists') + except AttributeError: + pass else: try: self._createDir() @@ -1839,66 +1674,55 @@ class File(Base): def remove(self): """Remove this file.""" - if self.exists() or self.islink(): + if self.fs.exists_or_islink(self.path): self.fs.unlink(self.path) return 1 return None - def do_duplicate(self, src): - self._createDir() - try: - Unlink(self, None, None) - except SCons.Errors.BuildError: - pass - try: - Link(self, src, None) - except SCons.Errors.BuildError, e: - 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() - def exists(self): - "__cacheable__" # 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 self: - return Base.exists(self) - # At this point, src is meant to be copied in a build 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 build 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. - return None + src=self.srcnode().rfile() + if src.exists() and src.abspath != self.abspath: + self._createDir() + try: + Unlink(self, None, None) + except SCons.Errors.BuildError: + pass + try: + Link(self, src, None) + except SCons.Errors.BuildError, e: + 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. + try: + delattr(self, '_exists') + except AttributeError: + pass + try: + delattr(self, '_rexists') + except AttributeError: + pass return Base.exists(self) - # - # SIGNATURE SUBSYSTEM - # - def new_binfo(self): - return BuildInfo(self) + return BuildInfo() - def new_ninfo(self): - ninfo = NodeInfo() - ninfo.update(self) - return ninfo + def del_cinfo(self): + try: + del self.binfo.csig + except AttributeError: + pass + try: + del self.binfo.timestamp + except AttributeError: + pass - def get_csig(self, calc=None): + def calc_csig(self, calc=None): """ Generate a node's content signature, the digested signature of its content. @@ -1907,48 +1731,47 @@ class File(Base): cache - alternate node to use for the signature cache returns - the content signature """ + if calc is None: + calc = self.calculator() + try: - return self.binfo.ninfo.csig + return self.binfo.csig except AttributeError: pass + + if calc.max_drift >= 0: + old = self.get_stored_info() + else: + old = BuildInfo() - if calc is None: - calc = self.calculator() + try: + mtime = self.get_timestamp() + except: + mtime = 0 + raise SCons.Errors.UserError, "no such %s" % self - max_drift = self.fs.max_drift - mtime = self.get_timestamp() - use_stored = max_drift >= 0 and (time.time() - mtime) > max_drift + try: + if (old.timestamp and old.csig and old.timestamp == mtime): + # use the signature stored in the .sconsign file + csig = old.csig + else: + csig = calc.module.signature(self) + except AttributeError: + csig = calc.module.signature(self) - csig = None - if use_stored: - old = self.get_stored_info().ninfo + if calc.max_drift >= 0 and (time.time() - mtime) > calc.max_drift: try: - if old.timestamp and old.csig and old.timestamp == mtime: - csig = old.csig + binfo = self.binfo except AttributeError: - pass - if csig is None: - csig = calc.module.signature(self) - - binfo = self.get_binfo() - ninfo = binfo.ninfo - ninfo.csig = csig - ninfo.update(self) - - if use_stored: + binfo = self.binfo = self.new_binfo() + binfo.csig = csig + binfo.timestamp = mtime self.store_info(binfo) return csig - # - # - # - - def current(self, calc=None): + def current(self, calc=None, scan=1): self.binfo = self.gen_binfo(calc) - return self._cur2() - def _cur2(self): - "__cacheable__" if self.always_build: return None if not self.exists(): @@ -1957,32 +1780,30 @@ class File(Base): if r != self: # ...but there is one in a Repository... old = r.get_stored_info() - new = self.get_binfo() - if new == old: + if old == self.binfo: # ...and it's even up-to-date... if self._local: # ...and they'd like a local copy. LocalCopy(self, r, None) - self.store_info(new) + self.store_info(self.binfo) return 1 + self._rfile = self return None else: old = self.get_stored_info() - new = self.get_binfo() - return (new == old) + return (old == self.binfo) def rfile(self): - "__cacheable__" - 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()): - return node - return self + try: + return self._rfile + except AttributeError: + self._rfile = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=File, + cwd=self.fs.Top) + if n: + self._rfile = n + return self._rfile def rstr(self): return str(self.rfile()) @@ -1990,15 +1811,12 @@ class File(Base): def cachepath(self): if not self.fs.CachePath: return None, None - ninfo = self.get_binfo().ninfo - if not hasattr(ninfo, 'bsig'): - raise SCons.Errors.InternalError, "cachepath(%s) found no bsig" % self.path - elif ninfo.bsig is None: + if self.binfo.bsig is None: raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path # 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. - cache_sig = SCons.Sig.MD5.collect([ninfo.bsig, self.path]) + cache_sig = SCons.Sig.MD5.collect([self.binfo.bsig, self.path]) subdir = string.upper(cache_sig[0]) dir = os.path.join(self.fs.CachePath, subdir) return dir, os.path.join(dir, cache_sig) @@ -2011,16 +1829,14 @@ class File(Base): File, this is a TypeError...""" raise TypeError, "Tried to lookup File '%s' as a Dir." % self.path -default_fs = None +default_fs = FS() -def find_file(filename, paths, verbose=None): +def find_file(filename, paths, node_factory=default_fs.File, 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. + paths - a list of directory path *nodes* to search in returns - the node created from the found file. @@ -2029,43 +1845,30 @@ def find_file(filename, paths, verbose=None): Only the first file found is returned, and none is returned if no file is found. - __cacheable__ """ - if verbose: - if not SCons.Util.is_String(verbose): - verbose = "find_file" - if not callable(verbose): - verbose = ' %s: ' % verbose - verbose = lambda s, v=verbose: sys.stdout.write(v + s) - else: - verbose = lambda x: x - - if callable(paths): - paths = paths() - - # Give Entries a chance to morph into Dirs. - paths = map(lambda p: p.must_be_a_Dir(), paths) - - filedir, filename = os.path.split(filename) - if filedir: - def filedir_lookup(p, fd=filedir): - try: - return p.Dir(fd) - except TypeError: - # We tried to look up a Dir, but it seems there's already - # a File (or something else) there. No big. - return None - paths = filter(None, map(filedir_lookup, paths)) - + if verbose and not SCons.Util.is_String(verbose): + verbose = "find_file" + retval = None for dir in paths: - verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) - node, d = dir.srcdir_find_file(filename) - if node: - verbose("... FOUND '%s' in '%s'\n" % (filename, d)) - return node - return None + if verbose: + sys.stdout.write(" %s: looking for '%s' in '%s' ...\n" % (verbose, filename, dir)) + try: + node = node_factory(filename, dir) + # Return true if the node exists or is a derived node. + if node.is_derived() or \ + node.is_pseudo_derived() or \ + (isinstance(node, SCons.Node.FS.Base) and node.exists()): + retval = node + if verbose: + sys.stdout.write(" %s: ... FOUND '%s' in '%s'\n" % (verbose, filename, dir)) + break + except TypeError: + # If we find a directory instead of a file, we don't care + pass + + return retval -def find_files(filenames, paths): +def find_files(filenames, paths, node_factory = default_fs.File): """ find_files([str], [Dir()]) -> [nodes] @@ -2080,5 +1883,7 @@ def find_files(filenames, paths): Only the first file found is returned for each filename, and any files that aren't found are ignored. """ - nodes = map(lambda x, paths=paths: find_file(x, paths), filenames) - return filter(None, nodes) + nodes = map(lambda x, paths=paths, node_factory=node_factory: + find_file(x, paths, node_factory), + filenames) + return filter(lambda x: x != None, nodes) diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 8c2e6ea6..ce677815 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -24,12 +24,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os -import re -import string import sys import types import unittest -import UserList import SCons.Errors import SCons.Node @@ -42,27 +39,7 @@ 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): +class MyAction: def __init__(self): self.order = 0 @@ -76,36 +53,29 @@ class MyAction(MyActionBase): self.order = built_order return 0 -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 get_actions(self): + return [self] + +class MyNonGlobalAction: + def __init__(self): + self.order = 0 + self.built_it = None + self.built_target = None + self.built_source = None + def __call__(self, target, source, env, errfunc): - for A in self.list: - A(target, source, env, errfunc) + # Okay, so not ENTIRELY non-global... + global built_order + self.built_it = 1 + self.built_target = target + self.built_source = source + self.built_args = env + built_order = built_order + 1 + self.order = built_order + return 0 + + def get_actions(self): + return [self] class Environment: def __init__(self, **kw): @@ -121,23 +91,13 @@ class Environment: return apply(Environment, (), d) def _update(self, dict): self._dict.update(dict) - def get_calculator(self): - return SCons.Sig.default_calc - 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 + def __init__(self, is_explicit=1): + self.env = Environment() 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): @@ -179,12 +139,8 @@ class Scanner: 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 @@ -200,116 +156,6 @@ class MyNode(SCons.Node.Node): 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 NodeInfoTestCase(unittest.TestCase): - - def test___cmp__(self): - """Test comparing NodeInfo objects""" - ni1 = SCons.Node.NodeInfo() - ni2 = SCons.Node.NodeInfo() - - assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__) - - ni1.foo = 777 - assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__) - - ni2.foo = 888 - assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__) - - ni1.foo = 888 - assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__) - - def test_merge(self): - """Test merging NodeInfo attributes""" - ni1 = SCons.Node.NodeInfo() - ni2 = SCons.Node.NodeInfo() - - ni1.a1 = 1 - ni1.a2 = 2 - - ni2.a2 = 222 - ni2.a3 = 333 - - ni1.merge(ni2) - assert ni1.__dict__ == {'a1':1, 'a2':222, 'a3':333}, ni1.__dict__ - - def test_update(self): - """Test the update() method""" - ni = SCons.Node.NodeInfo() - ni.update(SCons.Node.Node()) - - - -class BuildInfoTestCase(unittest.TestCase): - - def test___init__(self): - """Test BuildInfo initialization""" - bi = SCons.Node.BuildInfo(SCons.Node.Node()) - assert hasattr(bi, 'ninfo') - - class MyNode(SCons.Node.Node): - def new_ninfo(self): - return 'ninfo initialization' - bi = SCons.Node.BuildInfo(MyNode()) - assert bi.ninfo == 'ninfo initialization', bi.ninfo - - def test___cmp__(self): - """Test comparing BuildInfo objects""" - bi1 = SCons.Node.BuildInfo(SCons.Node.Node()) - bi2 = SCons.Node.BuildInfo(SCons.Node.Node()) - - assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__) - - bi1.ninfo.foo = 777 - assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__) - - bi2.ninfo.foo = 888 - assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__) - - bi1.ninfo.foo = 888 - assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__) - - bi1.foo = 999 - assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__) - - def test_merge(self): - """Test merging BuildInfo attributes""" - bi1 = SCons.Node.BuildInfo(SCons.Node.Node()) - bi2 = SCons.Node.BuildInfo(SCons.Node.Node()) - - bi1.a1 = 1 - bi1.a2 = 2 - - bi2.a2 = 222 - bi2.a3 = 333 - - bi1.ninfo.a4 = 4 - bi1.ninfo.a5 = 5 - bi2.ninfo.a5 = 555 - bi2.ninfo.a6 = 666 - - bi1.merge(bi2) - assert bi1.a1 == 1, bi1.a1 - assert bi1.a2 == 222, bi1.a2 - assert bi1.a3 == 333, bi1.a3 - assert bi1.ninfo.a4 == 4, bi1.ninfo.a4 - assert bi1.ninfo.a5 == 555, bi1.ninfo.a5 - assert bi1.ninfo.a6 == 666, bi1.ninfo.a6 - - class NodeTestCase(unittest.TestCase): @@ -390,62 +236,36 @@ class NodeTestCase(unittest.TestCase): 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" + built_it = None + built_order = 0 + node = MyNode("xxx") + node.builder_set(Builder()) + node.env_set(Environment()) + node.sources = ["yyy", "zzz"] + pre1 = MyNonGlobalAction() + pre2 = MyNonGlobalAction() + post1 = MyNonGlobalAction() + post2 = MyNonGlobalAction() + node.add_pre_action(pre1) + node.add_pre_action(pre2) + node.add_post_action(post1) + node.add_post_action(post2) + node.build() + assert built_it + assert pre1.built_it + assert pre2.built_it + assert post1.built_it + assert post2.built_it + assert pre1.order == 1, pre1.order + assert pre2.order == 2, pre1.order + # The action of the builder itself is order 3... + assert post1.order == 4, pre1.order + assert post2.order == 5, pre1.order + + for act in [ pre1, pre2, post1, post2 ]: + assert type(act.built_target[0]) == type(MyNode("bar")), type(act.built_target[0]) + assert str(act.built_target[0]) == "xxx", str(act.built_target[0]) + assert act.built_source == ["yyy", "zzz"], act.built_source def test_built(self): """Test the built() method""" @@ -471,6 +291,14 @@ class NodeTestCase(unittest.TestCase): n = SCons.Node.Node() n.visited() + def test_depends_on(self): + """Test the depends_on() method + """ + parent = SCons.Node.Node() + child = SCons.Node.Node() + parent.add_dependency([child]) + assert parent.depends_on([child]) + def test_builder_set(self): """Test setting a Node's Builder """ @@ -492,24 +320,11 @@ class NodeTestCase(unittest.TestCase): """ n1 = SCons.Node.Node() assert not n1.has_explicit_builder() - n1.set_explicit(1) + n1.builder_set(Builder(is_explicit=1)) assert n1.has_explicit_builder() - n1.set_explicit(None) + n1.builder_set(Builder(is_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 """ @@ -546,23 +361,6 @@ class NodeTestCase(unittest.TestCase): node = SCons.Node.Node() assert node.current() 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() - - calc = Calculator(111) - - n1.add_source(n2) - assert n1.children_are_up_to_date(calc), "expected up to date" - n2.set_state(SCons.Node.executed) - assert not n1.children_are_up_to_date(calc), "expected not up to date" - n2.set_state(SCons.Node.up_to_date) - assert n1.children_are_up_to_date(calc), "expected up to date" - n1.always_build = 1 - assert not n1.children_are_up_to_date(calc), "expected not up to date" - def test_env_set(self): """Test setting a Node's Environment """ @@ -579,80 +377,85 @@ class NodeTestCase(unittest.TestCase): a = node.builder.get_actions() assert isinstance(a[0], MyAction), a[0] - def test_get_bsig(self): + def test_calc_bsig(self): """Test generic build signature calculation """ + class Calculator: + 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, self.val) + self.module = M(val) node = SCons.Node.Node() - result = node.get_bsig(Calculator(222)) + result = node.calc_bsig(Calculator(222)) assert result == 222, result - result = node.get_bsig(Calculator(333)) + result = node.calc_bsig(Calculator(333)) assert result == 222, result - def test_get_csig(self): + def test_calc_csig(self): """Test generic content signature calculation """ + 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 + self.module = M(val) node = SCons.Node.Node() - result = node.get_csig(Calculator(444)) + result = node.calc_csig(Calculator(444)) assert result == 444, result - result = node.get_csig(Calculator(555)) + result = node.calc_csig(Calculator(555)) assert result == 444, 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.BuildInfo), binfo - - node.binfo = 777 - binfo = node.get_binfo() - assert binfo == 777, binfo - def test_gen_binfo(self): """Test generating a build information structure """ + class Calculator: + 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, self.val) + self.module = M(val) + node = SCons.Node.Node() - d = SCons.Node.Node() - i = SCons.Node.Node() - node.depends = [d] - node.implicit = [i] - node.gen_binfo(Calculator(666)) - binfo = node.binfo + binfo = node.gen_binfo(Calculator(666)) assert isinstance(binfo, SCons.Node.BuildInfo), binfo assert hasattr(binfo, 'bsources') assert hasattr(binfo, 'bsourcesigs') - assert binfo.bdepends == [d] + assert hasattr(binfo, 'bdepends') assert hasattr(binfo, 'bdependsigs') - assert binfo.bimplicit == [i] + assert hasattr(binfo, 'bimplicit') assert hasattr(binfo, 'bimplicitsigs') - assert binfo.ninfo.bsig == 1998, binfo.ninfo.bsig + assert binfo.bsig == 666, binfo.bsig 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 = SCons.Node.Node() node.exists = lambda: None - # Can't do this with new-style classes (python bug #1066490) - #node.__str__ = lambda: 'xyzzy' + 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' - node = testNode2() + node = SCons.Node.Node() result = node.explain() assert result == None, result - def get_null_info(): - class Null_BInfo: + class Null_BInfo: + def __init__(self): pass - return Null_BInfo() - node.get_stored_info = get_null_info - #see above: node.__str__ = lambda: 'null_binfo' + node.get_stored_info = Null_BInfo + 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 @@ -723,8 +526,6 @@ class NodeTestCase(unittest.TestCase): def test_prepare(self): """Test preparing a node to be built - - By extension, this also tests the missing() method. """ node = SCons.Node.Node() @@ -881,55 +682,41 @@ class NodeTestCase(unittest.TestCase): assert deps == [], deps s = Scanner() - d1 = MyNode("d1") - d2 = MyNode("d2") - node.found_includes = [d1, d2] + d = MyNode("ddd") + node.found_includes = [d] # Simple return of the found includes deps = node.get_implicit_deps(env, s, target) - assert deps == [d1, d2], deps + assert deps == [d], deps - # By default, our fake scanner recurses + # No "recursive" attribute on scanner doesn't recurse e = MyNode("eee") - f = MyNode("fff") - g = MyNode("ggg") - d1.found_includes = [e, f] - d2.found_includes = [e, f] - f.found_includes = [g] + d.found_includes = [e] deps = node.get_implicit_deps(env, s, target) - assert deps == [d1, d2, e, f, g], map(str, deps) + assert deps == [d], map(str, deps) - # Recursive scanning eliminates duplicates - e.found_includes = [f] + # Explicit "recursive" attribute on scanner doesn't recurse + s.recursive = None deps = node.get_implicit_deps(env, s, target) - assert deps == [d1, d2, e, f, g], map(str, deps) + assert deps == [d], 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 + # Explicit "recursive" attribute on scanner which does recurse + s.recursive = 1 deps = node.get_implicit_deps(env, s, target) - assert deps == [d1, d2, e, f], map(str, deps) + assert deps == [d, e], map(str, deps) - # Scanner method can short-circuit recursing entirely - s.recurse_nodes = lambda nodes: [] + # Recursive scanning eliminates duplicates + f = MyNode("fff") + d.found_includes = [e, f] + e.found_includes = [f] deps = node.get_implicit_deps(env, s, target) - assert deps == [d1, d2], map(str, deps) - - def test_get_scanner(self): - """Test fetching the environment scanner for a Node - """ - node = SCons.Node.Node() - scanner = Scanner() - env = Environment(SCANNERS = [scanner]) - s = node.get_scanner(env) - assert s == scanner, s - s = node.get_scanner(env, {'X':1}) - assert s == scanner, s + assert deps == [d, e, f], map(str, deps) def test_get_source_scanner(self): """Test fetching the source scanner for a Node """ + class Builder: + pass target = SCons.Node.Node() source = SCons.Node.Node() s = target.get_source_scanner(source) @@ -939,48 +726,32 @@ class NodeTestCase(unittest.TestCase): 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) + source.backup_source_scanner = ts1 + s = target.get_source_scanner(source) assert s is ts1, s - target.builder_set(Builder2(ts1)) + target.builder = Builder() 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) + target.source_scanner = ts3 + s = target.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]) - + node.env_set(Environment()) s = Scanner() + d = MyNode("ddd") node.found_includes = [d] - node.builder.target_scanner = s + assert node.target_scanner == None, node.target_scanner + node.target_scanner = s assert node.implicit is None node.scan() @@ -1012,14 +783,13 @@ class NodeTestCase(unittest.TestCase): SCons.Node.implicit_deps_unchanged = None try: sn = StoredNode("eee") - sn.builder_set(Builder()) - sn.builder.target_scanner = s + sn._children = ['fake'] + sn.target_scanner = s sn.scan() assert sn.implicit == [], sn.implicit - assert sn.children() == [], sn.children() - + assert not hasattr(sn, '_children'), "unexpected _children attribute" finally: SCons.Sig.default_calc = save_default_calc SCons.Node.implicit_cache = save_implicit_cache @@ -1094,7 +864,7 @@ class NodeTestCase(unittest.TestCase): """Test setting and getting the state of a node """ node = SCons.Node.Node() - assert node.get_state() == SCons.Node.no_state + assert node.get_state() == None node.set_state(SCons.Node.executing) assert node.get_state() == SCons.Node.executing assert SCons.Node.pending < SCons.Node.executing @@ -1229,17 +999,14 @@ class NodeTestCase(unittest.TestCase): n.implicit = 'testimplicit' n.waiting_parents = ['foo', 'bar'] - x = MyExecutor() - n.set_executor(x) - n.clear() + assert n.get_state() is None, n.get_state() assert not hasattr(n, 'binfo'), n.bsig assert n.includes is None, n.includes assert n.found_includes == {}, n.found_includes assert n.implicit is None, n.implicit assert n.waiting_parents == [], n.waiting_parents - assert x.cleaned_up def test_get_subst_proxy(self): """Test the get_subst_proxy method.""" @@ -1259,6 +1026,12 @@ class NodeTestCase(unittest.TestCase): s = n.get_suffix() assert s == '', s + def test_generate_build_dict(self): + """Test the base Node generate_build_dict() method""" + n = SCons.Node.Node() + dict = n.generate_build_dict() + assert dict == {}, dict + def test_postprocess(self): """Test calling the base Node postprocess() method""" n = SCons.Node.Node() @@ -1283,46 +1056,9 @@ class NodeTestCase(unittest.TestCase): n1.call_for_all_waiting_parents(func) assert result == [n1, n2], result -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', '"]*3, ", ") - assert r == '[%s]' % l, r - if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ BuildInfoTestCase, - NodeInfoTestCase, - NodeTestCase, - NodeListTestCase ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(map(tclass, names)) + suite = unittest.makeSuite(NodeTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index e73e5f38..38cff929 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -48,10 +48,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import copy import string -import UserList from SCons.Debug import logInstanceCreation -import SCons.Executor import SCons.SConsign import SCons.Util @@ -62,7 +60,6 @@ import SCons.Util # 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 @@ -70,17 +67,7 @@ executed = 4 failed = 5 stack = 6 # nodes that are in the current Taskmaster execution stack -StateString = { - 0 : "0", - 1 : "pending", - 2 : "executing", - 3 : "up_to_date", - 4 : "executed", - 5 : "failed", - 6 : "stack", -} - -# controls whether implicit dependencies are cached: +# controls whether implicit depedencies are cached: implicit_cache = 0 # controls whether implicit dep changes are ignored: @@ -95,69 +82,20 @@ def do_nothing(node): pass Annotate = do_nothing -# Classes for signature info for Nodes. - -class NodeInfo: - """ - A generic class for signature information for a Node. - - We actually expect that modules containing Node subclasses will also - subclass NodeInfo, to provide their own logic for dealing with their - own Node-specific signature information. - """ - def __init__(self): - """A null initializer so that subclasses have a superclass - initialization method to call for future use. - """ - pass - def __cmp__(self, other): - return cmp(self.__dict__, other.__dict__) - def update(self, node): - pass - def merge(self, other): - for key, val in other.__dict__.items(): - self.__dict__[key] = val - class BuildInfo: - """ - The generic 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. - """ - def __init__(self, node): - self.ninfo = node.new_ninfo() - self.bsourcesigs = [] - self.bdependsigs = [] - self.bimplicitsigs = [] - self.bactsig = None def __cmp__(self, other): - return cmp(self.ninfo, other.ninfo) - def merge(self, other): - for key, val in other.__dict__.items(): - try: - merge = self.__dict__[key].merge - except (AttributeError, KeyError): - self.__dict__[key] = val - else: - merge(val) + return cmp(self.__dict__, 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 - class Attrs: pass def __init__(self): - if __debug__: logInstanceCreation(self, 'Node.Node') + if __debug__: logInstanceCreation(self, '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 @@ -180,13 +118,17 @@ class Node: self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) self.waiting_parents = [] self.wkids = None # Kids yet to walk, when it's an array + self.target_scanner = None # explicit scanner from this node's Builder + self.source_scanner = None + self.backup_source_scanner = None self.env = None - self.state = no_state + self.state = None self.precious = None self.always_build = None self.found_includes = {} self.includes = None + self.overrides = {} # construction variable overrides for building this node 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 @@ -202,14 +144,15 @@ class Node: def get_suffix(self): return '' - def get_build_env(self): - """Fetch the appropriate Environment to build this node. - __cacheable__""" - return self.get_executor().get_build_env() + def generate_build_dict(self): + """Return an appropriate dictionary of values for building + this Node.""" + return {} - 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 get_build_env(self): + """Fetch the appropriate Environment to build this node.""" + executor = self.get_executor() + return executor.get_build_env() def set_executor(self, executor): """Set the action executor for this node.""" @@ -223,35 +166,15 @@ class Node: 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) + import SCons.Executor + executor = SCons.Executor.Executor(self.builder.action, + 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 retrieve_from_cache(self): """Try to retrieve the node's content from a cache @@ -270,15 +193,15 @@ class Node: so only do thread safe stuff here. Do thread unsafe stuff in built(). """ - def exitstatfunc(stat, node=self): - if stat: - msg = "Error %d" % stat - raise SCons.Errors.BuildError(node=node, errstr=msg) + if not self.has_builder(): + return + def errfunc(stat, node=self): + raise SCons.Errors.BuildError(node=node, errstr="Error %d" % stat) executor = self.get_executor() - apply(executor, (self, exitstatfunc), kw) + apply(executor, (self, errfunc), kw) def built(self): - """Called just after this node is successfully built.""" + """Called just after this node is sucessfully built.""" # Clear the implicit dependency caches of any Nodes # waiting for this Node to be built. @@ -287,27 +210,30 @@ class Node: parent.del_binfo() try: - new = self.binfo + new_binfo = self.binfo except AttributeError: # Node arrived here without build info; apparently it # doesn't need it, so don't bother calculating or storing # it. - new = None + new_binfo = None # Reset this Node's cached state since it was just built and # various state has changed. + save_state = self.get_state() self.clear() + self.set_state(save_state) + + # Had build info, so it should be stored in the signature + # cache. However, if the build info included a content + # signature then it should be recalculated before being + # stored. - if new: - # It had build info, so it should be stored in the signature - # cache. However, if the build info included a content - # signature then it must be recalculated before being stored. - if hasattr(new.ninfo, 'csig'): - self.get_csig() + if new_binfo: + if hasattr(new_binfo, 'csig'): + new_binfo = self.gen_binfo() # sets self.binfo else: - new.ninfo.update(self) - self.binfo = new - self.store_info(self.binfo) + self.binfo = new_binfo + self.store_info(new_binfo) def add_to_waiting_parents(self, node): self.waiting_parents.append(node) @@ -320,16 +246,21 @@ class Node: def postprocess(self): """Clean up anything we don't need to hang onto after we've been built.""" - self.executor_cleanup() + try: + executor = self.get_executor(create=None) + except AttributeError: + pass + else: + executor.cleanup() 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). - __reset_cache__ """ - self.executor_cleanup() + self.set_state(None) self.del_binfo() + self.del_cinfo() try: delattr(self, '_calculated_sig') except AttributeError: @@ -345,8 +276,15 @@ class Node: without requiring a build..""" pass + def depends_on(self, nodes): + """Does this node depend on any of 'nodes'?""" + for node in nodes: + if node in self.children(): + return 1 + + return 0 + def builder_set(self, builder): - "__cache_reset__" self.builder = builder def has_builder(self): @@ -369,9 +307,6 @@ class Node: b = self.builder return not b is 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 @@ -379,18 +314,7 @@ class Node: 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 + return self.has_builder() and self.builder.is_explicit multiple_side_effect_has_builder = has_builder @@ -403,7 +327,6 @@ class Node: 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. - __cacheable__ """ return self.has_builder() or self.side_effect @@ -420,7 +343,7 @@ class Node: """ return [], None - def get_found_includes(self, env, scanner, path): + def get_found_includes(self, env, scanner, target): """Return the scanned include lines (implicit dependencies) found in this node. @@ -430,7 +353,7 @@ class Node: """ return [] - def get_implicit_deps(self, env, scanner, path): + def get_implicit_deps(self, env, scanner, target): """Return a list of implicit dependencies for this node. This method exists to handle recursive invocation of the scanner @@ -444,57 +367,59 @@ class Node: # for this Node. scanner = scanner.select(self) + try: + recurse = scanner.recursive + except AttributeError: + recurse = None + 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)) + n = nodes.pop(0) + d = filter(lambda x, seen=seen: not seen.has_key(x), + n.get_found_includes(env, scanner, target)) + if d: + deps.extend(d) + for n in d: + seen[n] = 1 + if recurse: + nodes.extend(d) return deps - def get_scanner(self, env, kw={}): - return env.get_scanner(self.scanner_key()) + # cache used to make implicit_factory fast. + implicit_factory_cache = {} + + def implicit_factory(self, path): + """ + Turn a cache implicit dependency path into a node. + This is called so many times that doing caching + here is a significant performance boost. + """ + try: + return self.implicit_factory_cache[path] + except KeyError: + n = self.builder.source_factory(path) + self.implicit_factory_cache[path] = n + return n 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 + if self.source_scanner: + return self.source_scanner try: scanner = self.builder.source_scanner + if scanner: + return 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_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_dict = {} - self._children_reset() - self._add_child(self.implicit, self.implicit_dict, deps) + return node.backup_source_scanner or None def scan(self): """Scan this node's dependents for implicit dependencies.""" @@ -514,44 +439,34 @@ class Node: # Here's where we implement --implicit-cache. if implicit_cache and not implicit_deps_changed: implicit = self.get_stored_implicit() - if implicit: - factory = build_env.get_factory(self.builder.source_factory) - nodes = [] - for i in implicit: - try: - n = factory(i) - except TypeError: - # The implicit dependency was cached as one type - # of Node last time, but the configuration has - # changed (probably) and it's a different type - # this time. Just ignore the mismatch and go - # with what our current configuration says the - # Node is. - pass - else: - nodes.append(n) - self._add_child(self.implicit, self.implicit_dict, nodes) + if implicit is not None: + implicit = map(self.implicit_factory, implicit) + self._add_child(self.implicit, self.implicit_dict, implicit) calc = build_env.get_calculator() - if implicit_deps_unchanged or self.current(calc): + if implicit_deps_unchanged or self.current(calc, scan=0): return - # one of this node's sources has changed, so - # we need to recalculate the implicit deps, - # and the bsig: - self.implicit = [] - self.implicit_dict = {} - self._children_reset() - self.del_binfo() - - executor = self.get_executor() - - # Have the executor scan the sources. - executor.scan_sources(self.builder.source_scanner) + else: + # one of this node's sources has changed, so + # we need to recalculate the implicit deps, + # and the bsig: + self.implicit = [] + self.implicit_dict = {} + self._children_reset() + self.del_binfo() + + for child in self.children(scan=0): + scanner = self.get_source_scanner(child) + if scanner: + deps = child.get_implicit_deps(build_env, scanner, self) + self._add_child(self.implicit, self.implicit_dict, deps) + + # scan this node itself for implicit dependencies + deps = self.get_implicit_deps(build_env, self.target_scanner, self) + self._add_child(self.implicit, self.implicit_dict, deps) - # If there's a target scanner, have the executor scan the target - # node itself and associated targets that might be built. - scanner = self.builder.target_scanner - if scanner: - executor.scan_targets(scanner) + # XXX See note above re: --implicit-cache. + #if implicit_cache: + # self.store_implicit() def scanner_key(self): return None @@ -561,10 +476,6 @@ class Node: return self.env = env - # - # SIGNATURE SUBSYSTEM - # - def calculator(self): import SCons.Defaults @@ -574,42 +485,46 @@ class Node: def calc_signature(self, calc=None): """ Select and calculate the appropriate build signature for a node. - __cacheable__ self - the node calc - the signature calculation module returns - the signature """ - if self.is_derived(): - import SCons.Defaults - - env = self.env or SCons.Defaults.DefaultEnvironment() - if env.use_build_signature(): - return self.get_bsig(calc) - elif not self.rexists(): - return None - return self.get_csig(calc) - - def new_ninfo(self): - return NodeInfo() - - def new_binfo(self): - return BuildInfo(self) - - def get_binfo(self): try: - return self.binfo + return self._calculated_sig except AttributeError: - self.binfo = self.new_binfo() - return self.binfo + if self.is_derived(): + import SCons.Defaults + + env = self.env or SCons.Defaults.DefaultEnvironment() + if env.use_build_signature(): + sig = self.calc_bsig(calc) + else: + sig = self.calc_csig(calc) + elif not self.rexists(): + sig = None + else: + sig = self.calc_csig(calc) + self._calculated_sig = sig + return sig + + def new_binfo(self): + return BuildInfo() def del_binfo(self): - """Delete the build info from this node.""" + """Delete the bsig from this node.""" try: delattr(self, 'binfo') except AttributeError: pass + def calc_bsig(self, calc=None): + try: + return self.binfo.bsig + except AttributeError: + self.binfo = self.gen_binfo(calc) + return self.binfo.bsig + def gen_binfo(self, calc=None, scan=1): """ Generate a node's build signature, the digested signatures @@ -623,70 +538,68 @@ class Node: node's children's signatures. We expect that they're already built and updated by someone else, if that's what's wanted. - __cacheable__ """ if calc is None: calc = self.calculator() - binfo = self.get_binfo() + binfo = self.new_binfo() if scan: self.scan() - executor = self.get_executor() + sources = self.filter_ignore(self.sources) + depends = self.filter_ignore(self.depends) + if self.implicit is None: + implicit = [] + else: + implicit = self.filter_ignore(self.implicit) + def calc_signature(node, calc=calc): return node.calc_signature(calc) - - sources = executor.process_sources(None, self.ignore) - sourcesigs = executor.process_sources(calc_signature, self.ignore) - - depends = self.depends - implicit = self.implicit or [] - - if self.ignore: - depends = filter(self.do_not_ignore, depends) - implicit = filter(self.do_not_ignore, implicit) - + sourcesigs = map(calc_signature, sources) dependsigs = map(calc_signature, depends) implicitsigs = map(calc_signature, implicit) sigs = sourcesigs + dependsigs + implicitsigs if self.has_builder(): - binfo.bact = str(executor) + executor = self.get_executor() + binfo.bact = executor.strfunction() binfo.bactsig = calc.module.signature(executor) sigs.append(binfo.bactsig) - binfo.bsources = sources - binfo.bdepends = depends - binfo.bimplicit = implicit + binfo.bsources = map(str, sources) + binfo.bdepends = map(str, depends) + binfo.bimplicit = map(str, implicit) binfo.bsourcesigs = sourcesigs binfo.bdependsigs = dependsigs binfo.bimplicitsigs = implicitsigs - binfo.ninfo.bsig = calc.module.collect(filter(None, sigs)) + binfo.bsig = calc.module.collect(filter(None, sigs)) return binfo - def get_bsig(self, calc=None): - binfo = self.get_binfo() + def del_cinfo(self): try: - return binfo.ninfo.bsig + del self.binfo.csig except AttributeError: - self.binfo = self.gen_binfo(calc) - return self.binfo.ninfo.bsig + pass - def get_csig(self, calc=None): - binfo = self.get_binfo() + def calc_csig(self, calc=None): try: - return binfo.ninfo.csig + binfo = self.binfo + except AttributeError: + binfo = self.binfo = self.new_binfo() + try: + return binfo.csig except AttributeError: if calc is None: calc = self.calculator() - csig = binfo.ninfo.csig = calc.module.signature(self) - return csig + binfo.csig = calc.module.signature(self) + self.store_info(binfo) + return binfo.csig def store_info(self, obj): """Make the build signature permanent (that is, store it in the @@ -700,10 +613,6 @@ class Node: """Fetch the stored implicit dependencies""" return None - # - # - # - def set_precious(self, precious = 1): """Set the Node's precious value.""" self.precious = precious @@ -721,24 +630,18 @@ class Node: """Does this node exist locally or in a repositiory?""" # There are no repositories by default: return self.exists() - - def missing(self): - """__cacheable__""" - return not self.is_derived() and \ - not self.is_pseudo_derived() and \ - not self.linked and \ - not self.rexists() def prepare(self): """Prepare for this Node to be created. The default implemenation checks that all children either exist or are derived. """ - l = self.depends - if not self.implicit is None: - l = l + self.implicit - missing_sources = self.get_executor().get_missing_sources() \ - + filter(lambda c: c.missing(), l) + def missing(node): + return not node.is_derived() and \ + not node.is_pseudo_derived() and \ + not node.linked and \ + not node.rexists() + missing_sources = filter(missing, self.children()) if missing_sources: desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self) raise SCons.Errors.StopError, desc @@ -806,15 +709,33 @@ class Node: self.wkids.append(wkid) def _children_reset(self): - "__cache_reset__" - # We need to let the Executor clear out any calculated - # bsig info that it's cached so we can re-calculate it. - self.executor_cleanup() + try: + delattr(self, '_children') + except AttributeError: + pass - def do_not_ignore(self, node): - return node not in self.ignore + def filter_ignore(self, nodelist): + ignore = self.ignore + result = [] + for node in nodelist: + if node not in ignore: + result.append(node) + return result - def _all_children_get(self): + 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() + try: + return self._children + except AttributeError: + c = self.all_children(scan=0) + self._children = self.filter_ignore(c) + return self._children + + def all_children(self, scan=1): + """Return a list of all the node's direct children.""" # 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 @@ -832,31 +753,13 @@ class Node: # using dictionary keys, lose the order, and the only ordered # dictionary patterns I found all ended up using "not in" # internally anyway...) + if scan: + self.scan() if self.implicit is None: return self.sources + self.depends else: return self.sources + self.depends + self.implicit - def _children_get(self): - "__cacheable__" - children = self._all_children_get() - if self.ignore: - children = filter(self.do_not_ignore, children) - return children - - def all_children(self, scan=1): - """Return a list of all the node's direct children.""" - if scan: - self.scan() - return self._all_children_get() - - 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 @@ -876,8 +779,6 @@ class Node: rebind their current() method to this method.""" # Allow the children to calculate their signatures. self.binfo = self.gen_binfo(calc) - if self.always_build: - return None state = 0 for kid in self.children(None): s = kid.get_state() @@ -890,6 +791,16 @@ class Node: the command interpreter literally.""" return 1 + def add_pre_action(self, act): + """Adds an Action performed on this Node only before + building it.""" + self.pre_actions.append(act) + + def add_post_action(self, act): + """Adds and Action performed on this Node only after + building it.""" + self.post_actions.append(act) + def render_include_tree(self): """ Return a text representation, suitable for displaying to the @@ -899,9 +810,8 @@ class Node: env = self.get_build_env() for s in self.sources: scanner = self.get_source_scanner(s) - path = self.get_build_scanner_path(scanner) - def f(node, env=env, scanner=scanner, path=path): - return node.get_found_includes(env, scanner, path) + def f(node, env=env, scanner=scanner, target=self): + return node.get_found_includes(env, scanner, target) return SCons.Util.render_tree(s, f, 1) else: return None @@ -974,63 +884,53 @@ class Node: result[k] = s try: - osig = {} - dictify(osig, old.bsources, old.bsourcesigs) - dictify(osig, old.bdepends, old.bdependsigs) - dictify(osig, old.bimplicit, old.bimplicitsigs) + old_bkids = old.bsources + old.bdepends + old.bimplicit except AttributeError: return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self - new = self.get_binfo() - - nsig = {} - dictify(nsig, new.bsources, new.bsourcesigs) - dictify(nsig, new.bdepends, new.bdependsigs) - dictify(nsig, new.bimplicit, new.bimplicitsigs) - - old_bkids = old.bsources + old.bdepends + old.bimplicit - new_bkids = new.bsources + new.bdepends + new.bimplicit + osig = {} + dictify(osig, old.bsources, old.bsourcesigs) + dictify(osig, old.bdepends, old.bdependsigs) + dictify(osig, old.bimplicit, old.bimplicitsigs) - # 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. - stringify = lambda s, E=self.dir.Entry: str(E(s)) + new_bsources = map(str, self.binfo.bsources) + new_bdepends = map(str, self.binfo.bdepends) + new_bimplicit = map(str, self.binfo.bimplicit) - lines = [] + nsig = {} + dictify(nsig, new_bsources, self.binfo.bsourcesigs) + dictify(nsig, new_bdepends, self.binfo.bdependsigs) + dictify(nsig, new_bimplicit, self.binfo.bimplicitsigs) - 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)) + new_bkids = new_bsources + new_bdepends + new_bimplicit + lines = map(lambda x: "`%s' is no longer a dependency\n" % x, + filter(lambda x, nk=new_bkids: not x in nk, old_bkids)) for k in new_bkids: if not k in old_bkids: - lines.append("`%s' is a new dependency\n" % stringify(k)) + lines.append("`%s' is a new dependency\n" % k) elif osig[k] != nsig[k]: - lines.append("`%s' changed\n" % stringify(k)) + lines.append("`%s' changed\n" % 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))) + "%sold: %s\n" % (' '*15, old_bkids) + + "%snew: %s\n" % (' '*15, new_bkids)) if len(lines) == 0: + newact, newactsig = self.binfo.bact, self.binfo.bactsig 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: + if old.bactsig != newactsig: + if old.bact == newact: lines.append("the contents of the build action changed\n" + - fmt_with_title('action: ', new.bact)) + fmt_with_title('action: ', newact)) else: lines.append("the build action changed:\n" + fmt_with_title('old: ', old.bact) + - fmt_with_title('new: ', new.bact)) + fmt_with_title('new: ', newact)) if len(lines) == 0: return "rebuilding `%s' for unknown reasons\n" % self @@ -1042,28 +942,6 @@ class Node: lines = ["%s:\n" % preamble] + lines return string.join(lines, ' '*11) -l = [1] -ul = UserList.UserList([2]) -try: - l.extend(ul) -except TypeError: - def NodeList(l): - return l -else: - class NodeList(UserList.UserList): - def __str__(self): - return str(map(str, self.data)) -del l -del ul - -if SCons.Memoize.use_old_memoization(): - _Base = Node - class Node(SCons.Memoize.Memoizer, _Base): - def __init__(self, *args, **kw): - apply(_Base.__init__, (self,)+args, kw) - SCons.Memoize.Memoizer.__init__(self) - - def get_children(node, parent): return node.children() def ignore_cycle(node, stack): pass def do_nothing(node, parent): pass -- 2.26.2