summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-04-15 18:43:38 (GMT)
committerSteven Knight <knight@baldmt.com>2002-04-15 18:43:38 (GMT)
commita8176f609ff3ecc090f51830408d3b4dc6338d7e (patch)
treebab059042f2f8cbc85dcf7a619dbebbbe23dc4fb
parent05029e336146444501a66b53e4699c09d6e08977 (diff)
downloadSCons-a8176f609ff3ecc090f51830408d3b4dc6338d7e.zip
SCons-a8176f609ff3ecc090f51830408d3b4dc6338d7e.tar.gz
SCons-a8176f609ff3ecc090f51830408d3b4dc6338d7e.tar.bz2
Big change for shared libraries and a bunch of other flexibility. (Charles Crain)
-rw-r--r--doc/man/scons.1304
-rw-r--r--src/CHANGES.txt44
-rw-r--r--src/engine/SCons/Action.py125
-rw-r--r--src/engine/SCons/ActionTests.py63
-rw-r--r--src/engine/SCons/Builder.py302
-rw-r--r--src/engine/SCons/BuilderTests.py146
-rw-r--r--src/engine/SCons/Defaults.py231
-rw-r--r--src/engine/SCons/Environment.py13
-rw-r--r--src/engine/SCons/EnvironmentTests.py6
-rw-r--r--src/engine/SCons/Node/NodeTests.py10
-rw-r--r--src/engine/SCons/Node/__init__.py39
-rw-r--r--src/engine/SCons/Scanner/Prog.py19
-rw-r--r--src/engine/SCons/Scanner/ProgTests.py5
-rw-r--r--src/engine/SCons/Sig/MD5.py9
-rw-r--r--src/engine/SCons/Sig/MD5Tests.py3
-rw-r--r--src/engine/SCons/Util.py30
-rw-r--r--src/engine/SCons/UtilTests.py46
-rw-r--r--test/Command.py4
-rw-r--r--test/LIBPATH.py6
-rw-r--r--test/LIBPREFIXES.py71
-rw-r--r--test/LIBSUFFIXES.py71
-rw-r--r--test/SHCC.py86
-rw-r--r--test/SHCCFLAGS.py84
-rw-r--r--test/SHCXX.py90
-rw-r--r--test/SHCXXFLAGS.py87
-rw-r--r--test/SHF77.py205
-rw-r--r--test/SHF77FLAGS.py208
-rw-r--r--test/SHLIBPREFIX.py55
-rw-r--r--test/SHLIBSUFFIX.py55
-rw-r--r--test/SHLINK.py88
-rw-r--r--test/SHLINKFLAGS.py88
-rw-r--r--test/SharedLibrary.py165
-rw-r--r--test/multiline.py4
33 files changed, 2387 insertions, 375 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index be1ded0..ec2fbdd 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -1,3 +1,5 @@
+.\" 1. Builder's __call__ method can now take arbitrary keyword arguments.
+.\" These args are saved with the target node of the build, then passed along
.\" Copyright (c) 2001, 2002 Steven Knight
.\"
.\" Permission is hereby granted, free of charge, to any person obtaining
@@ -31,9 +33,9 @@
.RE
.fi
..
-.TH SCONS 1 "January 2002"
+.TH SCONS 1 "April 2002"
.SH NAME
-scons \- software constructor
+scons \- a software construction tool
.SH SYNOPSIS
.B scons
[
@@ -492,7 +494,7 @@ any out-of-date target files, but do not execute the commands.
.TP
.RI --profile= file
-Run SCons under the Python profile
+Run SCons under the Python profiler
and save the results in the specified
.IR file .
The results may be analyzed using the Python
@@ -653,6 +655,17 @@ env.Object(target = 'aaa', source = 'aaa.c')
env.Object(target = 'bbb.o', source = 'bbb.c++')
env.Object(target = 'ccc.obj', source = 'ccc.f')
.EE
+.IP
+The
+.B Object
+builder accepts an optional "shared" keyword that, when non-zero,
+specifies that the object file should be built for
+inclusion in a shared library
+(that is, built with the '-fPIC' option when using gcc):
+
+.ES
+env.Object(target = 'ddd.obj', source = 'ddd.c', shared = 1)
+.EE
.IP Program
Builds an executable given one or more object files or C/C++ source
@@ -665,14 +678,49 @@ env.Program(target = 'foo', source = 'foo.o bar.c baz.f')
.EE
.IP Library
-Builds a library given one or more object files or C/C++ source
-files. If any C/C++ source files are given, then they will be automatically
+Builds a static or shared library given one or more object files
+or C, C++ or Fortran source files.
+If any source files are given,
+then they will be automatically
compiled to object files. The library prefix and suffix (if any) are
automatically added to the target. Example:
.ES
env.Library(target = 'bar', source = 'bar.c foo.o')
.EE
+.IP
+By default,
+.B Library
+builds a static library.
+A shared library (.so on a POSIX system, .dll on WIN32)
+may be specified by setting the
+.B shared
+keyword argument to non-zero:
+
+.ES
+env.Library(target = 'bar', source = 'bar.c foo.o', shared = 1)
+.EE
+.IP
+On WIN32 systems, the
+.B Library
+builder will always build an import (.lib) library
+in addition to the shared (.dll) library,
+adding a .lib library with the same basename
+if there is not already a .lib file explicitly
+listed in the targets.
+
+Any object files listed in the
+.B source
+list for a shared library
+must have been built for a shared library
+(that is, using a non-zero
+.B shared
+keyword argument).
+Conversely, object files built into a static library must
+.I not
+have been built for a shared library.
+.B scons
+will raise an error if there is any mismatch.
.IP CFile
Builds a C source file given a lex (.l) or yacc (.y) input file.
@@ -737,7 +785,8 @@ env.PostScript(target = 'aaa.ps', source = 'aaa.tex') # builds from aaa.tex
env.PostScript(target = 'bbb', source = 'bbb.dvi') # builds bbb.dvi
.EE
.LP
-.B scons automatically scans
+.B scons
+automatically scans
C, C++ and Fortran source files with .F, .fpp, or .FOR file extensions
for C preprocessor dependencies,
so the dependencies do not need to be specified explicitly.
@@ -901,12 +950,12 @@ A list of the available builders.
.IP CC
The C compiler.
+.IP CCCOM
+The command line used to compile a C source file to a (static) object file.
+
.IP CCFLAGS
General options that are passed to the C compiler.
-.IP CCCOM
-The command line used to compile a C source file to an object file.
-
.IP CFILESUFFIX
The suffix for C source files.
This is used by the internal CFile builder
@@ -957,12 +1006,12 @@ when generating C++ files from Lex (.ll) or YACC (.yy) input files.
The default suffix is
.IR .cc .
-.IP CXXFLAGS
-General options that are passed to the C++ compiler.
-
.IP CXXCOM
The command line used to compile a C++ source file to an object file.
+.IP CXXFLAGS
+General options that are passed to the C++ compiler.
+
.IP DVIPDF
The TeX DVI file to PDF file converter.
@@ -1024,12 +1073,12 @@ env = Environment(ENV = {'PATH' : os.environ['PATH']})
.IP F77
The Fortran compiler.
-.IP F77FLAGS
-General options that are passed to the Fortran compiler.
-
.IP F77COM
The command line used to compile a Fortran source file to an object file.
+.IP F77FLAGS
+General options that are passed to the Fortran compiler.
+
.IP F77PPCOM
The command line used to compile a Fortran source file to an object file
after first running the file through the C preprocessor.
@@ -1101,7 +1150,10 @@ env = Environment(LIBPATH=libs)
.EE
.IP LIBPREFIX
-The prefix used for library file names.
+The prefix used for (static) library file names.
+
+.IP LIBPREFIXES
+An array of legal prefixes for library file names.
.IP LIBS
A list of one or more libraries
@@ -1110,7 +1162,10 @@ any executable programs
created by this environment.
.IP LIBSUFFIX
-The suffix used for library file names.
+The suffix used for (static) library file names.
+
+.IP LIBSUFFIXES
+An array of legal suffixes for library file names.
.IP LINK
The linker.
@@ -1160,6 +1215,58 @@ General options passed to the archive indexer.
.IP SCANNERS
A list of the available implicit dependency scanners. [CScan] by default.
+.IP SHCC
+The C compiler used for generating shared-library objects.
+
+.IP SHCCCOM
+The command line used to compile a C source file
+to a shared-library object file.
+
+.IP SHCCFLAGS
+Options that are passed to the C compiler
+to generate shared-library objects.
+
+.IP SHCXX
+The C++ compiler used for generating shared-library objects.
+
+.IP SHCXXCOM
+The command line used to compile a C++ source file
+to a shared-library object file.
+
+.IP SHCXXFLAGS
+Options that are passed to the C++ compiler
+to generate shared-library objects.
+
+.IP SHF77
+The Fortran compiler used for generating shared-library objects.
+
+.IP SHF77COM
+The command line used to compile a Fortran source file
+to a shared-library object file.
+
+.IP SHF77FLAGS
+Options that are passed to the Fortran compiler
+to generated shared-library objects.
+
+.IP SHF77PPCOM
+The command line used to compile a Fortran source file to a
+shared-library object file
+after first running the file through the C preprocessor.
+Any options specified in the $CPPFLAGS construction variable
+are included on this command line.
+
+.IP SHLIBPREFIX
+The prefix used for shared library file names.
+
+.IP SHLIBSUFFIX
+The suffix used for shared library file names.
+
+.IP SHLINK
+The linker for programs that use shared libraries.
+
+.IP SHLINKFLAGS
+General options passed to the linker for programs using shared libraries.
+
.IP TEX
The TeX formatter and typesetter.
@@ -1169,6 +1276,24 @@ The command line used to call the TeX formatter and typesetter.
.IP TEXFLAGS
General options passed to the TeX formatter and typesetter.
+.IP WIN32_INSERT_DEF
+When this is set to true,
+a library build of a WIN32 shared library (.dll file)
+will also build a corresponding .def file at the same time,
+if a .def file is not already listed as a build target.
+
+.IP WIN32DEFPREFIX
+The prefix used to build WIN32 .def files.
+
+.IP WIN32DEFSUFFIX
+The suffix used for WIN32 .def file names.
+
+.IP WIN32DLLPREFIX
+The prefix used to build WIN32 shared libraries (.dll files).
+
+.IP WIN32IMPLIBPREFIX
+The prefix used to build WIN32 import libraries.
+
.IP YACC
The parser generator.
@@ -1379,7 +1504,7 @@ to the directory in which each subsidiary
configure (SConscript) file lives.
Note that you may enable and disable
this ability by calling
-.R SConscriptChdir()
+SConscriptChdir()
multiple times:
.ES
@@ -1401,7 +1526,7 @@ The function must expect three arguments:
.ES
def commandhandler(cmd, args, env):
.EE
-
+.IP
.I cmd
is the path to the command to be executed.
.I args
@@ -1437,8 +1562,10 @@ by default.
can be extended by adding new builders to a construction
environment using the
.B Builder
-function. Builder accepts the following
-arguments:
+function.
+The
+.B Builder
+function accepts the following arguments:
.IP name
The name of the builder. This will be the
@@ -1448,39 +1575,74 @@ used to create an instance of the builder.
.IP action
The command line string used to build the target from the source.
.B action
-can also be a dictionary
+can also be:
+a list of strings representing the command
+to be executed and its arguments
+(suitable for enclosing white space in an argument),
+a dictionary
mapping source file name suffixes to
any combination of command line strings
(if the builder should accept multiple source file extensions),
-Python functions,
-or Action objects
-(see the next section).
+a Python function;
+an Action object
+(see the next section);
+or a list of any of the above.
.IP prefix
The prefix that will be prepended to the target file name.
+The value may also be a function call
+that returns the prefix.
+The function will be passed the environment
+and any extra keyword arguments
+supplied when the Builder is called.
.IP suffix
The suffix that will be appended to the target file name.
+The value may also be a function call
+that returns the suffix.
+The function will be passed the environment
+and any extra keyword arguments
+supplied when the Builder is called.
.IP src_suffix
The expected source file name suffix.
+The value may also be a function call
+that returns the source file name suffix.
+The function will be passed the environment
+and any extra keyword arguments
+supplied when the Builder is called.
.IP src_builder
Specifies a builder to use when a source file name suffix does not match
any of the suffixes of the builder. Using this argument produces a
multi-stage builder.
+.IP emitter
+A function that is passed the target, source, and environment,
+and which returns a tuple containing two lists,
+the list of targets to be built by this builder,
+and the list of sources for this builder.
+This allows the target and source lists to
+be manipulated before the target(s) are actually built.
+Example:
+
+.ES
+def e(target, source, env):
+ return (target + ['foo.foo'], source + ['foo.src'])
+
+b = Builder(name="Foo", emitter=e)
+.EE
+
.IP generator
-A function that returns a list of command lines that will be executed to build
-the target(s) from the source(s). The command lines must be returned as
-lists, where the first element of the list is the executable, and the other
-elements in the list are the arguments that will be passed to the
-executable. The
-.I generator
-and
-.I action
-arguments must not both be used for the same Builder. The generator function
-should take 3 arguments:
+A function that returns a list of actions that will be executed to build
+the target(s) from the source(s).
+The returned action(s) may be
+an Action object, or anything that
+can be converted into an Action object
+(see the next section).
+
+The generator function
+takes 3 arguments:
.I source
- a list of source nodes,
.I target
@@ -1489,12 +1651,36 @@ should take 3 arguments:
- the construction environment. Example:
.ES
-def g(env, source, target):
- return [["gcc", "-c", "-o"] + target + source]
+def g(source, target, env):
+ return [["gcc", "-c", "-o"] + target + source]
b = Builder(name="Object", generator=g)
.EE
+The
+.I generator
+and
+.I action
+arguments must not both be used for the same Builder.
+
+Any additional keyword arguments supplied
+when a Builder object is called
+will be associated with the target
+(and any other files built as a
+result of the call).
+
+.ES
+b = Builder(name='MyBuild', action="build < $SOURCE > $TARGET")
+env = Environment(BUILDERS = [b])
+env.MyBuild('foo.out', 'foo.in', my_arg = 'xyzzy')
+.EE
+
+These extra keyword arguments are passed to the
+following functions:
+command generator functions, funcion Actions,
+emitter functions,
+and functions that generate prefix, suffix or src_suffix.
+
.SS Action Objects
The Builder function will turn its
@@ -1524,6 +1710,24 @@ a command-line Action is returned.
Action('$CC -c -o $TARGET $SOURCES')
.EE
+.IP List
+If the argument is a list,
+then a list of Action objects is returned.
+An Action object is created as necessary
+for each element in the list.
+If an element
+.I within
+the list is itself a list,
+the internal list is the
+command and arguments to be executed via
+the command line.
+This allows white space to be enclosed
+in an argument by defining
+a command in a list within a list:
+.ES
+Action([['cc', '-c', '-DWHITE SPACE', '-o', '$TARGET', '$SOURCES']])
+.EE
+
.IP Function
If the argument is a Python function,
a function Action is returned.
@@ -1566,12 +1770,6 @@ def build_it(target = None, source = None, env = None):
a = Action(build_it)
.EE
-
-.IP List
-If the argument is a list,
-then a list of Action objects is returned.
-An Action object is created as necessary
-for each element in the list.
.PP
If the action argument is not one of the above,
None is returned.
@@ -1605,26 +1803,34 @@ The file names of the sources of the build command.
.LP
For example, given the construction variable CC='cc', targets=['foo'], and
sources=['foo.c', 'bar.c']:
+
.ES
action='$CC -c -o $TARGET $SOURCES'
.EE
+
would produce the command line:
+
.ES
cc -c -o foo foo.c bar.c
.EE
+
Variable names may be surrounded by curly braces ({})
to separate the name from the trailing characters.
Within the curly braces, a variable name may have
a Python slice subscript appended to select one
or more items from a list.
In the previous example, the string:
+
.ES
${SOURCES[1]}
.EE
+
would produce:
+
.ES
bar.c
.EE
+
Additionally, a variable name may
have the following special
modifiers appended within the enclosing curly braces
@@ -1820,11 +2026,19 @@ env = Environment(CPPPATH = ['include1', 'include2'])
env.Program(target = 'foo', source = 'foo.c')
.EE
-.SS Building a Library
+.SS Building a Static Library
.ES
env = Environment()
-env.Library(target = 'mylib', source = 'l1.c l2.c')
+env.Library(target = 'static', source = 'l1.c l2.c')
+.EE
+
+.SS Building a Shared Library
+
+.ES
+env = Environment()
+env.Library(target = 'shared', source = 'l3.c l4.c',
+ shared = 1)
.EE
.SS Linking a Local Library Into a Program
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 1983494..df542b9 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -14,6 +14,50 @@ RELEASE 0.07 -
- Internal cleanup of environment passing to function Actions.
+ - Builders can now take arbitrary keyword arguments to create
+ attributes to be passed to: command generator functions,
+ FunctionAction functions, Builder emitter functions (below),
+ and prefix/suffix generator functions (below).
+
+ - Command generator functions can now return ANYTHING that can be
+ converted into an Action (a function, a string, a CommandGenerator
+ instance, even an ActionBase instance).
+
+ - Actions now call get_contents() with the actual target and source
+ nodes used for the build.
+
+ - A new DictCmdGenerator class replaces CompositeBuilder to support
+ more flexible Builder behavior internally.
+
+ - Builders can now take an emitter= keyword argument. An emitter
+ is a function that takes target, source, and env argument, then
+ return a 2-tuple of (new sources, new targets). The emitter is
+ called when the Builder is __call__'ed, allowing a user to modify
+ source and target lists.
+
+ - The prefix, suffix and src_suffix Builder arguments now take a
+ callable as well a string. The callable is passed the Environment
+ and any extra Builder keyword arguments and is expected to return
+ the appropriate prefix or suffix.
+
+ - CommandActions can now be a string, a list of command + argument
+ strings, or a list of commands (strings or lists).
+
+ - Added shared library support. The Object and Library Builders now
+ take a "shared=1" keyword argument to specify that a shared object
+ or shared library should be built. It is an error to try to build
+ static objects into a shared library or vice versa.
+
+ - Win32 support for .def files has been added. Added the Win32-specific
+ construction variables $WIN32DEFPREFIX, $WIN32DEFSUFFIX,
+ $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX. When building a .dll,
+ the new construction variable $WIN32_INSERT_DEF, controls whether
+ the appropriately-named .def file is inserted into the target
+ list (if not already present). A .lib file is always added to
+ a Library build if not present in the list of targets.
+
+ - ListBuilder now passes all targets to the action, not just the first.
+
From Steven Knight:
- Fix so that -c -n does *not* remove the targets!
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 3d52142..38b5413 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -38,6 +38,7 @@ import sys
import UserDict
import SCons.Util
+import SCons.Errors
print_actions = 1;
execute_actions = 1;
@@ -171,28 +172,53 @@ def GetCommandHandler():
class CommandGenerator:
"""
- Wrappes a command generator function so the Action() factory
+ Wraps a command generator function so the Action() factory
function can tell a generator function from a function action.
"""
def __init__(self, generator):
self.generator = generator
+def _do_create_action(act):
+ """This is the actual "implementation" for the
+ Action factory method, below. This handles the
+ fact that passing lists to Action() itself has
+ different semantics than passing lists as elements
+ of lists.
+
+ The former will create a ListAction, the latter
+ will create a CommandAction by converting the inner
+ list elements to strings."""
-def Action(act):
- """A factory for action objects."""
if isinstance(act, ActionBase):
return act
+ elif SCons.Util.is_List(act):
+ return CommandAction(act)
elif isinstance(act, CommandGenerator):
return CommandGeneratorAction(act.generator)
elif callable(act):
return FunctionAction(act)
elif SCons.Util.is_String(act):
- return CommandAction(act)
- elif SCons.Util.is_List(act):
- return ListAction(act)
+ listCmds = map(lambda x: CommandAction(string.split(x)),
+ string.split(act, '\n'))
+ if len(listCmds) == 1:
+ return listCmds[0]
+ else:
+ return ListAction(listCmds)
else:
return None
+def Action(act):
+ """A factory for action objects."""
+ if SCons.Util.is_List(act):
+ acts = filter(lambda x: not x is None,
+ map(_do_create_action, act))
+ if len(acts) == 1:
+ return acts[0]
+ else:
+ return ListAction(acts)
+ else:
+ return _do_create_action(act)
+
class ActionBase:
"""Base class for actions that create output objects."""
def __cmp__(self, other):
@@ -241,7 +267,7 @@ class ActionBase:
t = [t]
try:
cwd = t[0].cwd
- except AttributeError:
+ except (IndexError, AttributeError):
pass
dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
if dict['TARGETS']:
@@ -260,6 +286,16 @@ class ActionBase:
return dict
+def _string_from_cmd_list(cmd_list):
+ """Takes a list of command line arguments and returns a pretty
+ representation for printing."""
+ cl = []
+ for arg in cmd_list:
+ if ' ' in arg or '\t' in arg:
+ arg = '"' + arg + '"'
+ cl.append(arg)
+ return string.join(cl)
+
_rm = re.compile(r'\$[()]')
_remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
@@ -286,22 +322,19 @@ class EnvDictProxy(UserDict.UserDict):
class CommandAction(ActionBase):
"""Class for command-execution actions."""
- def __init__(self, string):
- self.command = string
+ def __init__(self, cmd):
+ import SCons.Util
+
+ self.cmd_list = map(SCons.Util.to_String, cmd)
def execute(self, **kw):
dict = apply(self.subst_dict, (), kw)
import SCons.Util
- cmd_list = SCons.Util.scons_subst_list(self.command, dict, {}, _rm)
+ cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
for cmd_line in cmd_list:
if len(cmd_line):
if print_actions:
- cl = []
- for arg in cmd_line:
- if ' ' in arg or '\t' in arg:
- arg = '"' + arg + '"'
- cl.append(arg)
- self.show(string.join(cl))
+ self.show(_string_from_cmd_list(cmd_line))
if execute_actions:
try:
ENV = kw['env']['ENV']
@@ -328,7 +361,8 @@ class CommandAction(ActionBase):
def get_raw_contents(self, **kw):
"""Return the complete contents of this action's command line.
"""
- return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {})
+ return SCons.Util.scons_subst(string.join(self.cmd_list),
+ self._sig_dict(kw), {})
def get_contents(self, **kw):
"""Return the signature contents of this action's command line.
@@ -336,19 +370,16 @@ class CommandAction(ActionBase):
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
- return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {}, _remove)
+ return SCons.Util.scons_subst(string.join(self.cmd_list),
+ self._sig_dict(kw), {}, _remove)
class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions."""
def __init__(self, generator):
self.generator = generator
- def execute(self, **kw):
- # ensure that target is a list, to make it easier to write
- # generator functions:
+ def __generate(self, kw):
import SCons.Util
- if kw.has_key("target") and not SCons.Util.is_List(kw["target"]):
- kw["target"] = [kw["target"]]
# Wrap the environment dictionary in an EnvDictProxy
# object to make variable interpolation easier for the
@@ -357,36 +388,19 @@ class CommandGeneratorAction(ActionBase):
if args.has_key("env") and not isinstance(args["env"], EnvDictProxy):
args["env"] = EnvDictProxy(args["env"])
- gen_list = apply(self.generator, (), args)
- gen_list = map(lambda x: map(str, x), gen_list)
+ # ensure that target is a list, to make it easier to write
+ # generator functions:
+ if args.has_key("target") and not SCons.Util.is_List(args["target"]):
+ args["target"] = [args["target"]]
- # Do environment variable substitution on returned command list
- dict = apply(self.subst_dict, (), kw)
- cmd_list = [ ]
- for gen_line in gen_list:
- cmd_list.append([])
- curr_line = cmd_list[-1]
- for gen_arg in gen_line:
- arg_list = SCons.Util.scons_subst_list(gen_arg, dict, {})
- curr_line.extend(arg_list[0])
- if(len(arg_list) > 1):
- cmd_list.extend(arg_list[1:])
- curr_line = cmd_list[-1]
-
- for cmd_line in filter(lambda x: x, cmd_list):
- if print_actions:
- self.show(cmd_line)
- if execute_actions:
- try:
- ENV = kw['env']['ENV']
- except:
- import SCons.Defaults
- ENV = SCons.Defaults.ConstructionEnvironment['ENV']
- ret = spawn(cmd_line[0], cmd_line, ENV)
- if ret:
- return ret
+ ret = apply(self.generator, (), args)
+ gen_cmd = Action(ret)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
+ return gen_cmd
- return 0
+ def execute(self, **kw):
+ return apply(self.__generate(kw).execute, (), kw)
def get_contents(self, **kw):
"""Return the signature contents of this action's command line.
@@ -394,14 +408,7 @@ class CommandGeneratorAction(ActionBase):
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
- kw['source'] = ["__s1__", "__s2__"]
- kw['target'] = ["__t1__", "__t2__"]
- cmd_list = apply(self.generator, (), kw)
- cmd_list = map(lambda x: map(str, x), cmd_list)
- cmd_list = map(lambda x: string.join(x, "\0"), cmd_list)
- cmd_list = map(lambda x: _remove.sub('', x), cmd_list)
- cmd_list = map(lambda x: filter(lambda y: y, string.split(x, "\0")), cmd_list)
- return cmd_list
+ return apply(self.__generate(kw).get_contents, (), kw)
class FunctionAction(ActionBase):
"""Class for Python function actions."""
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 1a0596f..648b77b 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -54,14 +54,27 @@ class ActionTestCase(unittest.TestCase):
exec "a3 = SCons.Action.Action(u'string')"
exec "assert isinstance(a3, SCons.Action.CommandAction), a3"
- a4 = SCons.Action.Action(["x", a2, "y"])
+ a4 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]])
assert isinstance(a4, SCons.Action.ListAction), a4
+ assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0]
+ assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1]
+ assert isinstance(a4.list[2], SCons.Action.CommandAction), a4.list[2]
+ assert isinstance(a4.list[3], SCons.Action.CommandAction), a4.list[3]
+ assert a4.list[3].cmd_list == [ "a", "b", "c" ], a4.list[3].cmd_list
a5 = SCons.Action.Action(1)
assert a5 is None, a5
a6 = SCons.Action.Action(a1)
- assert a6 is a1
+ assert a6 is a1, a6
+
+ a7 = SCons.Action.Action([[ "explicit", "command", "line" ]])
+ assert isinstance(a7, SCons.Action.CommandAction), a7
+ assert a7.cmd_list == [ "explicit", "command", "line" ], a7.cmd_list
+
+ a8 = SCons.Action.Action(["a8"])
+ assert isinstance(a8, SCons.Action.CommandAction), a8
+ assert a8.cmd_list == [ "a8" ], a8.cmd_list
class ActionBaseTestCase(unittest.TestCase):
@@ -106,8 +119,8 @@ class CommandActionTestCase(unittest.TestCase):
def test_init(self):
"""Test creation of a command Action
"""
- a = SCons.Action.CommandAction("xyzzy")
- assert a.command == "xyzzy"
+ a = SCons.Action.CommandAction(["xyzzy"])
+ assert a.cmd_list == [ "xyzzy" ], a.cmd_list
def test_execute(self):
"""Test executing a command Action
@@ -127,23 +140,27 @@ class CommandActionTestCase(unittest.TestCase):
return 0
SCons.Action.SetCommandHandler(func)
assert SCons.Action.spawn is func
- a = SCons.Action.CommandAction("xyzzy")
+ a = SCons.Action.CommandAction(["xyzzy"])
a.execute()
assert t.executed == 1
def test_get_raw_contents(self):
"""Test fetching the contents of a command Action
"""
- a = SCons.Action.CommandAction("| $( $foo | $bar $) |")
- c = a.get_contents(foo = 'FFF', bar = 'BBB')
+ a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
+ "$)", "|"])
+ c = a.get_contents(target=[], source=[],
+ foo = 'FFF', bar = 'BBB')
assert c == "| $( FFF | BBB $) |"
def test_get_contents(self):
"""Test fetching the contents of a command Action
"""
- a = SCons.Action.CommandAction("| $foo $( | $) $bar |")
- c = a.get_contents(foo = 'FFF', bar = 'BBB')
- assert c == "| FFF BBB |"
+ a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
+ "$)", "|"])
+ c = a.get_contents(target=[], source=[],
+ foo = 'FFF', bar = 'BBB')
+ assert c == "| |", c
class CommandGeneratorActionTestCase(unittest.TestCase):
@@ -164,7 +181,14 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert env.subst('$FOO') == 'foo baz\nbar ack', env.subst('$FOO')
assert env.subst_list('$FOO') == [ [ 'foo', 'baz' ],
[ 'bar', 'ack' ] ], env.subst_list('$FOO')
- return [["$FOO"]]
+ return "$FOO"
+ def func_action(env, dummy, self=self):
+ assert env.subst('$foo') == 'bar', env.subst('$foo')
+ assert env.subst_list('$foo') == [ [ 'bar' ] ], env.subst_list('$foo')
+ assert env.subst_list([ '$foo', 'bar' ]) == [[ 'bar', 'bar' ]], env.subst_list([ [ '$foo', 'bar' ] ])
+ self.dummy=dummy
+ def f2(dummy, env, f=func_action):
+ return f
def ch(cmd, args, env, self=self):
self.cmd.append(cmd)
self.args.append(args)
@@ -182,6 +206,11 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert self.dummy == 1
assert self.cmd == [ 'foo', 'bar'], self.cmd
assert self.args == [ [ 'foo', 'baz' ], [ 'bar', 'ack' ] ], self.args
+
+ b=SCons.Action.CommandGeneratorAction(f2)
+ self.dummy = 0
+ b.execute(dummy=2, env={ 'foo' : 'bar' })
+ assert self.dummy==2, self.dummy
del self.dummy
def test_get_contents(self):
@@ -191,8 +220,9 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
return [["guux", foo, "$(", "ignore", "$)", bar]]
a = SCons.Action.CommandGeneratorAction(f)
- c = a.get_contents(foo = 'FFF', bar = 'BBB')
- assert c == [["guux", 'FFF', 'BBB']], c
+ c = a.get_contents(target=[], source=[],
+ foo = 'FFF', bar = 'BBB')
+ assert c == "guux FFF BBB", c
class FunctionActionTestCase(unittest.TestCase):
@@ -227,7 +257,7 @@ class FunctionActionTestCase(unittest.TestCase):
"""Test fetching the contents of a function Action
"""
a = SCons.Action.FunctionAction(Func)
- c = a.get_contents()
+ c = a.get_contents(target=[], source=[])
assert c == "\177\036\000\177\037\000d\000\000S", repr(c)
class ListActionTestCase(unittest.TestCase):
@@ -241,8 +271,7 @@ class ListActionTestCase(unittest.TestCase):
assert isinstance(a.list[0], SCons.Action.CommandAction)
assert isinstance(a.list[1], SCons.Action.FunctionAction)
assert isinstance(a.list[2], SCons.Action.ListAction)
- assert isinstance(a.list[2].list[0], SCons.Action.CommandAction)
- assert isinstance(a.list[2].list[1], SCons.Action.CommandAction)
+ assert a.list[2].list[0].cmd_list == [ 'y' ]
def test_execute(self):
"""Test executing a list of subsidiary Actions
@@ -259,7 +288,7 @@ class ListActionTestCase(unittest.TestCase):
"""Test fetching the contents of a list of subsidiary Actions
"""
a = SCons.Action.ListAction(["x", "y", "z"])
- c = a.get_contents()
+ c = a.get_contents(target=[], source=[])
assert c == "xyz", c
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 2ccba00..2a1b7d8 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -33,32 +33,61 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os.path
import string
-from Errors import UserError
+import copy
+from SCons.Errors import UserError
import SCons.Action
import SCons.Node
import SCons.Node.FS
import SCons.Util
+class DictCmdGenerator:
+ """This is a callable class that can be used as a
+ command generator function. It holds on to a dictionary
+ mapping file suffixes to Actions. It uses that dictionary
+ to return the proper action based on the file suffix of
+ the source file."""
+
+ def __init__(self, action_dict):
+ self.action_dict = action_dict
+
+ def src_suffixes(self):
+ return self.action_dict.keys()
+
+ def __call__(self, source, target, env, **kw):
+ ext = None
+ for src in map(str, source):
+ my_ext = os.path.splitext(src)[1]
+ if ext and my_ext != ext:
+ raise UserError("Cannot build multiple sources with different extensions.")
+ ext = my_ext
+
+ if ext is None:
+ raise UserError("Cannot deduce file extension from source files: %s" % repr(map(str, source)))
+ try:
+ # XXX Do we need to perform Environment substitution
+ # on the keys of action_dict before looking it up?
+ return self.action_dict[ext]
+ except KeyError:
+ raise UserError("Don't know how to build a file with suffix %s." % ext)
def Builder(**kw):
"""A factory for builder objects."""
-
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.CommandGenerator(kw['generator'])
del kw['generator']
-
- if kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
- return apply(CompositeBuilder, (), kw)
- elif kw.has_key('src_builder'):
+ elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
+ action_dict = kw['action']
+ kw['action'] = SCons.Action.CommandGenerator(DictCmdGenerator(action_dict))
+ kw['src_suffix'] = action_dict.keys()
+
+ if kw.has_key('src_builder'):
return apply(MultiStepBuilder, (), kw)
else:
return apply(BuilderBase, (), kw)
-
-
def _init_nodes(builder, env, tlist, slist):
"""Initialize lists of target and source nodes with all of
the proper Builder information.
@@ -76,6 +105,29 @@ def _init_nodes(builder, env, tlist, slist):
t.add_source(slist)
if builder.scanner:
t.target_scanner = builder.scanner
+
+class _callable_adaptor:
+ """When crteating a Builder, you can pass a string OR
+ a callable in for prefix, suffix, or src_suffix.
+ src_suffix even takes a list!
+
+ If a string or list is passed, we use this class to
+ adapt it to a callable."""
+ def __init__(self, static):
+ self.static = static
+
+ def __call__(self, **kw):
+ return self.static
+
+ def __cmp__(self, other):
+ if isinstance(other, _callable_adaptor):
+ return cmp(self.static, other.static)
+ return -1
+
+def _adjust_suffix(suff):
+ if suff and not suff[0] in [ '.', '$' ]:
+ return '.' + suff
+ return suff
class BuilderBase:
"""Base class for Builders, objects that create output
@@ -90,27 +142,40 @@ class BuilderBase:
node_factory = SCons.Node.FS.default_fs.File,
target_factory = None,
source_factory = None,
- scanner = None):
+ scanner = None,
+ emitter = None):
if name is None:
raise UserError, "You must specify a name for the builder."
self.name = name
self.action = SCons.Action.Action(action)
- self.prefix = prefix
- self.suffix = suffix
- self.src_suffix = src_suffix
+ if callable(prefix):
+ self.prefix = prefix
+ else:
+ self.prefix = _callable_adaptor(str(prefix))
+
+ if callable(suffix):
+ self.suffix = suffix
+ else:
+ self.suffix = _callable_adaptor(str(suffix))
+
+ if callable(src_suffix):
+ self.src_suffix = src_suffix
+ elif SCons.Util.is_String(src_suffix):
+ self.src_suffix = _callable_adaptor([ str(src_suffix) ])
+ else:
+ self.src_suffix = _callable_adaptor(src_suffix)
+
self.target_factory = target_factory or node_factory
self.source_factory = source_factory or node_factory
self.scanner = scanner
- if self.suffix and self.suffix[0] not in '.$':
- self.suffix = '.' + self.suffix
- if self.src_suffix and self.src_suffix[0] not in '.$':
- self.src_suffix = '.' + self.src_suffix
+
+ self.emitter = emitter
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
- def _create_nodes(self, env, target = None, source = None):
+ def _create_nodes(self, env, args, target = None, source = None):
"""Create and return lists of target and source nodes.
"""
def adjustixes(files, pre, suf):
@@ -122,25 +187,46 @@ class BuilderBase:
if pre and f[:len(pre)] != pre:
path, fn = os.path.split(os.path.normpath(f))
f = os.path.join(path, pre + fn)
- if suf:
- if f[-len(suf):] != suf:
- f = f + suf
- ret.append(f)
- return ret
-
+ # Only append a suffix if the file does not have one.
+ if suf and not os.path.splitext(f)[1]:
+ if f[-len(suf):] != suf:
+ f = f + suf
+ ret.append(f)
+ return ret
+
+ pre = self.get_prefix(env, args)
+ suf = self.get_suffix(env, args)
tlist = SCons.Node.arg2nodes(adjustixes(target,
- env.subst(self.prefix),
- env.subst(self.suffix)),
+ pre, suf),
self.target_factory)
-
+ src_suf = self.get_src_suffix(env, args)
slist = SCons.Node.arg2nodes(adjustixes(source,
None,
- env.subst(self.src_suffix)),
+ src_suf),
self.source_factory)
+ if self.emitter:
+ emit_args = { 'target' : tlist,
+ 'source' : slist,
+ 'env' : env }
+ emit_args.update(args)
+ target, source = apply(self.emitter, (), emit_args)
+
+ # Have to run it through again in case the
+ # function returns non-Node targets/sources.
+ tlist = SCons.Node.arg2nodes(adjustixes(target,
+ pre, suf),
+ self.target_factory)
+ slist = SCons.Node.arg2nodes(adjustixes(source,
+ None,
+ src_suf),
+ self.source_factory)
+
+ for t in tlist:
+ t.build_args = args
return tlist, slist
- def __call__(self, env, target = None, source = None):
- tlist, slist = self._create_nodes(env, target, source)
+ def __call__(self, env, target = None, source = None, **kw):
+ tlist, slist = self._create_nodes(env, kw, target, source)
if len(tlist) == 1:
_init_nodes(self, env, tlist, slist)
@@ -167,10 +253,23 @@ class BuilderBase:
"""
return apply(self.action.get_contents, (), kw)
- def src_suffixes(self, env):
- if self.src_suffix != '':
- return [env.subst(self.src_suffix)]
- return []
+ def src_suffixes(self, env, args):
+ return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
+ apply(self.src_suffix, (), args))
+
+ def get_src_suffix(self, env, args):
+ """Get the first src_suffix in the list of src_suffixes."""
+ ret = self.src_suffixes(env, args)
+ if not ret:
+ return ''
+ else:
+ return ret[0]
+
+ def get_suffix(self, env, args):
+ return env.subst(_adjust_suffix(apply(self.suffix, (), args)))
+
+ def get_prefix(self, env, args):
+ return env.subst(apply(self.prefix, (), args))
def targets(self, node):
"""Return the list of targets for this builder instance.
@@ -199,7 +298,7 @@ class ListBuilder:
# unlink all targets and make all directories
# before building anything
t.prepare()
- kw['target'] = self.tlist[0]
+ kw['target'] = self.tlist
self.status = apply(self.builder.execute, (), kw)
for t in self.tlist:
if not t is kw['target']:
@@ -212,8 +311,8 @@ class ListBuilder:
def get_contents(self, **kw):
return apply(self.builder.get_contents, (), kw)
- def src_suffixes(self, env):
- return self.builder.src_suffixes(env)
+ def src_suffixes(self, env, args):
+ return self.builder.src_suffixes(env, args)
def targets(self, node):
"""Return the list of targets for this builder instance.
@@ -240,112 +339,55 @@ class MultiStepBuilder(BuilderBase):
node_factory = SCons.Node.FS.default_fs.File,
target_factory = None,
source_factory = None,
- scanner=None):
+ scanner=None,
+ emitter=None):
BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
node_factory, target_factory, source_factory,
- scanner)
+ scanner, emitter)
+ if not SCons.Util.is_List(src_builder):
+ src_builder = [ src_builder ]
self.src_builder = src_builder
+ self.sdict = {}
- def __call__(self, env, target = None, source = None):
+ def __call__(self, env, target = None, source = None, **kw):
slist = SCons.Node.arg2nodes(source, self.source_factory)
final_sources = []
- src_suffix = env.subst(self.src_suffix)
- sdict = {}
- for suff in self.src_builder.src_suffixes(env):
- sdict[suff] = None
+
+ r=repr(env)
+ try:
+ sdict = self.sdict[r]
+ except KeyError:
+ sdict = {}
+ self.sdict[r] = sdict
+ for bld in self.src_builder:
+ for suf in bld.src_suffixes(env, kw):
+ sdict[suf] = bld
+
for snode in slist:
path, ext = os.path.splitext(snode.abspath)
if sdict.has_key(ext):
- tgt = self.src_builder(env, target = [ path ], source = snode)
+ src_bld = sdict[ext]
+
+ dictArgs = copy.copy(kw)
+ dictArgs['target'] = [path]
+ dictArgs['source'] = snode
+ dictArgs['env'] = env
+ tgt = apply(src_bld, (), dictArgs)
if not SCons.Util.is_List(tgt):
final_sources.append(tgt)
else:
final_sources.extend(tgt)
else:
final_sources.append(snode)
- return BuilderBase.__call__(self, env, target=target,
- source=final_sources)
-
- def src_suffixes(self, env):
- return BuilderBase.src_suffixes(self, env) + \
- self.src_builder.src_suffixes(env)
-
-class CompositeBuilder(BuilderBase):
- """This is a convenient Builder subclass that can build different
- files based on their suffixes. For each target, this builder
- will examine the target's sources. If they are all the same
- suffix, and that suffix is equal to one of the child builders'
- src_suffix, then that child builder will be used. Otherwise,
- UserError is thrown."""
- def __init__(self, name = None,
- prefix='',
- suffix='',
- action = {},
- src_builder = []):
- BuilderBase.__init__(self, name=name, prefix=prefix,
- suffix=suffix)
- if src_builder and not SCons.Util.is_List(src_builder):
- src_builder = [src_builder]
- self.src_builder = src_builder
- self.action_dict = action
- self.sdict = {}
- self.sbuild = {}
-
- def __call__(self, env, target = None, source = None):
- tlist, slist = BuilderBase._create_nodes(self, env,
- target=target, source=source)
-
- r = repr(env)
- if not self.sdict.has_key(r):
- self.sdict[r] = {}
- self.sbuild[r] = []
- for suff in self.src_suffixes(env):
- suff = env.subst(suff)
- self.sdict[r][suff] = suff
- self.sbuild[r].extend(filter(lambda x, e=env, s=suff:
- e.subst(x.suffix) == s,
- self.src_builder))
- for sb in self.sbuild[r]:
- suff = env.subst(sb.suffix)
- for s in sb.src_suffixes(env):
- self.sdict[r][env.subst(s)] = suff
-
- sufflist = map(lambda x, s=self.sdict[r]:
- s[os.path.splitext(x.path)[1]],
- slist)
- last_suffix = ''
- for suff in sufflist:
- if last_suffix and last_suffix != suff:
- raise UserError, "The builder for %s can only build source files of identical suffixes: %s." % \
- (tlist[0].path,
- str(map(lambda t: str(t.path), tlist[0].sources)))
- last_suffix = suff
-
- if last_suffix:
- kw = {
- 'name' : self.name,
- 'action' : self.action_dict[last_suffix],
- 'src_suffix' : last_suffix,
- }
- if self.sbuild[r]:
- sb = filter(lambda x, e=env, s=last_suffix:
- e.subst(x.suffix) == s,
- self.sbuild[r])
- if sb:
- kw['src_builder'] = sb[0]
- # XXX We should be able to cache this
- bld = apply(Builder, (), kw)
- for tnode in tlist:
- bld.__call__(env, target = tnode, source = slist)
-
- if len(tlist) == 1:
- tlist = tlist[0]
- return tlist
-
- def src_suffixes(self, env):
- suffixes = map(lambda k, e=env: e.subst(k), self.action_dict.keys()) + \
- reduce(lambda x, y: x + y,
- map(lambda b, e=env: b.src_suffixes(e),
- self.src_builder),
- [])
- return suffixes
+ dictKwArgs = kw
+ dictKwArgs['target'] = target
+ dictKwArgs['source'] = final_sources
+ return apply(BuilderBase.__call__,
+ (self, env), dictKwArgs)
+
+ def src_suffixes(self, env, args):
+ return BuilderBase.src_suffixes(self, env, args) + \
+ reduce(lambda x, y: x + y,
+ map(lambda b, e=env, args=args: b.src_suffixes(e, args),
+ self.src_builder),
+ [])
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 0527fe0..e30079c 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -82,6 +82,11 @@ class Environment:
return self.d.get(s, s)
def get_scanner(self, ext):
return env_scanner
+ def Dictionary(self):
+ return {}
+ def autogenerate(self, dir=''):
+ return {}
+
env = Environment()
class BuilderTestCase(unittest.TestCase):
@@ -129,20 +134,23 @@ class BuilderTestCase(unittest.TestCase):
assert target.sources[0].name == 'n10'
assert target.sources[1].name == 'n11'
- if hasattr(types, 'UnicodeType'):
- code = """if 1:
- targets = builder(env, target = u'n12 n13', source = [u'n14 n15'])
- assert targets[0].name == u'n12'
- assert targets[0].sources[0].name == u'n14 n15'
- assert targets[1].name == u'n13'
- assert targets[1].sources[0].name == u'n14 n15'
-
- target = builder(env, target = [u'n16 n17'], source = u'n18 n19')
- assert target.name == u'n16 n17'
- assert target.sources[0].name == u'n18'
- assert target.sources[1].name == u'n19'
- \n"""
- exec code
+ if not hasattr(types, 'UnicodeType'):
+ uni = str
+ else:
+ uni = unicode
+
+ targets = builder(env, target = uni('n12 n13'),
+ source = [uni('n14 n15')])
+ assert targets[0].name == uni('n12')
+ assert targets[0].sources[0].name == uni('n14 n15')
+ assert targets[1].name == uni('n13')
+ assert targets[1].sources[0].name == uni('n14 n15')
+
+ target = builder(env, target = [uni('n16 n17')],
+ source = uni('n18 n19'))
+ assert target.name == uni('n16 n17')
+ assert target.sources[0].name == uni('n18')
+ assert target.sources[1].name == uni('n19')
def test_noname(self):
"""Test error reporting for missing name
@@ -163,7 +171,7 @@ class BuilderTestCase(unittest.TestCase):
Verify that we can retrieve the supplied action attribute.
"""
builder = SCons.Builder.Builder(name="builder", action="foo")
- assert builder.action.command == "foo"
+ assert builder.action.cmd_list == ["foo"]
def test_generator(self):
"""Test Builder creation given a generator function."""
@@ -276,7 +284,8 @@ class BuilderTestCase(unittest.TestCase):
def my_show(string):
global show_string
show_string = show_string + string + "\n"
- builder.action.show = my_show
+ for action in builder.action.list:
+ action.show = my_show
r = builder.execute()
assert r == 0
@@ -341,7 +350,7 @@ class BuilderTestCase(unittest.TestCase):
def __init__(self, **kw):
open(kw['out'], 'a').write("class2b\n")
- builder = MyBuilder(action = [cmd2, function2, class2a(), class2b], name = "clist")
+ builder = MyBuilder(action = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]), name = "clist")
r = builder.execute(out = outfile)
assert r.__class__ == class2b
c = test.read(outfile, 'r')
@@ -384,7 +393,7 @@ class BuilderTestCase(unittest.TestCase):
contents = b2.get_contents()
assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
- b3 = SCons.Builder.Builder(name = "b3", action = ["foo", Func, "bar"])
+ b3 = SCons.Builder.Builder(name = "b3", action = SCons.Action.ListAction(["foo", Func, "bar"]))
contents = b3.get_contents()
assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
@@ -430,9 +439,9 @@ class BuilderTestCase(unittest.TestCase):
Make sure that there is no '.' separator appended.
"""
builder = SCons.Builder.Builder(name = "builder", prefix = 'lib.')
- assert builder.prefix == 'lib.'
+ assert builder.get_prefix(env,{}) == 'lib.'
builder = SCons.Builder.Builder(name = "builder", prefix = 'lib')
- assert builder.prefix == 'lib'
+ assert builder.get_prefix(env,{}) == 'lib'
tgt = builder(env, target = 'tgt1', source = 'src1')
assert tgt.path == 'libtgt1', \
"Target has unexpected name: %s" % tgt.path
@@ -451,7 +460,7 @@ class BuilderTestCase(unittest.TestCase):
env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
b1 = SCons.Builder.Builder(name = "builder", src_suffix = '.c')
- assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
+ assert b1.src_suffixes(env,{}) == ['.c'], b1.src_suffixes(env,{})
tgt = b1(env, target = 'tgt2', source = 'src2')
assert tgt.sources[0].path == 'src2.c', \
@@ -466,19 +475,19 @@ class BuilderTestCase(unittest.TestCase):
b2 = SCons.Builder.Builder(name = "b2",
src_suffix = '.2',
src_builder = b1)
- assert b2.src_suffixes(env) == ['.2', '.c'], b2.src_suffixes(env)
+ assert b2.src_suffixes(env,{}) == ['.2', '.c'], b2.src_suffixes(env,{})
b3 = SCons.Builder.Builder(name = "b3",
action = {'.3a' : '', '.3b' : ''})
- s = b3.src_suffixes(env)
+ s = b3.src_suffixes(env,{})
s.sort()
assert s == ['.3a', '.3b'], s
b4 = SCons.Builder.Builder(name = "b4", src_suffix = '$XSUFFIX')
- assert b4.src_suffixes(env) == ['.x'], b4.src_suffixes(env)
+ assert b4.src_suffixes(env,{}) == ['.x'], b4.src_suffixes(env,{})
b5 = SCons.Builder.Builder(name = "b5", action = {'$YSUFFIX' : ''})
- assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
+ assert b5.src_suffixes(env,{}) == ['.y'], b5.src_suffixes(env,{})
def test_suffix(self):
"""Test Builder creation with a specified target suffix
@@ -487,9 +496,9 @@ class BuilderTestCase(unittest.TestCase):
beginning if it isn't already present.
"""
builder = SCons.Builder.Builder(name = "builder", suffix = '.o')
- assert builder.suffix == '.o'
+ assert builder.get_suffix(env,{}) == '.o', builder.get_suffix(env,{})
builder = SCons.Builder.Builder(name = "builder", suffix = 'o')
- assert builder.suffix == '.o'
+ assert builder.get_suffix(env,{}) == '.o', builder.get_suffix(env,{})
tgt = builder(env, target = 'tgt3', source = 'src3')
assert tgt.path == 'tgt3.o', \
"Target has unexpected name: %s" % tgt[0].path
@@ -575,20 +584,24 @@ class BuilderTestCase(unittest.TestCase):
def test_CompositeBuilder(self):
"""Testing CompositeBuilder class."""
+ def func_action(target, source, env):
+ return 0
+
builder = SCons.Builder.Builder(name = "builder",
- action={ '.foo' : 'foo',
- '.bar' : 'bar' })
+ action={ '.foo' : func_action,
+ '.bar' : func_action })
- assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder, SCons.Builder.BuilderBase)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
tgt = builder(env, target='test1', source='test1.foo')
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
- assert tgt.builder.action.command == 'foo'
- tgt = builder(env, target='test2', source='test2.bar')
- assert tgt.builder.action.command == 'bar'
+ assert isinstance(tgt.builder.action.generator, SCons.Builder.DictCmdGenerator)
flag = 0
+ tgt = builder(env, target='test2', source='test2.bar test1.foo')
try:
- tgt = builder(env, target='test2', source='test2.bar test1.foo')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
@@ -601,7 +614,8 @@ class BuilderTestCase(unittest.TestCase):
action = { '.foo' : 'foo',
'.bar' : 'bar' },
src_builder = foo_bld)
- assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder, SCons.Builder.MultiStepBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
tgt = builder(env, target='t1', source='t1a.ina t1b.ina')
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
@@ -618,7 +632,8 @@ class BuilderTestCase(unittest.TestCase):
action = { '.foo' : 'foo',
'.bar' : 'bar' },
src_builder = [foo_bld, bar_bld])
- assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder, SCons.Builder.MultiStepBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')
assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
@@ -627,26 +642,33 @@ class BuilderTestCase(unittest.TestCase):
assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
flag = 0
+ tgt = builder(env, target='t5', source='test5a.foo test5b.inb')
try:
- tgt = builder(env, target='t5', source='test5a.foo test5b.inb')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
flag = 0
+ tgt = builder(env, target='t6', source='test6a.bar test6b.ina')
try:
- tgt = builder(env, target='t6', source='test6a.bar test6b.ina')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
flag = 0
+ tgt = builder(env, target='t4', source='test4a.ina test4b.inb')
try:
- tgt = builder(env, target='t4', source='test4a.ina test4b.inb')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
+
def test_build_scanner(self):
"""Testing ability to set a target scanner through a builder."""
global instanced
@@ -683,6 +705,44 @@ class BuilderTestCase(unittest.TestCase):
assert tgt.target_scanner != env_scanner, tgt.target_scanner
assert src.source_scanner == env_scanner
+ def test_Builder_Args(self):
+ """Testing passing extra agrs to a builder."""
+ def buildFunc(target, source, env, foo, bar, s=self):
+ s.foo=foo
+ s.bar=bar
+
+ builder = SCons.Builder.Builder(name="builder", action=buildFunc)
+ tgt = builder(env, target='foo', source='bar', foo=1, bar=2)
+ tgt.build()
+ assert self.foo == 1, self.foo
+ assert self.bar == 2, self.bar
+
+ def test_emitter(self):
+ """Test emitter functions."""
+ def emit(target, source, env, foo=0, bar=0):
+ if foo:
+ target.append("bar")
+ if bar:
+ source.append("foo")
+ return ( target, source )
+
+ builder = SCons.Builder.Builder(name="builder", action='foo',
+ emitter=emit)
+ tgt = builder(env, target='foo', source='bar')
+ assert str(tgt) == 'foo', str(tgt)
+ assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+ tgt = builder(env, target='foo', source='bar', foo=1)
+ assert len(tgt) == 2, len(tgt)
+ assert 'foo' in map(str, tgt), map(str, tgt)
+ assert 'bar' in map(str, tgt), map(str, tgt)
+
+ tgt = builder(env, target='foo', source='bar', bar=1)
+ assert str(tgt) == 'foo', str(tgt)
+ assert len(tgt.sources) == 2, len(tgt.sources)
+ assert 'foo' in map(str, tgt.sources), map(str, tgt.sources)
+ assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
if __name__ == "__main__":
suite = unittest.makeSuite(BuilderTestCase, 'test_')
if not unittest.TextTestRunner().run(suite).wasSuccessful():
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 711130d..67317a3 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -40,6 +40,7 @@ import os
import stat
import string
import sys
+import os.path
import SCons.Action
import SCons.Builder
@@ -50,8 +51,33 @@ import SCons.Scanner.C
import SCons.Scanner.Prog
import SCons.Util
-
-
+class SharedCmdGenerator:
+ """A callable class that acts as a command generator.
+ It is designed to hold on to 2 actions, and return
+ one if the shared=1 keyword arg is supplied to the
+ Builder method, and the other if not.
+
+ Also, all target nodes will have the shared attribute
+ set to match the vaue of the shared keyword argument,
+ zero by default."""
+ def __init__(self, static, shared):
+ self.action_static = static
+ self.action_shared = shared
+
+ def __call__(self, target, source, env, shared=0):
+ for src in source:
+ try:
+ if src.attributes.shared != shared:
+ raise UserError("Source file: %s must be built with shared=%s in order to be compatible with the selected target." % (src, str(shared)))
+ except AttributeError:
+ pass
+ for t in target:
+ t.attributes.shared = shared
+ if shared:
+ return self.action_shared
+ else:
+ return self.action_static
+
CFile = SCons.Builder.Builder(name = 'CFile',
action = { '.l' : '$LEXCOM',
'.y' : '$YACCCOM',
@@ -64,29 +90,48 @@ CXXFile = SCons.Builder.Builder(name = 'CXXFile',
},
suffix = '$CXXFILESUFFIX')
-CPlusPlusAction = SCons.Action.Action('$CXXCOM')
-
-FortranAction = SCons.Action.Action('$F77COM')
-
-FortranPPAction = SCons.Action.Action('$F77PPCOM')
-
+CXXAction = SCons.Action.Action("$CXXCOM")
+ShCXXAction = SCons.Action.Action("$SHCXXCOM")
+F77Action = SCons.Action.Action("$F77COM")
+ShF77Action = SCons.Action.Action("$SHF77COM")
+F77PPAction = SCons.Action.Action("$F77PPCOM")
+ShF77PPAction = SCons.Action.Action("$SHF77PPCOM")
+
+shared_obj = SCons.Builder.DictCmdGenerator({ ".C" : ShCXXAction,
+ ".cc" : ShCXXAction,
+ ".cpp" : ShCXXAction,
+ ".cxx" : ShCXXAction,
+ ".c++" : ShCXXAction,
+ ".C++" : ShCXXAction,
+ ".c" : "$SHCCCOM",
+ ".f" : ShF77Action,
+ ".for" : ShF77Action,
+ ".FOR" : ShF77Action,
+ ".F" : ShF77PPAction,
+ ".fpp" : ShF77PPAction,
+ ".FPP" : ShF77PPAction })
+
+static_obj = SCons.Builder.DictCmdGenerator({ ".C" : CXXAction,
+ ".cc" : CXXAction,
+ ".cpp" : CXXAction,
+ ".cxx" : CXXAction,
+ ".c++" : CXXAction,
+ ".C++" : CXXAction,
+ ".c" : "$CCCOM",
+ ".f" : F77Action,
+ ".for" : F77Action,
+ ".F" : F77PPAction,
+ ".FOR" : F77Action,
+ ".fpp" : F77PPAction,
+ ".FPP" : F77PPAction })
+
Object = SCons.Builder.Builder(name = 'Object',
- action = { '.c' : '$CCCOM',
- '.C' : CPlusPlusAction,
- '.cc' : CPlusPlusAction,
- '.cpp' : CPlusPlusAction,
- '.cxx' : CPlusPlusAction,
- '.c++' : CPlusPlusAction,
- '.C++' : CPlusPlusAction,
- '.f' : FortranAction,
- '.for' : FortranAction,
- '.FOR' : FortranAction,
- '.F' : FortranPPAction,
- '.fpp' : FortranPPAction,
- '.FPP' : FortranPPAction,
- },
+ generator = \
+ SharedCmdGenerator(static=SCons.Action.CommandGeneratorAction(static_obj),
+ shared=SCons.Action.CommandGeneratorAction(shared_obj)),
prefix = '$OBJPREFIX',
suffix = '$OBJSUFFIX',
+ src_suffix = static_obj.src_suffixes(),
src_builder = [CFile, CXXFile])
Program = SCons.Builder.Builder(name = 'Program',
@@ -97,12 +142,91 @@ Program = SCons.Builder.Builder(name = 'Program',
src_builder = Object,
scanner = SCons.Scanner.Prog.ProgScan())
-Library = SCons.Builder.Builder(name = 'Library',
- action = '$ARCOM',
- prefix = '$LIBPREFIX',
- suffix = '$LIBSUFFIX',
- src_suffix = '$OBJSUFFIX',
- src_builder = Object)
+class LibAffixGenerator:
+ def __init__(self, static, shared):
+ self.static_affix = static
+ self.shared_affix = shared
+
+ def __call__(self, shared=0, win32=0):
+ if shared:
+ return self.shared_affix
+ return self.static_affix
+
+def win32LibGenerator(target, source, env, shared=1):
+ listCmd = [ "$SHLINK", "$SHLINKFLAGS" ]
+
+ for tgt in target:
+ ext = os.path.splitext(str(tgt))[1]
+ if ext == env.subst("$LIBSUFFIX"):
+ # Put it on the command line as an import library.
+ listCmd.append("${WIN32IMPLIBPREFIX}%s" % tgt)
+ else:
+ listCmd.append("${WIN32DLLPREFIX}%s" % tgt)
+
+ listCmd.extend([ '$_LIBDIRFLAGS', '$_LIBFLAGS' ])
+ for src in source:
+ ext = os.path.splitext(str(src))[1]
+ if ext == env.subst("$WIN32DEFSUFFIX"):
+ # Treat this source as a .def file.
+ listCmd.append("${WIN32DEFPREFIX}%s" % src)
+ else:
+ # Just treat it as a generic source file.
+ listCmd.append(str(src))
+ return listCmd
+
+def win32LibEmitter(target, source, env, shared=0):
+ if shared:
+ dll = None
+ for tgt in target:
+ ext = os.path.splitext(str(tgt))[1]
+ if ext == env.subst("$SHLIBSUFFIX"):
+ dll = tgt
+ break
+ if not dll:
+ raise UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX"))
+
+ if env.has_key("WIN32_INSERT_DEF") and \
+ env["WIN32_INSERT_DEF"] and \
+ not '.def' in map(lambda x: os.path.split(str(x))[1],
+ source):
+
+ # append a def file to the list of sources
+ source.append("%s%s" % (os.path.splitext(str(dll))[0],
+ env.subst("$WIN32DEFSUFFIX")))
+ if not env.subst("$LIBSUFFIX") in \
+ map(lambda x: os.path.split(str(x))[1], target):
+ # Append an import library to the list of targets.
+ target.append("%s%s%s" % (env.subst("$LIBPREFIX"),
+ os.path.splitext(str(dll))[0],
+ env.subst("$LIBSUFFIX")))
+ return (target, source)
+
+PosixLibrary = SCons.Builder.Builder(name = 'Library',
+ generator = \
+ SharedCmdGenerator(shared="$SHLINKCOM",
+ static="$ARCOM"),
+ prefix = \
+ LibAffixGenerator(static='$LIBPREFIX',
+ shared='$SHLIBPREFIX'),
+ suffix = \
+ LibAffixGenerator(static='$LIBSUFFIX',
+ shared='$SHLIBSUFFIX'),
+ src_suffix = '$OBJSUFFIX',
+ src_builder = Object)
+
+Win32Library = SCons.Builder.Builder(name = 'Library',
+ generator = \
+ SharedCmdGenerator(shared=SCons.Action.CommandGeneratorAction(win32LibGenerator),
+ static="$ARCOM"),
+ emitter = win32LibEmitter,
+ prefix = \
+ LibAffixGenerator(static='$LIBPREFIX',
+ shared='$SHLIBPREFIX'),
+ suffix = \
+ LibAffixGenerator(static='$LIBSUFFIX',
+ shared='$SHLIBSUFFIX'),
+ src_suffix = '$OBJSUFFIX',
+ src_builder = Object)
LaTeXAction = SCons.Action.Action('$LATEXCOM')
@@ -244,21 +368,36 @@ def make_win32_env_from_paths(include, lib, path):
'CC' : 'cl',
'CCFLAGS' : '/nologo',
'CCCOM' : '$CC $CCFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+ 'SHCC' : '$CC',
+ 'SHCCFLAGS' : '$CCFLAGS',
+ 'SHCCCOM' : '$SHCC $SHCCFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
'CFILESUFFIX' : '.c',
'CXX' : '$CC',
'CXXFLAGS' : '$CCFLAGS',
'CXXCOM' : '$CXX $CXXFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+ 'SHCXX' : '$CXX',
+ 'SHCXXFLAGS' : '$CXXFLAGS',
+ 'SHCXXCOM' : '$SHCXX $SHCXXFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
'CXXFILESUFFIX' : '.cc',
'F77' : 'g77',
'F77FLAGS' : '',
'F77COM' : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77' : '$F77',
+ 'SHF77FLAGS' : '$F77FLAGS',
+ 'SHF77COM' : '$SHF77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'LINK' : 'link',
'LINKFLAGS' : '/nologo',
'LINKCOM' : '$LINK $LINKFLAGS /OUT:$TARGET $_LIBDIRFLAGS $_LIBFLAGS $SOURCES',
+ 'SHLINK' : '$LINK',
+ 'SHLINKFLAGS': '$LINKFLAGS /dll',
+ 'SHLINKCOM' : '$SHLINK $SHLINKFLAGS /OUT:$TARGET $_LIBDIRFLAGS $_LIBFLAGS $SOURCES',
'AR' : 'lib',
'ARFLAGS' : '/nologo',
'ARCOM' : '$AR $ARFLAGS /OUT:$TARGET $SOURCES',
+ 'SHLIBPREFIX': '',
+ 'SHLIBSUFFIX': '.dll',
'LEX' : 'lex',
'LEXFLAGS' : '',
'LEXCOM' : '$LEX $LEXFLAGS -t $SOURCES > $TARGET',
@@ -281,7 +420,7 @@ def make_win32_env_from_paths(include, lib, path):
'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
'PSPREFIX' : '',
'PSSUFFIX' : '.ps',
- 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Library, Object,
+ 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Win32Library, Object,
PDF, PostScript, Program],
'SCANNERS' : [CScan],
'OBJPREFIX' : '',
@@ -289,13 +428,20 @@ def make_win32_env_from_paths(include, lib, path):
'PROGPREFIX' : '',
'PROGSUFFIX' : '.exe',
'LIBPREFIX' : '',
+ 'LIBPREFIXES': '$LIBPREFIX',
'LIBSUFFIX' : '.lib',
+ 'LIBSUFFIXES': '$LIBSUFFIX',
'LIBDIRPREFIX' : '/LIBPATH:',
'LIBDIRSUFFIX' : '',
'LIBLINKPREFIX' : '',
'LIBLINKSUFFIX' : '$LIBSUFFIX',
'INCPREFIX' : '/I',
'INCSUFFIX' : '',
+ 'WIN32DEFPREFIX' : '/def:',
+ 'WIN32DEFSUFFIX' : '.def',
+ 'WIN32DLLPREFIX' : '/out:',
+ 'WIN32IMPLIBPREFIX' : '/implib:',
+ 'WIN32_INSERT_DEF' : 1,
'ENV' : {
'INCLUDE' : include,
'LIB' : lib,
@@ -316,7 +462,8 @@ def make_win32_env(version):
if os.name == 'posix':
-
+ Library = PosixLibrary
+
arcom = '$AR $ARFLAGS $TARGET $SOURCES'
ranlib = 'ranlib'
if SCons.Util.WhereIs(ranlib):
@@ -326,23 +473,41 @@ if os.name == 'posix':
'CC' : 'cc',
'CCFLAGS' : '',
'CCCOM' : '$CC $CCFLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHCC' : '$CC',
+ 'SHCCFLAGS' : '$CCFLAGS -fPIC',
+ 'SHCCCOM' : '$SHCC $SHCCFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'CFILESUFFIX' : '.c',
'CXX' : 'c++',
'CXXFLAGS' : '$CCFLAGS',
'CXXCOM' : '$CXX $CXXFLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'CXXFILESUFFIX' : '.cc',
+ 'SHCXX' : '$CXX',
+ 'SHCXXFLAGS' : '$CXXFLAGS -fPIC',
+ 'SHCXXCOM' : '$SHCXX $SHCXXFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'F77' : 'g77',
'F77FLAGS' : '',
'F77COM' : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77FLAGS' : '$F77FLAGS -fPIC',
+ 'SHF77COM' : '$F77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77PPCOM' : '$F77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77' : '$F77',
+ 'SHF77FLAGS' : '$F77FLAGS -fPIC',
+ 'SHF77COM' : '$SHF77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'LINK' : '$CXX',
'LINKFLAGS' : '',
'LINKCOM' : '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
+ 'SHLINK' : '$LINK',
+ 'SHLINKFLAGS': '$LINKFLAGS -shared',
+ 'SHLINKCOM' : '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
'AR' : 'ar',
'ARFLAGS' : 'r',
'RANLIB' : ranlib,
'RANLIBFLAGS' : '',
'ARCOM' : arcom,
+ 'SHLIBPREFIX': '$LIBPREFIX',
+ 'SHLIBSUFFIX': '.so',
'LEX' : 'lex',
'LEXFLAGS' : '',
'LEXCOM' : '$LEX $LEXFLAGS -t $SOURCES > $TARGET',
@@ -363,7 +528,7 @@ if os.name == 'posix':
'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
'PSPREFIX' : '',
'PSSUFFIX' : '.ps',
- 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Library, Object,
+ 'BUILDERS' : [Alias, CFile, CXXFile, DVI, PosixLibrary, Object,
PDF, PostScript, Program],
'SCANNERS' : [CScan],
'OBJPREFIX' : '',
@@ -371,7 +536,9 @@ if os.name == 'posix':
'PROGPREFIX' : '',
'PROGSUFFIX' : (sys.platform == 'cygwin') and '.exe' or '',
'LIBPREFIX' : 'lib',
+ 'LIBPREFIXES': '$LIBPREFIX',
'LIBSUFFIX' : '.a',
+ 'LIBSUFFIXES': [ '$LIBSUFFIX', '$SHLIBSUFFIX' ],
'LIBDIRPREFIX' : '-L',
'LIBDIRSUFFIX' : '',
'LIBLINKPREFIX' : '-l',
@@ -382,6 +549,8 @@ if os.name == 'posix':
}
elif os.name == 'nt':
+ Library = Win32Library
+
versions = None
try:
versions = get_devstudio_versions()
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 04bf26d..b0d1457 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -167,8 +167,9 @@ class Environment:
self.env = env
self.builder = builder
- def __call__(self, target = None, source = None):
- return self.builder(self.env, target, source)
+ def __call__(self, target = None, source = None, **kw):
+ return apply(self.builder, (self.env, target, source),
+ kw)
# This allows a Builder to be executed directly
# through the Environment to which it's attached.
@@ -238,6 +239,9 @@ class Environment:
def __delitem__(self, key):
del self._dict[key]
+ def has_key(self, key):
+ return self._dict.has_key(key)
+
def Command(self, target, source, action):
"""Builds the supplied target files from the supplied
source files using the supplied action. Action may
@@ -282,6 +286,11 @@ class Environment:
"""
return SCons.Util.scons_subst(string, self._dict, {})
+ def subst_list(self, string):
+ """Calls through to SCons.Util.scons_subst_list(). See
+ the documentation for that function."""
+ return SCons.Util.scons_subst_list(string, self._dict, {})
+
def get_scanner(self, skey):
"""Find the appropriate scanner given a key (usually a file suffix).
Does a linear search. Could be sped up by creating a dictionary if
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 654d432..24db460 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -296,7 +296,7 @@ class EnvironmentTestCase(unittest.TestCase):
action='buildfoo $target $source')
assert t.builder
assert t.builder.action.__class__.__name__ == 'CommandAction'
- assert t.builder.action.command == 'buildfoo $target $source'
+ assert t.builder.action.cmd_list == ['buildfoo', '$target', '$source']
assert 'foo1.in' in map(lambda x: x.path, t.sources)
assert 'foo2.in' in map(lambda x: x.path, t.sources)
@@ -328,6 +328,10 @@ class EnvironmentTestCase(unittest.TestCase):
str = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
assert str == "c c", str
+ env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
+ lst = env.subst_list([ "$AAA", "B $CCC" ])
+ assert lst == [ [ "a", "b" ], [ "c", "B a", "b" ], [ "c" ] ], lst
+
def test_autogenerate(dict):
"""Test autogenerating variables in a dictionary."""
env = Environment(LIBS = [ 'foo', 'bar', 'baz' ],
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 447ef93..fac20e3 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -40,12 +40,13 @@ cycle_detected = None
class Builder:
def execute(self, **kw):
- global built_it, built_target, built_source
- built_it = 1
+ global built_it, built_target, built_source, built_args
+ built_it = 1
built_target = kw['target']
built_source = kw['source']
+ built_args = kw
return 0
- def get_contents(self, env):
+ def get_contents(self, env, target, source):
return 7
class NoneBuilder(Builder):
@@ -160,11 +161,14 @@ class NodeTestCase(unittest.TestCase):
node.env_set(Environment())
node.path = "qqq"
node.sources = ["rrr", "sss"]
+ node.build_args = { "foo" : 1, "bar" : 2 }
node.build()
assert built_it
assert type(built_target) == type(MyNode()), type(built_target)
assert str(built_target) == "qqq", str(built_target)
assert built_source == ["rrr", "sss"], built_source
+ assert built_args["foo"] == 1, built_args
+ assert built_args["bar"] == 2, built_args
fff = MyNode()
ggg = MyNode()
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index c8d5b2c..db52b3e 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -62,6 +62,9 @@ class Node:
build, or use to build other Nodes.
"""
+ class Attrs:
+ pass
+
def __init__(self):
self.sources = [] # source files used to build node
self.depends = [] # explicit dependencies (from Depends)
@@ -80,6 +83,22 @@ class Node:
self.precious = None
self.found_includes = {}
self.includes = None
+ self.build_args = {}
+ self.attributes = self.Attrs() # Generic place to stick information about the Node.
+
+ def generate_build_args(self):
+ dict = copy.copy(self.env.Dictionary())
+ if hasattr(self, 'dir'):
+ auto = self.env.autogenerate(dir = self.dir)
+ else:
+ auto = self.env.autogenerate()
+ dict.update(auto)
+
+ dictArgs = { 'env' : dict,
+ 'target' : self,
+ 'source' : self.sources }
+ dictArgs.update(self.build_args)
+ return dictArgs
def build(self):
"""Actually build the node. Return the status from the build."""
@@ -93,15 +112,8 @@ class Node:
stat = self.builder.status
except AttributeError:
try:
- dict = copy.copy(self.env.Dictionary())
- if hasattr(self, 'dir'):
- auto = self.env.autogenerate(dir = self.dir)
- else:
- auto = self.env.autogenerate()
- dict.update(auto)
- stat = self.builder.execute(env = dict,
- target = self,
- source = self.sources)
+ stat = apply(self.builder.execute, (),
+ self.generate_build_args())
except:
raise BuildError(self, "Exception",
sys.exc_type,
@@ -158,13 +170,8 @@ class Node:
def __init__(self, node):
self.node = node
def get_contents(self):
- dict = self.node.env.Dictionary()
- dict.update(self.node.env.autogenerate())
- try:
- dir = self.node.getcwd()
- except AttributeError:
- dir = None
- return self.node.builder.get_contents(env = dict)
+ return apply(self.node.builder.get_contents, (),
+ self.node.generate_build_args())
return Adapter(self)
def get_implicit_deps(self, env, scanner, target):
diff --git a/src/engine/SCons/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py
index 76567ab..818aa86 100644
--- a/src/engine/SCons/Scanner/Prog.py
+++ b/src/engine/SCons/Scanner/Prog.py
@@ -66,14 +66,21 @@ def scan(node, env, target, fs):
libs = string.split(libs)
try:
- prefix = env.Dictionary('LIBPREFIX')
+ prefix = env.Dictionary('LIBPREFIXES')
+ if not SCons.Util.is_List(prefix):
+ prefix = [ prefix ]
except KeyError:
- prefix = ''
+ prefix = [ '' ]
try:
- suffix = env.Dictionary('LIBSUFFIX')
+ suffix = env.Dictionary('LIBSUFFIXES')
+ if not SCons.Util.is_List(suffix):
+ suffix = [ suffix ]
except KeyError:
- suffix = ''
+ suffix = [ '' ]
- libs = map(lambda x, s=suffix, p=prefix: p + x + s, libs)
- return SCons.Node.FS.find_files(libs, libpath, fs.File)
+ ret = []
+ for suf in map(env.subst, suffix):
+ for pref in map(env.subst, prefix):
+ ret.extend(map(lambda x, s=suf, p=pref: p + x + s, libs))
+ return SCons.Node.FS.find_files(ret, libpath, fs.File)
diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py
index ea3d00d..75ce697 100644
--- a/src/engine/SCons/Scanner/ProgTests.py
+++ b/src/engine/SCons/Scanner/ProgTests.py
@@ -49,7 +49,7 @@ class DummyTarget:
class DummyEnvironment:
def __init__(self, **kw):
self._dict = kw
- self._dict['LIBSUFFIX'] = '.lib'
+ self._dict['LIBSUFFIXES'] = '.lib'
def Dictionary(self, *args):
if not args:
@@ -67,6 +67,9 @@ class DummyEnvironment:
def __delitem__(self,key):
del self.Dictionary()[key]
+ def subst(self, s):
+ return s
+
def deps_match(deps, libs):
deps=map(str, deps)
deps.sort()
diff --git a/src/engine/SCons/Sig/MD5.py b/src/engine/SCons/Sig/MD5.py
index c2331bd..251c1eb 100644
--- a/src/engine/SCons/Sig/MD5.py
+++ b/src/engine/SCons/Sig/MD5.py
@@ -78,12 +78,9 @@ def collect(signatures):
def signature(obj):
"""Generate a signature for an object
"""
- try:
- contents = str(obj.get_contents())
- except AttributeError, e:
- raise AttributeError, \
- "unable to fetch contents of '%s': %s" % (str(obj), e)
- return hexdigest(md5.new(contents).digest())
+ if not hasattr(obj, 'get_contents'):
+ raise AttributeError, "unable to fetch contents of '%s'" % str(obj)
+ return hexdigest(md5.new(str(obj.get_contents())).digest())
def to_string(signature):
"""Convert a signature to a string"""
diff --git a/src/engine/SCons/Sig/MD5Tests.py b/src/engine/SCons/Sig/MD5Tests.py
index ecdb920..b5578b3 100644
--- a/src/engine/SCons/Sig/MD5Tests.py
+++ b/src/engine/SCons/Sig/MD5Tests.py
@@ -84,8 +84,7 @@ class MD5TestCase(unittest.TestCase):
try:
signature('string')
except AttributeError, e:
- # the error string should begin with "unable to fetch contents of 'string': "
- assert string.find(str(e), "unable to fetch contents of 'string':") == 0
+ assert string.find(str(e), "unable to fetch contents") == 0, str(e)
else:
raise AttributeError, "unexpected get_contents() attribute"
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 3612b9a..1f27ff8 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -153,6 +153,13 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
the argv array that should be passed to a spawn or exec
function.
+ Also, this method can accept a list of strings as input
+ to strSubst, which explicitly denotes the command line
+ arguments. This is useful if you want to pass in
+ command line arguments with spaces or newlines in them.
+ Otheriwise, if you just passed in a string, they would
+ get split along the spaces and newlines.
+
One important thing this guy does is preserve environment
variables that are lists. For instance, if you have
an environment variable that is a Python list (or UserList-
@@ -170,16 +177,23 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
if e is None:
s = ''
elif is_List(e):
- s = string.join(map(str, e), '\0')
+ s = string.join(map(to_String, e), '\0')
else:
- s = _space_sep.sub('\0', str(e))
+ s = _space_sep.sub('\0', to_String(e))
except NameError:
s = ''
return s
n = 1
- # Tokenize the original string...
- strSubst = _space_sep.sub('\0', str(strSubst))
+ if is_List(strSubst):
+ # This looks like our input is a list of strings,
+ # as explained in the docstring above. Munge
+ # it into a tokenized string by concatenating
+ # the list with nulls.
+ strSubst = string.join(strSubst, '\0')
+ else:
+ # Tokenize the original string...
+ strSubst = _space_sep.sub('\0', to_String(strSubst))
# Now, do the substitution
while n != 0:
@@ -249,6 +263,14 @@ def is_Dict(e):
def is_List(e):
return type(e) is types.ListType or isinstance(e, UserList.UserList)
+def to_String(s):
+ """Better than str() because it will preserve a unicode
+ object without converting it to ASCII."""
+ if is_String(s):
+ return s
+ else:
+ return str(s)
+
def argmunge(arg):
"""This function converts a string or list into a list of strings or Nodes.
It follows the rules outlined in the SCons design document by accepting
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index 8be22f8..fdffe4d 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -93,7 +93,9 @@ class UtilTestCase(unittest.TestCase):
assert newcom == cvt("test %s/foo/bar.exe"%os.getcwd()), newcom
newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
- assert newcom == cvt("test %s/foo/blah.cpp /bar/ack.cpp %s/foo/ack.c"%(os.getcwd(),os.path.normpath(os.getcwd()+"/.."))), newcom
+ assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(os.getcwd(),
+ os.path.abspath(os.path.normpath("/bar/ack.cpp")),
+ os.path.normpath(os.getcwd()+"/.."))), newcom
newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
assert newcom == cvt("test %s/foo/blah.cpp"%os.getcwd()), newcom
@@ -160,12 +162,24 @@ class UtilTestCase(unittest.TestCase):
assert cmd_list[1][0] == 'after', cmd_list[1][0]
assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
+ # Test inputting a list to scons_subst_list()
+ cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
+ "This is a test" ],
+ loc, {})
+ assert len(cmd_list) == 2, len(cmd_list)
+ assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
+ assert cmd_list[1][0] == cvt("after"), cmd_list[1]
+ assert cmd_list[1][4] == "This is a test", cmd_list[1]
+
glob = { 'a' : 1, 'b' : 2 }
loc = {'a' : 3, 'c' : 4 }
cmd_list = scons_subst_list("test $a $b $c $d test", glob, loc)
assert len(cmd_list) == 1, cmd_list
assert cmd_list[0] == ['test', '3', '2', '4', 'test'], cmd_list
+
+
+
def test_render_tree(self):
class Node:
def __init__(self, name, children=[]):
@@ -236,6 +250,36 @@ class UtilTestCase(unittest.TestCase):
assert not is_String({})
assert not is_String([])
+ def test_to_String(self):
+ """Test the to_String() method."""
+ assert to_String(1) == "1", to_String(1)
+ assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
+ assert to_String("foo") == "foo", to_String("foo")
+
+ try:
+ import UserString
+
+ s1=UserString.UserString('blah')
+ assert to_String(s1) is s1, s1
+ assert to_String(s1) == 'blah', s1
+
+ class Derived(UserString.UserString):
+ pass
+ s2 = Derived('foo')
+ assert to_String(s2) is s2, s2
+ assert to_String(s2) == 'foo', s2
+
+ if hasattr(types, 'UnicodeType'):
+ s3=UserString.UserString(unicode('bar'))
+ assert to_String(s3) is s3, s3
+ assert to_String(s3) == unicode('bar'), s3
+ except ImportError:
+ pass
+
+ if hasattr(types, 'UnicodeType'):
+ s4 = unicode('baz')
+ assert to_String(s4) == unicode('baz'), to_String(s4)
+
def test_WhereIs(self):
test = TestCmd.TestCmd(workdir = '')
diff --git a/test/Command.py b/test/Command.py
index 1f4a490..557b34e 100644
--- a/test/Command.py
+++ b/test/Command.py
@@ -53,8 +53,8 @@ env.Command(target = 'f1.out', source = 'f1.in',
env.Command(target = 'f2.out', source = 'f2.in',
action = r'%s' + " build.py temp2 $SOURCES\\n" + r'%s' + " build.py $TARGET temp2")
env.Command(target = 'f3.out', source = 'f3.in',
- action = [r'%s build.py temp3 $SOURCES',
- r'%s build.py $TARGET temp3'])
+ action = [ [ r'%s', 'build.py', 'temp3', '$SOURCES' ],
+ [ r'%s', 'build.py', '$TARGET', 'temp3'] ])
""" % (python, python, python, python))
test.write('f1.in', "f1.in\n")
diff --git a/test/LIBPATH.py b/test/LIBPATH.py
index 30bc421..afb87c6 100644
--- a/test/LIBPATH.py
+++ b/test/LIBPATH.py
@@ -101,9 +101,7 @@ test.run(program = prog1,
stdout = "f1.c 1\nprog.c\n")
test.run(program = prog2,
stdout = "f1.c 1\nprog.c\n")
-
-test.up_to_date(arguments = '.')
-
+#test.up_to_date(arguments = '.')
# Change LIBPATH and make sure we don't rebuild because of it.
test.write('SConstruct', """
env1 = Environment(LIBS = [ 'foo1' ],
@@ -117,7 +115,7 @@ env2.Program(target = 'prog2', source = 'prog.c')
env2.Library(target = 'foo2', source = 'f1.c')
""")
-test.up_to_date(arguments = '.', stderr = None)
+test.up_to_date(arguments = '.', stderr=None)
test.write('f1.c', r"""
void
diff --git a/test/LIBPREFIXES.py b/test/LIBPREFIXES.py
new file mode 100644
index 0000000..5d4f0cc
--- /dev/null
+++ b/test/LIBPREFIXES.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import sys
+import TestSCons
+
+if sys.platform == 'win32':
+ _lib = '.lib'
+else:
+ _lib = '.a'
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(LIBPREFIX = 'xxx-',
+ LIBPREFIXES = ['xxx-'])
+lib = env.Library(target = 'foo', source = 'foo.c')
+env.Program(target = 'prog', source = ['prog.c', lib])
+""")
+
+test.write('foo.c', r"""
+void
+foo(void)
+{
+ printf("foo.c\n");
+}
+""")
+
+test.write('prog.c', r"""
+void foo(void);
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ foo();
+ printf("prog.c\n");
+ return 0;
+}
+""")
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath('xxx-foo' + _lib)))
+
+test.run(program = test.workpath('prog'), stdout = "foo.c\nprog.c\n")
+
+test.pass_test()
diff --git a/test/LIBSUFFIXES.py b/test/LIBSUFFIXES.py
new file mode 100644
index 0000000..69c0d67
--- /dev/null
+++ b/test/LIBSUFFIXES.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import sys
+import TestSCons
+
+if sys.platform == 'win32':
+ lib_ = ''
+else:
+ lib_ = 'lib'
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(LIBSUFFIX = '.xxx',
+ LIBSUFFIXES = ['.xxx'])
+lib = env.Library(target = 'foo', source = 'foo.c')
+env.Program(target = 'prog', source = ['prog.c', lib])
+""")
+
+test.write('foo.c', r"""
+void
+foo(void)
+{
+ printf("foo.c\n");
+}
+""")
+
+test.write('prog.c', r"""
+void foo(void);
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ foo();
+ printf("prog.c\n");
+ return 0;
+}
+""")
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath(lib_ + 'foo.xxx')))
+
+test.run(program = test.workpath('prog'), stdout = "foo.c\nprog.c\n")
+
+test.pass_test()
diff --git a/test/SHCC.py b/test/SHCC.py
new file mode 100644
index 0000000..1313272
--- /dev/null
+++ b/test/SHCC.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+test.write('SConstruct', """
+foo = Environment()
+shcc = foo.Dictionary('SHCC')
+bar = Environment(SHCC = r'%s wrapper.py ' + shcc)
+foo.Program(target = 'foo', source = 'foo.c', shared = 1)
+bar.Program(target = 'bar', source = 'bar.c', shared = 1)
+""" % python)
+
+test.write('foo.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+test.write('bar.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+
+test.run(arguments = 'foo' + _exe)
+
+test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+test.run(arguments = 'bar' + _exe)
+
+test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/SHCCFLAGS.py b/test/SHCCFLAGS.py
new file mode 100644
index 0000000..fc881a3
--- /dev/null
+++ b/test/SHCCFLAGS.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import TestSCons
+
+if sys.platform == 'win32':
+ _obj = '.obj'
+ fooflags = '/nologo -DFOO'
+ barflags = '/nologo -DBAR'
+else:
+ _obj = '.o'
+ fooflags = '-DFOO'
+ barflags = '-DBAR'
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+foo = Environment(SHCCFLAGS = '%s')
+bar = Environment(SHCCFLAGS = '%s')
+foo.Object(target = 'foo%s', source = 'prog.c', shared = 1)
+bar.Object(target = 'bar%s', source = 'prog.c', shared = 1)
+foo.Program(target = 'foo', source = 'foo%s', shared = 1)
+bar.Program(target = 'bar', source = 'bar%s', shared = 1)
+""" % (fooflags, barflags, _obj, _obj, _obj, _obj))
+
+test.write('prog.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+#ifdef FOO
+ printf("prog.c: FOO\n");
+#endif
+#ifdef BAR
+ printf("prog.c: BAR\n");
+#endif
+ exit (0);
+}
+""")
+
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+
+test.write('SConstruct', """
+bar = Environment(SHCCFLAGS = '%s')
+bar.Object(target = 'foo%s', source = 'prog.c', shared = 1)
+bar.Object(target = 'bar%s', source = 'prog.c', shared = 1)
+bar.Program(target = 'foo', source = 'foo%s', shared = 1)
+bar.Program(target = 'bar', source = 'bar%s', shared = 1)
+""" % (barflags, _obj, _obj, _obj, _obj))
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+
+test.pass_test()
diff --git a/test/SHCXX.py b/test/SHCXX.py
new file mode 100644
index 0000000..0c3e2f2
--- /dev/null
+++ b/test/SHCXX.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+test.write('SConstruct', """
+foo = Environment()
+shcxx = foo.Dictionary('SHCXX')
+bar = Environment(SHCXX = r'%s wrapper.py ' + shcxx)
+foo.Program(target = 'foo', source = 'foo.cc', shared = 1)
+bar.Program(target = 'bar', source = 'bar.cc', shared = 1)
+""" % python)
+
+test.write('foo.cc', r"""
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+test.write('bar.cc', r"""
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+
+test.run(arguments = 'foo' + _exe)
+
+test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+test.run(arguments = 'bar' + _exe)
+
+test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/SHCXXFLAGS.py b/test/SHCXXFLAGS.py
new file mode 100644
index 0000000..af980fe
--- /dev/null
+++ b/test/SHCXXFLAGS.py
@@ -0,0 +1,87 @@
+
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import TestSCons
+
+if sys.platform == 'win32':
+ _obj = '.obj'
+ fooflags = '/nologo -DFOO'
+ barflags = '/nologo -DBAR'
+else:
+ _obj = '.o'
+ fooflags = '-DFOO'
+ barflags = '-DBAR'
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+foo = Environment(SHCXXFLAGS = '%s')
+bar = Environment(SHCXXFLAGS = '%s')
+foo.Object(target = 'foo%s', source = 'prog.cc', shared = 1)
+bar.Object(target = 'bar%s', source = 'prog.cc', shared = 1)
+foo.Program(target = 'foo', source = 'foo%s', shared = 1)
+bar.Program(target = 'bar', source = 'bar%s', shared = 1)
+""" % (fooflags, barflags, _obj, _obj, _obj, _obj))
+
+test.write('prog.cc', r"""
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+#ifdef FOO
+ printf("prog.c: FOO\n");
+#endif
+#ifdef BAR
+ printf("prog.c: BAR\n");
+#endif
+ exit (0);
+}
+""")
+
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+
+test.write('SConstruct', """
+bar = Environment(SHCXXFLAGS = '%s')
+bar.Object(target = 'foo%s', source = 'prog.cc', shared = 1)
+bar.Object(target = 'bar%s', source = 'prog.cc', shared = 1)
+bar.Program(target = 'foo', source = 'foo%s', shared = 1)
+bar.Program(target = 'bar', source = 'bar%s', shared = 1)
+""" % (barflags, _obj, _obj, _obj, _obj))
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+
+test.pass_test()
diff --git a/test/SHF77.py b/test/SHF77.py
new file mode 100644
index 0000000..5391170
--- /dev/null
+++ b/test/SHF77.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+
+
+if sys.platform == 'win32':
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+args = sys.argv[1:]
+while args:
+ a = args[0]
+ if a[0] != '/':
+ break
+ args.pop(0)
+ if a[:5] == '/OUT:': out = a[5:]
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+else:
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'o:')
+for opt, arg in opts:
+ if opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('myg77.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'cf:o:')
+for opt, arg in opts:
+ if opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:4] != '#g77':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(LINK = r'%s mylink.py',
+ SHF77 = r'%s myg77.py')
+env.Program(target = 'test1', source = 'test1.f', shared = 1)
+env.Program(target = 'test2', source = 'test2.for', shared = 1)
+env.Program(target = 'test3', source = 'test3.FOR', shared = 1)
+env.Program(target = 'test4', source = 'test4.F', shared = 1)
+env.Program(target = 'test5', source = 'test5.fpp', shared = 1)
+env.Program(target = 'test6', source = 'test6.FPP', shared = 1)
+""" % (python, python))
+
+test.write('test1.f', r"""This is a .f file.
+#g77
+#link
+""")
+
+test.write('test2.for', r"""This is a .for file.
+#g77
+#link
+""")
+
+test.write('test3.FOR', r"""This is a .FOR file.
+#g77
+#link
+""")
+
+test.write('test4.F', r"""This is a .F file.
+#g77
+#link
+""")
+
+test.write('test5.fpp', r"""This is a .fpp file.
+#g77
+#link
+""")
+
+test.write('test6.FPP', r"""This is a .FPP file.
+#g77
+#link
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.fail_test(test.read('test1' + _exe) != "This is a .f file.\n")
+
+test.fail_test(test.read('test2' + _exe) != "This is a .for file.\n")
+
+test.fail_test(test.read('test3' + _exe) != "This is a .FOR file.\n")
+
+test.fail_test(test.read('test4' + _exe) != "This is a .F file.\n")
+
+test.fail_test(test.read('test5' + _exe) != "This is a .fpp file.\n")
+
+test.fail_test(test.read('test6' + _exe) != "This is a .FPP file.\n")
+
+
+
+g77 = None
+for dir in string.split(os.environ['PATH'], os.pathsep):
+ g = os.path.join(dir, 'g77' + _exe)
+ if os.path.exists(g):
+ g77 = g
+ break
+
+if g77:
+
+ test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+ test.write('SConstruct', """
+foo = Environment(LIBS = 'g2c')
+shf77 = foo.Dictionary('SHF77')
+bar = foo.Copy(SHF77 = r'%s wrapper.py ' + shf77)
+foo.Program(target = 'foo', source = 'foo.f', shared = 1)
+bar.Program(target = 'bar', source = 'bar.f', shared = 1)
+""" % python)
+
+ test.write('foo.f', r"""
+ PROGRAM FOO
+ PRINT *,'foo.f'
+ STOP
+ END
+""")
+
+ test.write('bar.f', r"""
+ PROGRAM BAR
+ PRINT *,'bar.f'
+ STOP
+ END
+""")
+
+
+ test.run(arguments = 'foo' + _exe, stderr = None)
+
+ test.run(program = test.workpath('foo'), stdout = " foo.f\n")
+
+ test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+ test.run(arguments = 'bar' + _exe)
+
+ test.run(program = test.workpath('bar'), stdout = " bar.f\n")
+
+ test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/SHF77FLAGS.py b/test/SHF77FLAGS.py
new file mode 100644
index 0000000..e8b5213
--- /dev/null
+++ b/test/SHF77FLAGS.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+
+
+if sys.platform == 'win32':
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+args = sys.argv[1:]
+while args:
+ a = args[0]
+ if a[0] != '/':
+ break
+ args.pop(0)
+ if a[:5] == '/OUT:': out = a[5:]
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+else:
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'o:')
+for opt, arg in opts:
+ if opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('myg77.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'co:x')
+optstring = ''
+for opt, arg in opts:
+ if opt == '-o': out = arg
+ else: optstring = optstring + ' ' + opt
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+outfile.write(optstring + "\n")
+for l in infile.readlines():
+ if l[:4] != '#g77':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(LINK = r'%s mylink.py',
+ SHF77 = r'%s myg77.py', SHF77FLAGS = '-x')
+env.Program(target = 'test1', source = 'test1.f', shared = 1)
+env.Program(target = 'test2', source = 'test2.for', shared = 1)
+env.Program(target = 'test3', source = 'test3.FOR', shared = 1)
+env.Program(target = 'test4', source = 'test4.F', shared = 1)
+env.Program(target = 'test5', source = 'test5.fpp', shared = 1)
+env.Program(target = 'test6', source = 'test6.FPP', shared = 1)
+""" % (python, python))
+
+test.write('test1.f', r"""This is a .f file.
+#g77
+#link
+""")
+
+test.write('test2.for', r"""This is a .for file.
+#g77
+#link
+""")
+
+test.write('test3.FOR', r"""This is a .FOR file.
+#g77
+#link
+""")
+
+test.write('test4.F', r"""This is a .F file.
+#g77
+#link
+""")
+
+test.write('test5.fpp', r"""This is a .fpp file.
+#g77
+#link
+""")
+
+test.write('test6.FPP', r"""This is a .FPP file.
+#g77
+#link
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.fail_test(test.read('test1' + _exe) != " -x -c\nThis is a .f file.\n")
+
+test.fail_test(test.read('test2' + _exe) != " -x -c\nThis is a .for file.\n")
+
+test.fail_test(test.read('test3' + _exe) != " -x -c\nThis is a .FOR file.\n")
+
+test.fail_test(test.read('test4' + _exe) != " -x -c\nThis is a .F file.\n")
+
+test.fail_test(test.read('test5' + _exe) != " -x -c\nThis is a .fpp file.\n")
+
+test.fail_test(test.read('test6' + _exe) != " -x -c\nThis is a .FPP file.\n")
+
+
+
+g77 = None
+for dir in string.split(os.environ['PATH'], os.pathsep):
+ g = os.path.join(dir, 'g77' + _exe)
+ if os.path.exists(g):
+ g77 = g
+ break
+
+if g77:
+
+ test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+ test.write('SConstruct', """
+foo = Environment(LIBS = 'g2c')
+shf77 = foo.Dictionary('SHF77')
+bar = foo.Copy(SHF77 = r'%s wrapper.py ' + shf77, SHF77FLAGS = '-Ix')
+foo.Program(target = 'foo', source = 'foo.f', shared = 1)
+bar.Program(target = 'bar', source = 'bar.f', shared = 1)
+""" % python)
+
+ test.write('foo.f', r"""
+ PROGRAM FOO
+ PRINT *,'foo.f'
+ STOP
+ END
+""")
+
+ test.write('bar.f', r"""
+ PROGRAM BAR
+ PRINT *,'bar.f'
+ STOP
+ END
+""")
+
+
+ test.run(arguments = 'foo' + _exe, stderr = None)
+
+ test.run(program = test.workpath('foo'), stdout = " foo.f\n")
+
+ test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+ test.run(arguments = 'bar' + _exe)
+
+ test.run(program = test.workpath('bar'), stdout = " bar.f\n")
+
+ test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/SHLIBPREFIX.py b/test/SHLIBPREFIX.py
new file mode 100644
index 0000000..30b422e
--- /dev/null
+++ b/test/SHLIBPREFIX.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import sys
+import TestSCons
+
+if sys.platform == 'win32':
+ _lib = '.dll'
+else:
+ _lib = '.so'
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(SHLIBPREFIX = 'shlib-')
+env.Library(target = 'foo', source = 'foo.c', shared = 1)
+""")
+
+test.write('foo.c', r"""
+void
+foo(void)
+{
+ printf("foo.c\n");
+}
+""")
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath('shlib-foo' + _lib)))
+
+test.pass_test()
diff --git a/test/SHLIBSUFFIX.py b/test/SHLIBSUFFIX.py
new file mode 100644
index 0000000..e626e7c
--- /dev/null
+++ b/test/SHLIBSUFFIX.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import sys
+import TestSCons
+
+if sys.platform == 'win32':
+ lib_ = ''
+else:
+ lib_ = 'lib'
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(SHLIBSUFFIX = '.shlib')
+env.Library (target = 'foo', source = 'foo.c', shared = 1)
+""")
+
+test.write('foo.c', r"""
+void
+foo(void)
+{
+ printf("foo.c\n");
+}
+""")
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath(lib_ + 'foo.shlib')))
+
+test.pass_test()
diff --git a/test/SHLINK.py b/test/SHLINK.py
new file mode 100644
index 0000000..61060ee
--- /dev/null
+++ b/test/SHLINK.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+test.pass_test() #XXX Until someone can take a look and fix this.
+
+test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+test.write('SConstruct', """
+foo = Environment()
+shlink = foo.Dictionary('SHLINK')
+bar = Environment(SHLINK = r'%s wrapper.py ' + shlink)
+foo.Program(target = 'foo', source = 'foo.c', shared = 1)
+bar.Program(target = 'bar', source = 'bar.c', shared = 1)
+""" % python)
+
+test.write('foo.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+test.write('bar.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+
+test.run(arguments = 'foo' + _exe)
+
+test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+test.run(arguments = 'bar' + _exe)
+
+test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/SHLINKFLAGS.py b/test/SHLINKFLAGS.py
new file mode 100644
index 0000000..a885d84
--- /dev/null
+++ b/test/SHLINKFLAGS.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+test.pass_test() #XXX Until someone can take a look and fix this.
+
+test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+test.write('SConstruct', """
+foo = Environment()
+shlink = foo.Dictionary('SHLINK')
+bar = Environment(SHLINK = '', SHLINKFLAGS = r'%s wrapper.py ' + shlink)
+foo.Program(target = 'foo', source = 'foo.c', shared = 1)
+bar.Program(target = 'bar', source = 'bar.c', shared = 1)
+""" % python)
+
+test.write('foo.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+test.write('bar.c', r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+
+test.run(arguments = 'foo' + _exe)
+
+test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+test.run(arguments = 'bar' + _exe)
+
+test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/SharedLibrary.py b/test/SharedLibrary.py
new file mode 100644
index 0000000..038370b
--- /dev/null
+++ b/test/SharedLibrary.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import TestSCons
+import os
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env=Environment()
+env2 = Environment(LIBS = [ 'foo1', 'foo2', 'foo3' ],
+ LIBPATH = [ '.' ])
+env.Library(target = 'foo1', source = 'f1.c', shared=1)
+env.Library(target = 'foo2', source = 'f2a.c f2b.c f2c.c', shared=1)
+env.Library(target = 'foo3', source = ['f3a.c', 'f3b.c', 'f3c.c'], shared=1)
+env2.Program(target = 'prog', source = 'prog.c')
+""")
+
+test.write('f1.c', r"""
+#include <stdio.h>
+
+void
+f1(void)
+{
+ printf("f1.c\n");
+ fflush(stdout);
+}
+""")
+
+test.write("foo1.def", r"""
+LIBRARY "foo1"
+DESCRIPTION "Foo1 Shared Library"
+
+EXPORTS
+ f1
+""")
+
+test.write('f2a.c', r"""
+void
+f2a(void)
+{
+ printf("f2a.c\n");
+}
+""")
+
+test.write('f2b.c', r"""
+void
+f2b(void)
+{
+ printf("f2b.c\n");
+}
+""")
+
+test.write('f2c.c', r"""
+#include <stdio.h>
+
+void
+f2c(void)
+{
+ printf("f2c.c\n");
+ fflush(stdout);
+}
+""")
+
+test.write("foo2.def", r"""
+LIBRARY "foo2"
+DESCRIPTION "Foo2 Shared Library"
+
+EXPORTS
+ f2a
+ f2b
+ f2c
+""")
+
+test.write('f3a.c', r"""
+void
+f3a(void)
+{
+ printf("f3a.c\n");
+}
+""")
+
+test.write('f3b.c', r"""
+void
+f3b(void)
+{
+ printf("f3b.c\n");
+}
+""")
+
+test.write('f3c.c', r"""
+#include <stdio.h>
+
+f3c(void)
+{
+ printf("f3c.c\n");
+ fflush(stdout);
+}
+""")
+
+test.write("foo3.def", r"""
+LIBRARY "foo3"
+DESCRIPTION "Foo3 Shared Library"
+
+EXPORTS
+ f3a
+ f3b
+ f3c
+""")
+
+test.write('prog.c', r"""
+void f1(void);
+void f2a(void);
+void f2b(void);
+void f2c(void);
+void f3a(void);
+void f3b(void);
+void f3c(void);
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ f1();
+ f2a();
+ f2b();
+ f2c();
+ f3a();
+ f3b();
+ f3c();
+ printf("prog.c\n");
+ return 0;
+}
+""")
+
+test.run(arguments = '.')
+
+if os.name == 'posix':
+ os.environ['LD_LIBRARY_PATH'] = '.'
+test.run(program = test.workpath('prog'),
+ stdout = "f1.c\nf2a.c\nf2b.c\nf2c.c\nf3a.c\nf3b.c\nf3c.c\nprog.c\n")
+
+test.pass_test()
diff --git a/test/multiline.py b/test/multiline.py
index 5ec53e7..56c051b 100644
--- a/test/multiline.py
+++ b/test/multiline.py
@@ -42,8 +42,8 @@ sys.exit(0)
""")
test.write('SConstruct', """
-B1 = Builder(name = 'B1', action = [r'%s build.py .temp $SOURCES',
- r'%s build.py $TARGETS .temp'])
+B1 = Builder(name = 'B1', action = [ [ r'%s', 'build.py', '.temp', '$SOURCES' ],
+ [ r'%s', 'build.py', '$TARGETS', '.temp'] ])
B2 = Builder(name = 'B2', action = r'%s' + " build.py .temp $SOURCES\\n" + r'%s' + " build.py $TARGETS .temp")
env = Environment(BUILDERS = [B1, B2])
env.B1(target = 'foo1.out', source = 'foo1.in')