Add SWIG support.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 13 Aug 2003 05:56:44 +0000 (05:56 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 13 Aug 2003 05:56:44 +0000 (05:56 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@765 fdb21ef1-2011-0410-befe-b5e4ea1792b1

bin/files
doc/man/scons.1
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/swig.py [new file with mode: 0644]
test/SWIG.py [new file with mode: 0644]
test/import.py

index baeeb14b2dbaed752c442b23355c16f366cd63a9..a2facbc98423b5eafa85a3a148def9383b336a4b 100644 (file)
--- 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
index b9849c6e2e8cc39869613ca188ddf3ef5f5cafc2..aaf6c0ff9ee55afbdf4abacbf20dde0254a8167c 100644 (file)
@@ -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.
 
index de2d75cc1f1ca614f1732789dc4d2ce956ddb7c2..795b29d2e1f9fe0e7cf58cd054e8412a1616c95f 100644 (file)
@@ -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
index 3cfcff85711adc73b0ef0e7daa10964b49d6f573..b97cd29d356f2899f48f7b28faef9ef3bba20458 100644 (file)
@@ -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
index 34f28bc65a03ed52ae2a90daa2e022544dabb319..f7630e63e3cefee1b73585d1545f489fa0d0e0be 100644 (file)
@@ -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):
index 8aafefaced2b10e6d56ac085b4fcdf24a91fd941..f65230ca7386dad7490be8d064f8ca5f46d72938 100644 (file)
@@ -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."""
index 2f3f762fdfd15eabeb8d8075f921b655aebfb850..3a96d67f2574f0ad049ddc1086c5ecd461f11ae4 100644 (file)
@@ -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 (file)
index 0000000..9dcfad4
--- /dev/null
@@ -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 (file)
index 0000000..74e73c4
--- /dev/null
@@ -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()
index 99aa120cc3d02b22501a1a0e18ac8137098318bb..be3858a0f9915969980ab093254d4a8b6915e2bd 100644 (file)
@@ -99,6 +99,7 @@ tools = [
     'suncc',
     'sunlink',
     'Subversion',
+    'swig',
     'tar',
     'tex',
     'yacc',