summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/files1
-rw-r--r--doc/man/scons.152
-rw-r--r--src/CHANGES.txt2
-rw-r--r--src/engine/MANIFEST.in1
-rw-r--r--src/engine/SCons/Builder.py27
-rw-r--r--src/engine/SCons/BuilderTests.py66
-rw-r--r--src/engine/SCons/Tool/__init__.py5
-rw-r--r--src/engine/SCons/Tool/swig.py65
-rw-r--r--test/SWIG.py177
-rw-r--r--test/import.py1
10 files changed, 372 insertions, 25 deletions
diff --git a/bin/files b/bin/files
index baeeb14..a2facbc 100644
--- a/bin/files
+++ b/bin/files
@@ -78,6 +78,7 @@
./SCons/Tool/sunar.py
./SCons/Tool/suncc.py
./SCons/Tool/sunlink.py
+./SCons/Tool/swig.py
./SCons/Tool/tar.py
./SCons/Tool/tex.py
./SCons/Tool/yacc.py
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index b9849c6..aaf6c0f 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -931,6 +931,7 @@ sgilink
sunar
suncc
sunlink
+swig
tar
tex
yacc
@@ -3498,6 +3499,57 @@ in which the command should be executed.
'\".IP SVNFLAGS
'\"General options that are passed to Subversion.
+.IP SWIG
+The scripting language wrapper and interface generator.
+
+.IP SWIGCFILESUFFIX
+The suffix that will be used for intermediate C
+source files generated by
+the scripting language wrapper and interface generator.
+The default value is
+.BR _wrap$CFILESUFFIX .
+By default, this value is used whenever the
+.B -c++
+option is
+.I not
+specified as part of the
+.B SWIGFLAGS
+construction variable.
+
+.IP SWIGCOM
+The command line used to call
+the scripting language wrapper and interface generator.
+
+.IP SWIGCXXFILESUFFIX
+The suffix that will be used for intermediate C++
+source files generated by
+the scripting language wrapper and interface generator.
+The default value is
+.BR _wrap$CFILESUFFIX .
+By default, this value is used whenever the
+.B -c++
+option is specified as part of the
+.B SWIGFLAGS
+construction variable.
+
+.IP SWIGFLAGS
+General options passed to
+the scripting language wrapper and interface generator.
+This is where you should set
+.BR -python ,
+.BR -perl5 ,
+.BR -tcl ,
+or whatever other options you want to specify to SWIG.
+If you set the
+.B -c++
+option in this variable,
+.B scons
+will, by default,
+generate a C++ intermediate source file
+with the extension that is specified as the
+.B $CXXFILESUFFIX
+variable.
+
.IP TAR
The tar archiver.
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index de2d75c..795b29d 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -92,6 +92,8 @@ RELEASE 0.XX - XXX
source Node, not by trying to create the right string to pass to
arg2nodes().
+ - Add support for SWIG.
+
From Gary Oberbrunner:
- Report the target being built in error messages when building
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 3cfcff8..b97cd29 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -95,6 +95,7 @@ SCons/Tool/sunar.py
SCons/Tool/suncc.py
SCons/Tool/sunlink.py
SCons/Tool/Subversion.py
+SCons/Tool/swig.py
SCons/Tool/tar.py
SCons/Tool/tex.py
SCons/Tool/yacc.py
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 34f28bc..f7630e6 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -122,8 +122,9 @@ class Selector(UserDict.UserDict):
# emitter_dict before giving up.
s_dict = {}
for (k,v) in self.items():
- s_k = env.subst(k)
- s_dict[s_k] = v
+ if not k is None:
+ s_k = env.subst(k)
+ s_dict[s_k] = v
try:
return s_dict[ext]
except KeyError:
@@ -132,6 +133,15 @@ class Selector(UserDict.UserDict):
except KeyError:
return None
+class CallableSelector(Selector):
+ """A callable dictionary that wills, in turn, call the value it
+ finds if it can."""
+ def __call__(self, env, source):
+ value = Selector.__call__(self, env, source)
+ if callable(value):
+ value = value(env, source)
+ return value
+
class DictEmitter(Selector):
"""A callable dictionary that maps file suffixes to emitters.
When called, it finds the right emitter in its dictionary for the
@@ -198,7 +208,7 @@ def _init_nodes(builder, env, overrides, tlist, slist):
raise UserError, "Two different environments were specified for the same target: %s"%str(t)
elif t.overrides != overrides:
raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
- elif builder.scanner and builder.scanner != t.target_scanner:
+ elif builder.scanner and t.target_scanner and builder.scanner != t.target_scanner:
raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
if builder.multi:
@@ -293,10 +303,10 @@ class BuilderBase:
self.action = SCons.Action.Action(action)
self.multi = multi
if SCons.Util.is_Dict(prefix):
- prefix = Selector(prefix)
+ prefix = CallableSelector(prefix)
self.prefix = prefix
if SCons.Util.is_Dict(suffix):
- suffix = Selector(suffix)
+ suffix = CallableSelector(suffix)
self.suffix = suffix
self.env = env
self.overrides = overrides
@@ -419,7 +429,7 @@ class BuilderBase:
return tlist
def adjust_suffix(self, suff):
- if suff and not suff[0] in [ '.', '$' ]:
+ if suff and not suff[0] in [ '.', '_', '$' ]:
return '.' + suff
return suff
@@ -553,10 +563,9 @@ class MultiStepBuilder(BuilderBase):
src_suffixes = self.src_suffixes(env)
for snode in slist:
- path, ext = self.splitext(snode.get_abspath())
+ base, ext = self.splitext(str(snode))
if sdict.has_key(ext):
- src_bld = sdict[ext]
- tgt = apply(src_bld, (env, path, snode), kw)
+ tgt = apply(sdict[ext], (env, None, snode), kw)
# Only supply the builder with sources it is capable
# of building.
if SCons.Util.is_List(tgt):
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 8aafefa..f65230c 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -370,17 +370,26 @@ class BuilderTestCase(unittest.TestCase):
my_env['FOO'] = 'abracadabra'
assert builder.get_prefix(my_env) == "gen_prefix() says abracadabra"
- builder = SCons.Builder.Builder(prefix = {None : 'default-',
- '.in' : 'out-',
- '.x' : 'y-'})
- tgt = builder(env, source = 'f1')
+ def my_emit(env, sources):
+ return env.subst('$EMIT')
+ my_env = Environment(FOO = '.foo', EMIT = 'emit-')
+ builder = SCons.Builder.Builder(prefix = {None : 'default-',
+ '.in' : 'out-',
+ '.x' : 'y-',
+ '$FOO' : 'foo-',
+ '.zzz' : my_emit})
+ tgt = builder(my_env, source = 'f1')
assert tgt.path == 'default-f1', tgt.path
- tgt = builder(env, source = 'f2.c')
+ tgt = builder(my_env, source = 'f2.c')
assert tgt.path == 'default-f2', tgt.path
- tgt = builder(env, source = 'f3.in')
+ tgt = builder(my_env, source = 'f3.in')
assert tgt.path == 'out-f3', tgt.path
- tgt = builder(env, source = 'f4.x')
+ tgt = builder(my_env, source = 'f4.x')
assert tgt.path == 'y-f4', tgt.path
+ tgt = builder(my_env, source = 'f5.foo')
+ assert tgt.path == 'foo-f5', tgt.path
+ tgt = builder(my_env, source = 'f6.zzz')
+ assert tgt.path == 'emit-f6', tgt.path
def test_src_suffix(self):
"""Test Builder creation with a specified source file suffix
@@ -444,17 +453,26 @@ class BuilderTestCase(unittest.TestCase):
my_env['BAR'] = 'presto chango'
assert builder.get_suffix(my_env) == "gen_suffix() says presto chango"
- builder = SCons.Builder.Builder(suffix = {None : '.default',
- '.in' : '.out',
- '.x' : '.y'})
- tgt = builder(env, source = 'f1')
+ def my_emit(env, sources):
+ return env.subst('$EMIT')
+ my_env = Environment(BAR = '.bar', EMIT = '.emit')
+ builder = SCons.Builder.Builder(suffix = {None : '.default',
+ '.in' : '.out',
+ '.x' : '.y',
+ '$BAR' : '.new',
+ '.zzz' : my_emit})
+ tgt = builder(my_env, source = 'f1')
assert tgt.path == 'f1.default', tgt.path
- tgt = builder(env, source = 'f2.c')
+ tgt = builder(my_env, source = 'f2.c')
assert tgt.path == 'f2.default', tgt.path
- tgt = builder(env, source = 'f3.in')
+ tgt = builder(my_env, source = 'f3.in')
assert tgt.path == 'f3.out', tgt.path
- tgt = builder(env, source = 'f4.x')
+ tgt = builder(my_env, source = 'f4.x')
assert tgt.path == 'f4.y', tgt.path
+ tgt = builder(my_env, source = 'f5.bar')
+ assert tgt.path == 'f5.new', tgt.path
+ tgt = builder(my_env, source = 'f6.zzz')
+ assert tgt.path == 'f6.emit', tgt.path
def test_ListBuilder(self):
"""Testing ListBuilder class."""
@@ -513,6 +531,7 @@ class BuilderTestCase(unittest.TestCase):
builder2 = SCons.Builder.MultiStepBuilder(action='bar',
src_builder = builder1,
src_suffix = '.foo')
+
tgt = builder2(env, target='baz', source=['test.bar', 'test2.foo', 'test3.txt'])
assert str(tgt.sources[0]) == 'test.foo', str(tgt.sources[0])
assert str(tgt.sources[0].sources[0]) == 'test.bar', \
@@ -530,6 +549,25 @@ class BuilderTestCase(unittest.TestCase):
src_builder = 'xyzzy',
src_suffix = '.xyzzy')
assert builder3.get_src_builders(Environment()) == []
+
+ builder4 = SCons.Builder.Builder(action='bld4',
+ src_suffix='.i',
+ suffix='_wrap.c')
+ builder5 = SCons.Builder.MultiStepBuilder(action='bld5',
+ src_builder=builder4,
+ suffix='.obj',
+ src_suffix='.c')
+ builder6 = SCons.Builder.MultiStepBuilder(action='bld6',
+ src_builder=builder5,
+ suffix='.exe',
+ src_suffix='.obj')
+ tgt = builder6(env, 'test', 'test.i')
+ assert str(tgt) == 'test.exe', str(tgt)
+ assert str(tgt.sources[0]) == 'test_wrap.obj', str(tgt.sources[0])
+ assert str(tgt.sources[0].sources[0]) == 'test_wrap.c', \
+ str(tgt.sources[0].sources[0])
+ assert str(tgt.sources[0].sources[0].sources[0]) == 'test.i', \
+ str(tgt.sources[0].sources[0].sources[0])
def test_CompositeBuilder(self):
"""Testing CompositeBuilder class."""
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index 2f3f762..3a96d67 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -194,7 +194,7 @@ def createCFileBuilders(env):
except KeyError:
c_file = SCons.Builder.Builder(action = {},
emitter = {},
- suffix = '$CFILESUFFIX')
+ suffix = {None:'$CFILESUFFIX'})
env['BUILDERS']['CFile'] = c_file
env['CFILESUFFIX'] = '.c'
@@ -203,7 +203,7 @@ def createCFileBuilders(env):
except KeyError:
cxx_file = SCons.Builder.Builder(action = {},
emitter = {},
- suffix = '$CXXFILESUFFIX')
+ suffix = {None:'$CXXFILESUFFIX'})
env['BUILDERS']['CXXFile'] = cxx_file
env['CXXFILESUFFIX'] = '.cc'
@@ -310,6 +310,7 @@ def tool_list(platform, env):
'pdflatex', 'pdftex', 'Perforce',
'RCS', 'rmic', 'SCCS',
# 'Subversion',
+ 'swig',
'tar', 'tex', 'yacc', 'zip'],
env)
diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py
new file mode 100644
index 0000000..9dcfad4
--- /dev/null
+++ b/src/engine/SCons/Tool/swig.py
@@ -0,0 +1,65 @@
+"""SCons.Tool.swig
+
+Tool-specific initialization for swig.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# 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.path
+import string
+
+import SCons.Defaults
+import SCons.Tool
+
+def swigSuffixEmitter(env, source):
+ if '-c++' in string.split(env.subst("$SWIGFLAGS")):
+ return '$SWIGCXXFILESUFFIX'
+ else:
+ return '$SWIGCFILESUFFIX'
+
+def generate(env):
+ """Add Builders and construction variables for swig to an Environment."""
+ c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
+
+ c_file.suffix['.i'] = swigSuffixEmitter
+ cxx_file.suffix['.i'] = swigSuffixEmitter
+
+ c_file.add_action('.i', '$SWIGCOM')
+ cxx_file.add_action('.i', '$SWIGCOM')
+
+ env['SWIG'] = 'swig'
+ env['SWIGFLAGS'] = ''
+ env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX'
+ env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX'
+ env['SWIGCOM'] = '$SWIG $SWIGFLAGS -o $TARGET $SOURCES'
+
+def exists(env):
+ return env.Detect(['swig'])
diff --git a/test/SWIG.py b/test/SWIG.py
new file mode 100644
index 0000000..74e73c4
--- /dev/null
+++ b/test/SWIG.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# 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 = TestSCons.python
+_exe = TestSCons._exe
+_obj = TestSCons._obj
+
+test = TestSCons.TestSCons()
+
+
+
+test.write('myswig.py', r"""
+import getopt
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'c:o:')
+for opt, arg in opts:
+ if opt == '-c': pass
+ elif opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:4] != 'swig':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(tools=['default', 'swig'], SWIG = r'%s myswig.py')
+env.Program(target = 'test1', source = 'test1.i')
+env.CFile(target = 'test2', source = 'test2.i')
+env.Copy(SWIGFLAGS = '-c++').Program(target = 'test3', source = 'test3.i')
+""" % (python))
+
+test.write('test1.i', r"""
+void
+main(int argc, char *argv[]) {
+ argv[argc++] = "--";
+ printf("test1.i\n");
+ exit (0);
+}
+swig
+""")
+
+test.write('test2.i', r"""test2.i
+swig
+""")
+
+test.write('test3.i', r"""
+#include <stdio.h>
+#include <stdlib.h>
+void
+main(int argc, char *argv[]) {
+ argv[argc++] = "--";
+ printf("test3.i\n");
+ exit (0);
+}
+swig
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.run(program = test.workpath('test1' + _exe), stdout = "test1.i\n")
+test.fail_test(not os.path.exists(test.workpath('test1_wrap.c')))
+test.fail_test(not os.path.exists(test.workpath('test1_wrap' + _obj)))
+
+test.fail_test(test.read('test2_wrap.c') != "test2.i\n")
+
+test.run(program = test.workpath('test3' + _exe), stdout = "test3.i\n")
+test.fail_test(not os.path.exists(test.workpath('test3_wrap.cc')))
+test.fail_test(not os.path.exists(test.workpath('test3_wrap' + _obj)))
+
+
+
+swig = test.where_is('swig')
+
+if swig:
+
+ 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(SWIGFLAGS='-python',
+ CPPPATH='/usr/include/python1.5/',
+ SHCCFLAGS='',
+ SHOBJSUFFIX='.o',
+ SHLIBPREFIX='')
+swig = foo.Dictionary('SWIG')
+#bar = Environment(SWIG = r'%s wrapper.py ' + swig)
+foo.SharedLibrary(target = 'example', source = ['example.c', 'example.i'])
+#foo.SharedLibrary(target = 'foo', source = ['example.c'])
+#foo.SharedLibrary(target = 'foo', source = ['example.i'])
+#bar.SharedLibrary(target = 'bar', source = 'example.i')
+""" % python)
+
+ # Simple example.c and example.i stolen from the SWIG tutorial.
+ test.write("example.c", """\
+#include <time.h>
+double My_variable = 3.0;
+
+int fact(int n) {
+ if (n <= 1) return 1;
+ else return n*fact(n-1);
+}
+
+int my_mod(int x, int y) {
+ return (x%y);
+}
+
+char *get_time()
+{
+ return "Tue Aug 12 23:32:15 2003";
+}
+""")
+
+ test.write("example.i", """\
+%module example
+%{
+/* Put header files here (optional) */
+%}
+
+extern double My_variable;
+extern int fact(int n);
+extern int my_mod(int x, int y);
+extern char *get_time();
+""")
+
+ test.run(arguments = '.')
+
+ test.up_to_date(arguments = '.')
+
+ test.run(program = python, stdin = """\
+import example
+print example.fact(5)
+print example.my_mod(7, 3)
+print example.get_time()
+""", stdout="""\
+120
+1
+Tue Aug 12 23:32:15 2003
+""")
+
+
+
+test.pass_test()
diff --git a/test/import.py b/test/import.py
index 99aa120..be3858a 100644
--- a/test/import.py
+++ b/test/import.py
@@ -99,6 +99,7 @@ tools = [
'suncc',
'sunlink',
'Subversion',
+ 'swig',
'tar',
'tex',
'yacc',