file, specifying the target or targets to be built as
command-line arguments. The command
-.RS
+.IP
+.nf
scons .
-.RE
+.PP
+.fi
will build all target files in or below the current directory
.RI ( . ")."
-.RS
+.IP
+.nf
scons /
-.RE
+.PP
+.fi
will build all target files in or below the root directory (i.e.,
all files). Specific targets may be supplied:
-.RS
+.IP
+.nf
scons foo bar
-.RE
+.PP
+.fi
Targets may be omitted from the command line,
in which case the targets specified
.B Default
targets will be built:
-.RS
+.IP
+.nf
scons
-.RE
+.PP
+.fi
Specifying "cleanup" targets in configuration files is not
necessary. The
flag removes all files
necessary to build the specified target:
.IP
+.nf
scons -c .
.PP
+.fi
to remove all target files, or:
.IP
+.nf
scons -c build export
.PP
+.fi
to remove target files under build and export.
A subset of a hierarchical tree may be built by
file lives) and specifying the subdirectory as the target to be
built:
-.RS
+.IP
+.nf
scons src/subdir
-.RE
+.PP
+.fi
.\" or changing directory and invoking scons with the
.\" .B -u
option that takes, as its argument, the number
of simultaneous tasks that may be spawned:
-.RS
+.IP
+.nf
scons -j 4
-.RE
+.PP
+.fi
builds four targets in parallel, for example.
.\" Values of variables to be passed to the configuration file(s)
.\" may be specified on the command line:
.\"
-.\" .RS
+.\" .IP
+.\" .nf
.\" scons debug=1 .
-.\" .RE
+.\" .PP
+.\" .fi
.\"
.\" These variables can be used in the configuration file(s) to modify
.\" the build in any way.
.\" option.
.\"
.\" To print the database without performing a build do:
-.\" .RS
+.\" .IP
+.\" .nf
.\" scons -p -q
-.\" .RE
+.\" .PP
+.\" .fi
.\"
.\" .IP "-q, --question"
.\" Do not run any commands, or print anything. Just return an exit
.B Environment
function:
-.RS
+.IP
+.nf
env = Environment()
-.RE
+.PP
+.fi
Build rules are specified by calling builder methods on a construction
environment. The arguments to the builder methods are target (a list of
interprets it as a space delimited list
of files. The following are examples of calling a builder:
-.RS
+.IP
+.nf
env.Program(target = 'bar', source = 'bar.c foo.c')
-.RE
-.RS
env.Program('bar', 'bar.c foo.c')
-.RE
-.RS
env.Program('bar', ['bar.c', 'foo.c'])
-.RE
+.PP
+.fi
.B scons
provides the following builders:
must have one of the following extensions: .c, .C, .cc, .cpp, .cxx, .c++, .C++.
The target object file prefix and suffix (if any) are automatically
added. Example:
-
-.RS
+.IP
+.nf
env.Object(target = 'bar', source = 'bar.c')
-.RE
+.PP
+.fi
.IP Program
Builds an executable given one or more object files or C/C++ source
files. If any C/C++ source files are given, then they will be automatically
compiled to object files. The executable prefix and suffix (if any) are
automatically added to the target. Example:
-
-.RS
+.IP
+.nf
env.Program(target = 'bar', source = 'bar.c foo.o')
-.RE
+.PP
+.fi
.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
compiled to object files. The library prefix and suffix (if any) are
automatically added to the target. Example:
-
-.RS
+.IP
+.nf
env.Library(target = 'bar', source = 'bar.c foo.o')
-.RE
+.PP
+.fi
.LP
.B Depends
method of a construction environment:
-.RS
+.IP
+.nf
env.Depends('foo.c', 'foo.h')
-.RE
+.PP
+.fi
When
.B scons
.B Default
function:
-.RS
+.IP
+.nf
Default('foo', 'bar', 'baz')
-.RE
+.PP
+.fi
A configuration file can specify other configuration files to execute using
the
.B SConscript
function:
-.RS
+.IP
+.nf
SConscript('dir/SConscript')
-.RE
+.PP
+.fi
A construction environment has an associated dictionary of construction
variables that are used by built-in or user-supplied build rules. A number
directories. The C/C++ implicit dependency scanner will search these
directories for include files. Don't explicitly put include directory
arguments in CCFLAGS or CXXFLAGS because the result will be non-portable
-and the directories will not be searched by the depedency scanner.
+and the directories will not be searched by the depedency scanner. Note:
+directory names in CPPPATH will be looked-up relative to the SConscript
+directory when they are used in a command. To force
+.B scons
+to look-up a directory relative to the root of the source tree use #:
+.IP
+.nf
+env.Environment(CPPPATH='#/include')
+.PP
+.fi
+.RS
+The directory look-up can also be forced using the
+.BR Dir ()
+function:
+.RE
+.IP
+.nf
+include = Dir('include')
+env.Environment(CPPPATH=include)
+.PP
+.fi
+.RE
.IP LINK
The linker.
.B Dictionary
method of the construction environment:
-.RS
+.IP
+.nf
dict = env.Dictionary()
-.RE
-.RS
dict["CC"] = "cc"
-.RE
+.PP
+.fi
Construction variables can also be passed to the construction environment
constructor:
-.RS
+.IP
+.nf
env = Environment(CC="cc")
-.RE
+.PP
+.fi
or when copying a construction environment using the
.B Copy
method:
-.RS
+.IP
+.nf
env2 = env.Copy(CC="cl.exe")
-.RE
+.PP
+.fi
.B scons
also provides various function not associated with a construction
.BR SConscript ().
Example:
-.RS
+.IP
+.nf
foo = SConscript('subdir/SConscript', "env")
-.RE
+.PP
+.fi
.TP
.RI Export( vars )
.BR Export ()
in a space delimited string or as seperate arguments. Example:
-.RS
+.IP
+.nf
Export("env")
-.RE
+.PP
+.fi
.TP
.RI Import( vars )
.BR Import ()
in a space delimited string or as seperate arguments. Example:
-.RS
+.IP
+.nf
Import("env")
-.RE
+.PP
+.fi
.TP
.RI Return( vars )
.BR Return ()
in a space delimited string or as seperate arguments. Example:
-.RS
+.IP
+.nf
Return("foo")
-.RE
+.PP
+.fi
.TP
.RI Default( targets )
will exit after printing out the help text.
.TP
-.RI BuildDir( build_dir ", " src_dir )
+.RI BuildDir( build_dir ", " src_dir ", [" duplicate ])
This specifies a build directory to use for all derived files.
.I build_dir
specifies the build directory to be used for all derived files that would
example.
.B scons
will link or copy (depending on the platform) all the source files into the
-build directory, so the build commands will not be modifed if
-.BR BuildDir ()
-is used.
+build directory if
+.I duplicate
+is set to 1 (the default). If
+.I duplicate
+is set to 0, then
+.B scons
+will not copy or link any source files, which may cause build problems in
+certain situations (e.g. C source files that are generated by the
+build).
+.IR duplicate =0
+is usually safe, and is always more efficient than
+.IR duplicate =1.
+.TP
+.RI Dir( name ", [" directory ])
+This returns an object that represents a given directory
+.IR name .
+.I name
+can be a relative or absolute path.
+.I directory
+is an optional directory that will be used as the parent directory.
+.TP
+.RI File( name ", [" directory ])
+This returns an object that represents a given file
+.IR name .
+.I name
+can be a relative or absolute path.
+.I directory
+is an optional directory that will be used as the parent directory.
+
.SH EXTENDING
.B scons
For example, given the construction variable CC='cc', targets=['foo'], and
sources=['foo.c', 'bar.c']:
-.RS
+.IP
+.nf
action='$CC -c -o $TARGET $SOURCES'
-.RE
+.PP
+.fi
would produce the command line:
-.RS
+.IP
+.nf
cc -c -o foo foo.c bar.c
-.RE
+.PP
+.fi
.\" XXX document how to add user defined scanners.
f = _mode_writable
else:
f = _mode_non_writable
- os.path.walk(top, _walk_chmod, f)
-
+ try:
+ os.path.walk(top, _walk_chmod, f)
+ except:
+ pass # ignore any problems changing modes
+
def write(self, file, content, mode = 'wb'):
"""Writes the specified content text (second argument) to the
specified file name (first argument). The file name may be
- Search both /usr/lib and /usr/local/lib for scons directories by
adding them both to sys.path, with whichever is in sys.prefix first.
+ From Anthony Roach:
+
+ - Add a "duplicate" keyword argument to BuildDir() that can be set
+ to prevent linking/copying source files into build directories.
+
RELEASE 0.02 - Sun, 23 Dec 2001 19:05:09 -0600
name, directory = self.__transformPath(name, directory)
return self.__doLookup(Dir, name, directory)
- def BuildDir(self, build_dir, src_dir):
+ def BuildDir(self, build_dir, src_dir, duplicate=1):
"""Link the supplied build directory to the source directory
for purposes of building files."""
self.__setTopLevelDir()
- dirSrc = self.Dir(src_dir)
- dirBuild = self.Dir(build_dir)
- if not dirSrc.is_under(self.Top) or not dirBuild.is_under(self.Top):
+ if not isinstance(src_dir, SCons.Node.Node):
+ src_dir = self.Dir(src_dir)
+ if not isinstance(build_dir, SCons.Node.Node):
+ build_dir = self.Dir(build_dir)
+ build_dir.duplicate = duplicate
+ if not src_dir.is_under(self.Top) or not build_dir.is_under(self.Top):
raise UserError, "Both source and build directories must be under top of build tree."
- if dirSrc.is_under(dirBuild):
+ if src_dir.is_under(build_dir):
raise UserError, "Source directory cannot be under build directory."
- dirBuild.link(dirSrc)
-
+ build_dir.link(src_dir, duplicate)
class Entry(SCons.Node.Node):
"""A generic class for file system entries. This class if for
self.name = name
if directory:
+ self.duplicate = directory.duplicate
self.abspath = os.path.join(directory.abspath, name)
if str(directory.path) == '.':
self.path = name
self.path = os.path.join(directory.path, name)
else:
self.abspath = self.path = name
+ self.duplicate = 1
self.path_ = self.path
self.abspath_ = self.abspath
self.dir = directory
self.use_signature = 1
- self.__doSrcpath()
+ self.__doSrcpath(self.duplicate)
- def adjust_srcpath(self):
- self.__doSrcpath()
+ def adjust_srcpath(self, duplicate):
+ self.__doSrcpath(duplicate)
- def __doSrcpath(self):
+ def __doSrcpath(self, duplicate):
+ self.duplicate = duplicate
if self.dir:
if str(self.dir.srcpath) == '.':
self.srcpath = self.name
def __str__(self):
"""A FS node's string representation is its path name."""
- return self.path
+ if self.duplicate or self.builder:
+ return self.path
+ else:
+ return self.srcpath
def __cmp__(self, other):
if type(self) != types.StringType and type(other) != types.StringType:
return hash(self.abspath_)
def exists(self):
- return os.path.exists(self.abspath)
+ return os.path.exists(str(self))
def current(self):
"""If the underlying path doesn't exist, we know the node is
self.builder = 1
self._sconsign = None
- def __doReparent(self):
+ def __doReparent(self, duplicate):
for ent in self.entries.values():
if not ent is self and not ent is self.dir:
- ent.adjust_srcpath()
+ ent.adjust_srcpath(duplicate)
- def adjust_srcpath(self):
- Entry.adjust_srcpath(self)
- self.__doReparent()
+ def adjust_srcpath(self, duplicate):
+ Entry.adjust_srcpath(self, duplicate)
+ self.__doReparent(duplicate)
- def link(self, srcdir):
+ def link(self, srcdir, duplicate):
"""Set this directory as the build directory for the
supplied source directory."""
self.srcpath = srcdir.path
- self.__doReparent()
+ self.__doReparent(duplicate)
def up(self):
return self.entries['..']
def get_timestamp(self):
if self.exists():
- return os.path.getmtime(self.path)
+ return os.path.getmtime(str(self))
else:
return 0
if self.env:
for scn in self.scanners:
if not self.scanned.has_key(scn):
- self.add_implicit(scn.scan(self.path, self.env),
- scn)
+ deps = scn.scan(str(self), self.env)
+ self.add_implicit(deps,scn)
self.scanned[scn] = 1
def exists(self):
- if not self.created:
+ if self.duplicate and not self.created:
self.created = 1
if self.srcpath != self.path and \
os.path.exists(self.srcpath):
assert f1.srcpath == os.path.normpath('src/test1'), f1.srcpath
assert f2.srcpath == os.path.normpath('src/test1'), f2.srcpath
+ fs = SCons.Node.FS.FS()
+ fs.BuildDir('build/var1', 'src', duplicate=0)
+ fs.BuildDir('build/var2', 'src')
+ f1 = fs.File('build/var1/test1')
+ f1out = fs.File('build/var1/test1.out')
+ f1out.builder = 1
+ f2 = fs.File('build/var2/test1')
+ assert f1.srcpath == os.path.normpath('src/test1'), f1.srcpath
+ assert f1out.srcpath == os.path.normpath('src/test1.out'), f1out.srcpath
+ assert str(f1) == os.path.normpath('src/test1'), str(f1)
+ assert str(f1out) == os.path.normpath('build/var1/test1.out'), str(f1out)
+ assert f2.srcpath == os.path.normpath('src/test1'), str(f2)
+ assert str(f2) == os.path.normpath('build/var2/test1'), str(f2)
+
# Test to see if file_link() works...
test=TestCmd(workdir='')
test.subdir('src','build')
# push:
stack.append(Frame(exports))
- # call:
- if script == "-":
- exec sys.stdin in stack[-1].globals
- else:
- f = SCons.Node.FS.default_fs.File(script)
- if f.exists():
- file = open(str(f), "r")
- SCons.Node.FS.default_fs.chdir(f.dir)
- exec file in stack[-1].globals
+ try:
+ # call:
+ if script == "-":
+ exec sys.stdin in stack[-1].globals
else:
- sys.stderr.write("Ignoring missing SConscript '%s'\n" % f.path)
-
-
- # pop:
- frame = stack.pop()
- SCons.Node.FS.default_fs.chdir(frame.prev_dir)
+ if not isinstance(script, SCons.Node.Node):
+ script = SCons.Node.FS.default_fs.File(script)
+ if script.exists():
+ file = open(str(script), "r")
+ SCons.Node.FS.default_fs.chdir(script.dir)
+ exec file in stack[-1].globals
+ else:
+ sys.stderr.write("Ignoring missing SConscript '%s'\n" % script.path)
+ finally:
+ # pop:
+ frame = stack.pop()
+ SCons.Node.FS.default_fs.chdir(frame.prev_dir)
return frame.retval
print "Use scons -H for help about command-line options."
sys.exit(0)
-def BuildDir(build_dir, src_dir):
- SCons.Node.FS.default_fs.BuildDir(build_dir, src_dir)
+def BuildDir(build_dir, src_dir, duplicate=1):
+ SCons.Node.FS.default_fs.BuildDir(build_dir, src_dir, duplicate)
def GetBuildPath(files):
nodes = SCons.Util.scons_str2nodes(files,
globals['Export'] = Export
globals['Import'] = Import
globals['Return'] = Return
+ globals['Dir'] = SCons.Node.FS.default_fs.Dir
+ globals['File'] = SCons.Node.FS.default_fs.File
return globals
from UserList import UserList
import SCons.Node.FS
import copy
+import SCons.Node
def scons_str2nodes(arg, node_factory=SCons.Node.FS.default_fs.File):
"""This function converts a string or list into a list of Node instances.
n = 1
# Tokenize the original string...
- strSubst = _space_sep.sub('\0', strSubst)
+ strSubst = _space_sep.sub('\0', str(strSubst))
# Now, do the substitution
while n != 0:
src = dict[self.src]
if not type(src) is types.ListType and not isinstance(src, UserList):
src = [ src ]
- return map(lambda x, d=dict: scons_subst(x, {}, d), src)
+
+ def prepare(x, dict=dict):
+ if isinstance(x, SCons.Node.Node):
+ return x
+ else:
+ return scons_subst(x, {}, dict)
+
+ return map(prepare, src)
def generate(self, dict):
if not dict.has_key(self.src):
def prepareSrc(self, dict):
src = VarInterpolator.prepareSrc(self, dict)
- return map(lambda x, fs=self.fs, d=self.dir: \
- fs.Dir(str(x), directory = d).path,
- src)
+
+ def prepare(x, self=self):
+ if not isinstance(x, SCons.Node.Node):
+ return self.fs.Dir(str(x), directory=self.dir)
+ else:
+ return x
+
+ return map(prepare, src)
def instance(self, dir, fs):
try:
test = TestCmd.TestCmd(workdir = '')
test.write('./foo', 'Some file\n')
fs = SCons.Node.FS.FS(test.workpath(""))
+ os.chdir(test.workpath("")) # FS doesn't like the cwd to be something other than it's root
node_derived = fs.File(test.workpath('./bar/baz'))
node_derived.builder_set(1) # Any non-zero value.
- paths = map(lambda x, fs=fs: fs.Dir(x), ['.', './bar'])
+ paths = map(fs.Dir, ['.', './bar'])
nodes = find_files(['foo', 'baz'], paths, fs.File)
file_names = map(str, nodes)
file_names = map(os.path.normpath, file_names)
assert dict['_LIBFLAGS'][2] == 'foobazbar', \
dict['_LIBFLAGS'][2]
- dict = {'CPPPATH' : [ 'foo', 'bar', 'baz', '$FOO/bar' ],
+ blat = SCons.Node.FS.default_fs.File('blat')
+ dict = {'CPPPATH' : [ 'foo', 'bar', 'baz', '$FOO/bar', blat],
'INCPREFIX' : 'foo',
'INCSUFFIX' : 'bar',
'FOO' : 'baz' }
autogenerate(dict, dir = SCons.Node.FS.default_fs.Dir('/xx'))
- assert len(dict['_INCFLAGS']) == 4, dict['_INCFLAGS']
+ assert len(dict['_INCFLAGS']) == 5, dict['_INCFLAGS']
assert dict['_INCFLAGS'][0] == os.path.normpath('foo/xx/foobar'), \
dict['_INCFLAGS'][0]
assert dict['_INCFLAGS'][1] == os.path.normpath('foo/xx/barbar'), \
assert dict['_INCFLAGS'][3] == os.path.normpath('foo/xx/baz/barbar'), \
dict['_INCFLAGS'][3]
+ assert dict['_INCFLAGS'][4] == os.path.normpath('fooblatbar'), \
+ dict['_INCFLAGS'][4]
if __name__ == "__main__":
suite = unittest.makeSuite(UtilTestCase, 'test_')
test = TestSCons.TestSCons()
-foo1 = test.workpath('build/var1/foo1' + _exe)
-foo2 = test.workpath('build/var1/foo2' + _exe)
-foo3 = test.workpath('build/var2/foo1' + _exe)
-foo4 = test.workpath('build/var2/foo2' + _exe)
+foo11 = test.workpath('build', 'var1', 'foo1' + _exe)
+foo12 = test.workpath('build', 'var1', 'foo2' + _exe)
+foo21 = test.workpath('build', 'var2', 'foo1' + _exe)
+foo22 = test.workpath('build', 'var2', 'foo2' + _exe)
+foo31 = test.workpath('build', 'var3', 'foo1' + _exe)
+foo32 = test.workpath('build', 'var3', 'foo2' + _exe)
test.write('SConstruct', """
-BuildDir('build/var1', 'src')
-BuildDir('build/var2', 'src')
-SConscript('build/var1/SConscript')
-SConscript('build/var2/SConscript')
-""")
+src = Dir('src')
+var2 = Dir('build/var2')
+var3 = Dir('build/var3')
+
+BuildDir('build/var1', src)
+BuildDir(var2, src)
+BuildDir(var3, src, duplicate=0)
+
+env = Environment()
+SConscript('build/var1/SConscript', "env")
+SConscript('build/var2/SConscript', "env")
+
+env = Environment(CPPPATH=src)
+SConscript('build/var3/SConscript', "env")
+""")
test.subdir('src')
test.write('src/SConscript', """
f2.close()
f1.close()
return 0
-
-env = Environment()
+Import("env")
env.Command(target='f2.c', source='f2.in', action=buildIt)
env.Program(target='foo2', source='f2.c')
env.Program(target='foo1', source='f1.c')
test.run(arguments = '.')
-test.run(program = foo1, stdout = "f1.c\n")
-test.run(program = foo2, stdout = "f2.c\n")
-test.run(program = foo3, stdout = "f1.c\n")
-test.run(program = foo4, stdout = "f2.c\n")
+test.run(program = foo11, stdout = "f1.c\n")
+test.run(program = foo12, stdout = "f2.c\n")
+test.run(program = foo21, stdout = "f1.c\n")
+test.run(program = foo22, stdout = "f2.c\n")
+test.run(program = foo31, stdout = "f1.c\n")
+test.run(program = foo32, stdout = "f2.c\n")
+
+# Make sure we didn't duplicate the source files in build/var3.
+test.fail_test(os.path.exists(test.workpath('build', 'var3', 'f1.c')))
+test.fail_test(os.path.exists(test.workpath('build', 'var3', 'f2.in')))
test.pass_test()
prog = 'prog' + _exe
subdir_prog = os.path.join('subdir', 'prog' + _exe)
+variant_prog = os.path.join('variant', 'prog' + _exe)
-args = prog + ' ' + subdir_prog
+args = prog + ' ' + subdir_prog + ' ' + variant_prog
test = TestSCons.TestSCons()
obj = env.Object(target='prog', source='subdir/prog.c')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
+
+BuildDir('variant', 'subdir', 0)
+include = Dir('include')
+env = Environment(CPPPATH=[include])
+SConscript('variant/SConscript', "env")
""")
test.write(['subdir', 'SConscript'], """
-test.run(arguments = args, stderr = None)
+test.run(arguments = args)
test.run(program = test.workpath(prog),
stdout = "subdir/prog.c\ninclude/foo.h 1\ninclude/bar.h 1\n")
test.run(program = test.workpath(subdir_prog),
stdout = "subdir/prog.c\nsubdir/include/foo.h 1\nsubdir/include/bar.h 1\n")
-test.up_to_date(arguments = args)
+test.run(program = test.workpath(variant_prog),
+ stdout = "subdir/prog.c\ninclude/foo.h 1\ninclude/bar.h 1\n")
+# Make sure we didn't duplicate the source file in the variant subdirectory.
+test.fail_test(os.path.exists(test.workpath('variant', 'prog.c')))
+test.up_to_date(arguments = args)
test.unlink('include/foo.h')
test.write('include/foo.h',
test.run(program = test.workpath(subdir_prog),
stdout = "subdir/prog.c\nsubdir/include/foo.h 1\nsubdir/include/bar.h 1\n")
-test.up_to_date(arguments = args)
+test.run(program = test.workpath(variant_prog),
+ stdout = "subdir/prog.c\ninclude/foo.h 2\ninclude/bar.h 1\n")
+# Make sure we didn't duplicate the source file in the variant subdirectory.
+test.fail_test(os.path.exists(test.workpath('variant', 'prog.c')))
+
+test.up_to_date(arguments = args)
test.unlink('include/bar.h')
#define BAR_STRING "include/bar.h 2\n"
""")
-test.run(arguments = prog)
+test.run(arguments = args)
test.run(program = test.workpath(prog),
stdout = "subdir/prog.c\ninclude/foo.h 2\ninclude/bar.h 2\n")
test.run(program = test.workpath(subdir_prog),
stdout = "subdir/prog.c\nsubdir/include/foo.h 1\nsubdir/include/bar.h 1\n")
-test.up_to_date(arguments = prog)
+test.run(program = test.workpath(variant_prog),
+ stdout = "subdir/prog.c\ninclude/foo.h 2\ninclude/bar.h 2\n")
+
+# Make sure we didn't duplicate the source file in the variant subdirectory.
+test.fail_test(os.path.exists(test.workpath('variant', 'prog.c')))
+
+test.up_to_date(arguments = args)
test.pass_test()
assert x1 == "SConscript4 x1"
assert x2 == "SConscript4 x2"
+subdir = Dir('subdir')
+script = File('SConscript', subdir)
+foo = SConscript(script)
+assert foo == "subdir/SConscript foo"
""")
test.write('SConscript', """
Export("x1", "x2")
""")
+test.subdir('subdir')
+test.write(['subdir', 'SConscript'], """
+foo = 'subdir/SConscript foo'
+Return('foo')
+""")
wpath = test.workpath()