summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/man/scons.1127
-rw-r--r--src/engine/MANIFEST.in1
-rw-r--r--src/engine/SCons/SConscript.py177
-rw-r--r--src/engine/SCons/SConscriptTests.py28
-rw-r--r--src/engine/SCons/Script.py92
-rw-r--r--test/CPPPATH.py3
-rw-r--r--test/Depends.py3
-rw-r--r--test/SConscript.py91
-rw-r--r--test/actions.py2
-rw-r--r--test/errors.py12
10 files changed, 453 insertions, 83 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 51ad838..5b7b985 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -680,6 +680,126 @@ method:
env2 = env.Copy(CC="cl.exe")
.RE
+.B scons
+also provides various function not associated with a construction
+environment that configuration files can use to affect the build:
+
+.TP
+.RI SConscript( script ", [" exports ])
+This tells
+.B scons
+to execute
+.I script
+as a configuration file. The optional
+.I exports
+argument provides a list of variable names to export to
+.IR script ". " exports
+can also be a space delimited string of variables names.
+.I script
+must use the
+.BR Import ()
+function to import the variables. Any variables returned by
+.I script
+using
+.BR Return ()
+will be returned by the call to
+.BR SConscript ().
+Example:
+
+.RS
+foo = SConscript('subdir/SConscript', "env")
+.RE
+
+.TP
+.RI Export( vars )
+This tells
+.B scons
+to export a list of variables from the current
+configuration file to all other configuration files. The exported variables
+are kept in a global collection, so subsequent exports
+will over-write previous exports that have the same name.
+Multiple variable names can be passed to
+.BR Export ()
+in a space delimited string or as seperate arguments. Example:
+
+.RS
+Export("env")
+.RE
+
+.TP
+.RI Import( vars )
+This tells
+.B scons
+to import a list of variables into the current configuration file. This
+will import variables that were exported with
+.BR Export ()
+or in the
+.I exports
+argument to
+.BR SConscript ().
+Variables exported by
+.BR SConscript ()
+have precedence. Multiple variable names can be passed to
+.BR Import ()
+in a space delimited string or as seperate arguments. Example:
+
+.RS
+Import("env")
+.RE
+
+.TP
+.RI Return( vars )
+This tells
+.B scons
+what variable(s) to use as the return value(s) of the current configuration
+file. These variables will be returned to the "calling" configuration file
+as the return value(s) of
+.BR SConscript ().
+Multiple variable names can be passed to
+.BR Return ()
+in a space delimited string or as seperate arguments. Example:
+
+.RS
+Return("foo")
+.RE
+
+.TP
+.RI Default( targets )
+This specifies a list of default targets. Default targets will be built by
+.B scons
+if no explicit targets are given on the comamnd line. Multiple targets can
+be specified either as a space delimited string of target file names or as
+seperate arguments.
+.BR Default ()
+will also accept the return value of any of the ccnstruction environment
+builder methods.
+
+.TP
+.RI Help( text )
+This specifies help text to be printed if the
+.B -h
+argument is given to
+.BR scons .
+This function can be called multiple times to print multiple help messages.
+
+.TP
+.RI BuildDir( build_dir ", " src_dir )
+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
+normally be built under
+.IR src_dir .
+Multiple build directories can be set up for multiple build variants, for
+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.
+
+
+
+
.SH EXTENDING
.B scons
can be extended by adding new builders to a construction
@@ -767,6 +887,9 @@ Design Document,
.B scons
source code.
-.SH AUTHOR
-Steven Knight <knight@baldmt.com>, et. al.
+.SH AUTHORS
+Steven Knight <knight@baldmt.com>
+.RS
+.RE
+Anthony Roach <aroach@electriceyeball.com>
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index c22aebc..ad38b18 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -10,6 +10,7 @@ SCons/Node/FS.py
SCons/Scanner/__init__.py
SCons/Scanner/C.py
SCons/Scanner/Prog.py
+SCons/SConscript.py
SCons/Script.py
SCons/Sig/__init__.py
SCons/Sig/MD5.py
diff --git a/src/engine/SCons/SConscript.py b/src/engine/SCons/SConscript.py
new file mode 100644
index 0000000..1479c7a
--- /dev/null
+++ b/src/engine/SCons/SConscript.py
@@ -0,0 +1,177 @@
+"""engine.SCons.SConscript
+
+This module defines the Python API provided to SConscript and SConstruct
+files.
+
+"""
+
+#
+# Copyright (c) 2001 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__ = "src/engine/SCons/SConscript.py __REVISION__ __DATE__ __DEVELOPER__"
+
+import SCons.Errors
+import SCons.Builder
+import SCons.Defaults
+import SCons.Node
+import SCons.Node.FS
+import SCons.Environment
+import string
+import sys
+
+default_targets = []
+help_option = None
+
+# global exports set by Export():
+global_exports = {}
+
+class Frame:
+ """A frame on the SConstruct/SConscript call stack"""
+ def __init__(self, exports):
+ self.globals = BuildDefaultGlobals()
+ self.retval = None
+ self.prev_dir = SCons.Node.FS.default_fs.getcwd()
+ self.exports = {} # exports from the calling SConscript
+
+ try:
+ if type(exports) == type([]):
+ for export in exports:
+ self.exports[export] = stack[-1].globals[export]
+ else:
+ for export in string.split(exports):
+ self.exports[export] = stack[-1].globals[export]
+ except KeyError, x:
+ raise SCons.Errors.UserError, "Export of non-existant variable '%s'"%x
+
+
+# the SConstruct/SConscript call stack:
+stack = []
+
+# For documentation on the methods in this file, see the scons man-page
+
+def Return(*vars):
+ retval = []
+ try:
+ for var in vars:
+ for v in string.split(var):
+ retval.append(stack[-1].globals[v])
+ except KeyError, x:
+ raise SCons.Errors.UserError, "Return of non-existant variable '%s'"%x
+
+ if len(retval) == 1:
+ stack[-1].retval = retval[0]
+ else:
+ stack[-1].retval = tuple(retval)
+
+def SConscript(script, exports=[]):
+ retval = ()
+
+ # 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
+ else:
+ sys.stderr.write("Ignoring missing SConscript '%s'\n" % f.path)
+
+
+ # pop:
+ frame = stack.pop()
+ SCons.Node.FS.default_fs.chdir(frame.prev_dir)
+
+ return frame.retval
+
+def Default(*targets):
+ for t in targets:
+ if isinstance(t, SCons.Node.Node):
+ default_targets.append(t)
+ else:
+ for s in string.split(t):
+ default_targets.append(s)
+
+def Help(text):
+ global help_option
+ if help_option == 'h':
+ print text
+ 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 GetBuildPath(files):
+ nodes = SCons.Util.scons_str2nodes(files,
+ SCons.Node.FS.default_fs.Entry)
+ ret = map(str, nodes)
+ if len(ret) == 1:
+ return ret[0]
+ return ret
+
+def Export(*vars):
+ try:
+ for var in vars:
+ for v in string.split(var):
+ global_exports[v] = stack[-1].globals[v]
+ except KeyError, x:
+ raise SCons.Errors.UserError, "Export of non-existant variable '%s'"%x
+
+def Import(*vars):
+ try:
+ for var in vars:
+ for v in string.split(var):
+ if stack[-1].exports.has_key(v):
+ stack[-1].globals[v] = stack[-1].exports[v]
+ else:
+ stack[-1].globals[v] = global_exports[v]
+ except KeyError,x:
+ raise SCons.Errors.UserError, "Import of non-existant variable '%s'"%x
+
+def BuildDefaultGlobals():
+ """
+ Create a dictionary containing all the default globals for
+ SConscruct and SConscript files.
+ """
+
+ globals = {}
+ globals['Builder'] = SCons.Builder.Builder
+ globals['Environment'] = SCons.Environment.Environment
+ globals['Object'] = SCons.Defaults.Object
+ globals['Program'] = SCons.Defaults.Program
+ globals['Library'] = SCons.Defaults.Library
+ globals['CScan'] = SCons.Defaults.CScan
+ globals['SConscript'] = SConscript
+ globals['Default'] = Default
+ globals['Help'] = Help
+ globals['BuildDir'] = BuildDir
+ globals['GetBuildPath'] = GetBuildPath
+ globals['Export'] = Export
+ globals['Import'] = Import
+ globals['Return'] = Return
+ return globals
diff --git a/src/engine/SCons/SConscriptTests.py b/src/engine/SCons/SConscriptTests.py
new file mode 100644
index 0000000..d44af5c
--- /dev/null
+++ b/src/engine/SCons/SConscriptTests.py
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2001 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__ = "src/engine/SCons/SConscriptTests.py __REVISION__ __DATE__ __DEVELOPER__"
+
+import SCons.SConscript
+
+# all of the SConscript.py tests are in test/SConscript.py
diff --git a/src/engine/SCons/Script.py b/src/engine/SCons/Script.py
index 1298a9e..e35e03d 100644
--- a/src/engine/SCons/Script.py
+++ b/src/engine/SCons/Script.py
@@ -58,15 +58,8 @@ from SCons.Errors import *
import SCons.Sig
import SCons.Sig.MD5
from SCons.Taskmaster import Taskmaster
-import SCons.Util
-
-#
-# Modules and classes that we don't use directly in this script, but
-# which we want available for use in SConstruct and SConscript files.
-#
-from SCons.Environment import Environment
-from SCons.Builder import Builder
-from SCons.Defaults import *
+import SCons.Builder
+import SCons.SConscript
#
@@ -104,9 +97,7 @@ class CleanTask(SCons.Taskmaster.Task):
# Global variables
-default_targets = []
include_dirs = []
-help_option = None
num_jobs = 1
scripts = []
task_class = BuildTask # default action is to build targets
@@ -115,6 +106,7 @@ calc = None
ignore_errors = 0
keep_going_on_error = 0
+
# utility functions
def _scons_syntax_error(e):
@@ -161,40 +153,6 @@ def _scons_other_errors():
-def SConscript(sconscript, export={}):
- global scripts
- scripts.append( (SCons.Node.FS.default_fs.File(sconscript), export) )
-
-def Default(*targets):
- for t in targets:
- if isinstance(t, SCons.Node.Node):
- default_targets.append(t)
- else:
- for s in string.split(t):
- default_targets.append(s)
-
-def Help(text):
- global help_option
- if help_option == 'h':
- print text
- 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 GetBuildPath(files):
- nodes = SCons.Util.scons_str2nodes(files,
- SCons.Node.FS.default_fs.Entry)
- ret = map(str, nodes)
- if len(ret) == 1:
- return ret[0]
- return ret
-
-def Export(**kw):
- # A convenient shorthand to pass exports to the SConscript function.
- return kw
-
#
# After options are initialized, the following variables are
# filled in:
@@ -392,26 +350,21 @@ def options_init():
def opt_f(opt, arg):
global scripts
- if arg == '-':
- scripts.append( ( arg, {} ) )
- else:
- scripts.append( (SCons.Node.FS.default_fs.File(arg), {}) )
+ scripts.append(arg)
Option(func = opt_f,
short = 'f', long = ['file', 'makefile', 'sconstruct'], arg = 'FILE',
help = "Read FILE as the top-level SConstruct file.")
def opt_help(opt, arg):
- global help_option
- help_option = 'h'
+ SCons.SConscript.help_option = 'h'
Option(func = opt_help,
short = 'h', long = ['help'],
help = "Print defined help message, or this one.")
def opt_help_options(opt, arg):
- global help_option
- help_option = 'H'
+ SCons.SConscript.help_option = 'H'
Option(func = opt_help_options,
short = 'H', long = ['help-options'],
@@ -584,7 +537,7 @@ def UsageString():
def _main():
- global scripts, help_option, num_jobs, task_class, calc
+ global scripts, num_jobs, task_class, calc
targets = []
@@ -618,18 +571,18 @@ def _main():
if not scripts:
for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
if os.path.isfile(file):
- scripts.append( (SCons.Node.FS.default_fs.File(file), {}) )
+ scripts.append(file)
break
- if help_option == 'H':
+ if SCons.SConscript.help_option == 'H':
print UsageString()
sys.exit(0)
if not scripts:
- if help_option == 'h':
- # There's no SConstruct, but they specified either -h or
- # -H. Give them the options usage now, before we fail
- # trying to read a non-existent SConstruct file.
+ if SCons.SConscript.help_option == 'h':
+ # There's no SConstruct, but they specified -h.
+ # Give them the options usage now, before we fail
+ # trying to read a non-existent SConstruct file.
print UsageString()
sys.exit(0)
else:
@@ -637,30 +590,19 @@ def _main():
sys.path = include_dirs + sys.path
- while scripts:
- f, exports = scripts.pop(0)
- script_env = copy.copy(globals())
- script_env.update(exports)
- if f == "-":
- exec sys.stdin in script_env
- else:
- if f.exists():
- file = open(str(f), "r")
- SCons.Node.FS.default_fs.chdir(f.dir)
- exec file in script_env
- else:
- sys.stderr.write("Ignoring missing SConscript '%s'\n" % f.path)
+ for script in scripts:
+ SCons.SConscript.SConscript(script)
SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Top)
- if help_option == 'h':
+ if SCons.SConscript.help_option == 'h':
# They specified -h, but there was no Help() inside the
# SConscript files. Give them the options usage.
print UsageString()
sys.exit(0)
if not targets:
- targets = default_targets
+ targets = SCons.SConscript.default_targets
def Entry(x):
if isinstance(x, SCons.Node.Node):
diff --git a/test/CPPPATH.py b/test/CPPPATH.py
index aa75019..fb21426 100644
--- a/test/CPPPATH.py
+++ b/test/CPPPATH.py
@@ -46,10 +46,11 @@ test.write('SConstruct', """
env = Environment(CPPPATH = ['include'])
obj = env.Object(target='prog', source='subdir/prog.c')
env.Program(target='prog', source=obj)
-SConscript('subdir/SConscript', Export(env=env))
+SConscript('subdir/SConscript', "env")
""")
test.write(['subdir', 'SConscript'], """
+Import("env")
env.Program(target='prog', source='prog.c')
""")
diff --git a/test/Depends.py b/test/Depends.py
index ab045ae..1ccf7f4 100644
--- a/test/Depends.py
+++ b/test/Depends.py
@@ -52,10 +52,11 @@ env.Depends(target = 'f3.out', dependency = 'subdir/bar.dep')
env.Foo(target = 'f1.out', source = 'f1.in')
env.Foo(target = 'f2.out', source = 'f2.in')
env.Bar(target = 'f3.out', source = 'f3.in')
-SConscript('subdir/SConscript', Export(env=env))
+SConscript('subdir/SConscript', "env")
""" % (python, python))
test.write(['subdir', 'SConscript'], """
+Import("env")
env.Depends(target = 'f4.out', dependency = 'bar.dep')
env.Bar(target = 'f4.out', source = 'f4.in')
""")
diff --git a/test/SConscript.py b/test/SConscript.py
index b463f7e..5172040 100644
--- a/test/SConscript.py
+++ b/test/SConscript.py
@@ -30,16 +30,105 @@ test = TestSCons.TestSCons()
test.write('SConstruct', """
import os
+
print "SConstruct", os.getcwd()
SConscript('SConscript')
+
+x1 = "SConstruct x1"
+x2 = "SConstruct x2"
+x3,x4 = SConscript('SConscript1', "x1 x2")
+assert x3 == "SConscript1 x3"
+assert x4 == "SConscript1 x4"
+
+
+(x3,x4) = SConscript('SConscript2', ["x1","x2"])
+assert x3 == "SConscript2 x3"
+assert x4 == "SConscript2 x4"
+
+Export("x1 x2")
+SConscript('SConscript3')
+Import("x1 x2")
+assert x1 == "SConscript3 x1"
+assert x2 == "SConscript3 x2"
+
+x1 = "SConstruct x1"
+x2 = "SConstruct x2"
+Export("x1","x2")
+SConscript('SConscript4')
+Import("x1"," x2")
+assert x1 == "SConscript4 x1"
+assert x2 == "SConscript4 x2"
+
""")
-# XXX I THINK THEY SHOULD HAVE TO RE-IMPORT OS HERE
test.write('SConscript', """
+
+# os should not be automajically imported:
+assert not globals().has_key("os")
+
import os
print "SConscript " + os.getcwd()
""")
+test.write('SConscript1', """
+Import("x1 x2")
+assert x1 == "SConstruct x1"
+assert x2 == "SConstruct x2"
+
+x3 = "SConscript1 x3"
+x4 = "SConscript1 x4"
+Return("x3 x4")
+""")
+
+
+
+test.write('SConscript2', """
+Import("x1","x2")
+assert x1 == "SConstruct x1"
+assert x2 == "SConstruct x2"
+x3 = "SConscript2 x3"
+x4 = "SConscript2 x4"
+Return("x3","x4")
+""")
+
+test.write('SConscript3', """
+Import("x1 x2")
+assert x1 == "SConstruct x1"
+assert x2 == "SConstruct x2"
+x1 = "SConscript3 x1"
+x2 = "SConscript3 x2"
+
+x5 = SConscript('SConscript31', "x1")
+Import("x6")
+assert x5 == "SConscript31 x5"
+assert x6 == "SConscript31 x6"
+
+Export("x1 x2")
+
+
+""")
+
+test.write('SConscript31', """
+Import("x1 x2")
+assert x1 == "SConscript3 x1"
+assert x2 == "SConstruct x2"
+x5 = "SConscript31 x5"
+x6 = "SConscript31 x6"
+Export("x6")
+Return("x5")
+""")
+
+
+test.write('SConscript4', """
+Import("x1", "x2")
+assert x1 == "SConstruct x1"
+assert x2 == "SConstruct x2"
+x1 = "SConscript4 x1"
+x2 = "SConscript4 x2"
+Export("x1", "x2")
+""")
+
+
wpath = test.workpath()
test.run(stdout = "SConstruct %s\nSConscript %s\n" % (wpath, wpath))
diff --git a/test/actions.py b/test/actions.py
index 9ba689b..fca14bd 100644
--- a/test/actions.py
+++ b/test/actions.py
@@ -86,6 +86,8 @@ test.up_to_date(arguments = '.')
test.write('SConstruct', """
import os
+assert not globals().has_key('string')
+import string
class bld:
def __init__(self):
self.cmd = r'%s build.py %%s 4 %%s'
diff --git a/test/errors.py b/test/errors.py
index 5edd8f9..cb2248d 100644
--- a/test/errors.py
+++ b/test/errors.py
@@ -47,17 +47,21 @@ SyntaxError: invalid syntax
test.write('SConstruct2', """
-raise UserError, 'Depends() require both sources and targets.'
+assert not globals().has_key("UserError")
+import SCons.Errors
+raise SCons.Errors.UserError, 'Depends() require both sources and targets.'
""")
test.run(arguments='-f SConstruct2',
stdout = "",
stderr = """
SCons error: Depends\(\) require both sources and targets.
-File "SConstruct2", line 2, in \?
+File "SConstruct2", line 4, in \?
""")
test.write('SConstruct3', """
+assert not globals().has_key("InternalError")
+from SCons.Errors import InternalError
raise InternalError, 'error inside'
""")
@@ -67,7 +71,9 @@ test.run(arguments='-f SConstruct3',
File ".*Script.py", line \d+, in main
_main\(\)
File ".*Script.py", line \d+, in _main
- exec file in script_env
+ SCons.SConscript.SConscript\(script\)
+ File ".*SConscript.py", line \d+, in SConscript
+ exec file in stack\[-1\].globals
File "SConstruct3", line \d+, in \?
raise InternalError, 'error inside'
InternalError: error inside