Make the CFile Builder's SUFFIX configurable.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 7 Mar 2002 07:31:13 +0000 (07:31 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 7 Mar 2002 07:31:13 +0000 (07:31 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@283 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
test/CFILESUFFIX.py [new file with mode: 0644]

index 9614a0fc3aacbb455c21ba51c9fa8591b9a2a3f7..302b2ebef93c270e5be4b53e573ad3fb5038395f 100644 (file)
@@ -754,6 +754,14 @@ 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
+when generating destination files from Lex (.l) or YACC (.y)
+input files.
+The default suffix, of course, is
+.IR .c .
+
 .IP CPPPATH
 The list of directories that the C preprocessor will search for include
 directories. The C/C++ implicit dependency scanner will search these
index 5835996abcc7e0f4d6ab758a3b31643e82630360..8b4269c1077e1e141fb1efcc4bbb191633c06c1e 100644 (file)
@@ -23,6 +23,9 @@ RELEASE 0.06 -
   - Added RANLIB and RANLIBFLAGS construction variables.  Only use them
     in ARCOM if there's a "ranlib" program on the system.
 
+  - Add a configurable CFILESUFFIX for the Builder of .l and .y files
+    into C files.
+
 
 
 RELEASE 0.05 - Thu, 21 Feb 2002 16:50:03 -0600
index 0b8fc494bb4d5fd95f7745e62a1f8089d28cde67..2c87b6adb919e20d5d44acd543e0f44033ccf4b9 100644 (file)
@@ -168,9 +168,9 @@ class BuilderBase:
         """
         return apply(self.action.get_contents, (), kw)
 
-    def src_suffixes(self):
+    def src_suffixes(self, env):
         if self.src_suffix != '':
-            return [self.src_suffix]
+            return [env.subst(self.src_suffix)]
         return []
 
     def targets(self, node):
@@ -213,8 +213,8 @@ class ListBuilder:
     def get_contents(self, **kw):
         return apply(self.builder.get_contents, (), kw)
 
-    def src_suffixes(self):
-        return self.builder.src_suffixes()
+    def src_suffixes(self, env):
+        return self.builder.src_suffixes(env)
 
     def targets(self, node):
         """Return the list of targets for this builder instance.
@@ -243,19 +243,18 @@ class MultiStepBuilder(BuilderBase):
         BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
                              node_factory, scanner)
         self.src_builder = src_builder
-        self.dictSrcSuffix = {}
-        for suff in self.src_builder.src_suffixes():
-            self.dictSrcSuffix[suff] = None
 
     def __call__(self, env, target = None, source = None):
         slist = SCons.Util.scons_str2nodes(source, self.node_factory)
         final_sources = []
         src_suffix = env.subst(self.src_suffix)
+        sdict = {}
+        for suff in self.src_builder.src_suffixes(env):
+            sdict[suff] = None
         for snode in slist:
             path, ext = os.path.splitext(snode.abspath)
-            if self.dictSrcSuffix.has_key(ext):
-                tgt = self.src_builder(env, target = [ path ],
-                                     source=snode)
+            if sdict.has_key(ext):
+                tgt = self.src_builder(env, target = [ path ], source = snode)
                 if not SCons.Util.is_List(tgt):
                     final_sources.append(tgt)
                 else:
@@ -265,8 +264,9 @@ class MultiStepBuilder(BuilderBase):
         return BuilderBase.__call__(self, env, target=target,
                                     source=final_sources)
 
-    def src_suffixes(self):
-        return BuilderBase.src_suffixes(self) + self.src_builder.src_suffixes()
+    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
@@ -285,53 +285,61 @@ class CompositeBuilder(BuilderBase):
         if src_builder and not SCons.Util.is_List(src_builder):
             src_builder = [src_builder]
         self.src_builder = src_builder
-        self.builder_dict = {}
-        for suff, act in action.items():
-             # Create subsidiary builders for every suffix in the
-             # action dictionary.  If there's a src_builder that
-             # matches the suffix, add that to the initializing
-             # keywords so that a MultiStepBuilder will get created.
-             kw = {'name' : name, 'action' : act, 'src_suffix' : suff}
-             src_bld = filter(lambda x, s=suff: x.suffix == s, self.src_builder)
-             if src_bld:
-                 kw['src_builder'] = src_bld[0]
-             self.builder_dict[suff] = apply(Builder, (), kw)
+        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)
 
-        # XXX These [bs]dict tables are invariant for each unique
-        # CompositeBuilder + Environment pair, so we should cache them.
-        bdict = {}
-        sdict = {}
-        for suffix, bld in self.builder_dict.items():
-            bdict[env.subst(bld.src_suffix)] = bld
-            sdict[suffix] = suffix
-            for s in bld.src_suffixes():
-                bdict[s] = bld
-                sdict[s] = suffix
-
-        for tnode in tlist:
-            suflist = map(lambda x, s=sdict: s[os.path.splitext(x.path)[1]],
-                          slist)
-            last_suffix=''
-            for suffix in suflist:
-                if last_suffix and last_suffix != suffix:
-                    raise UserError, "The builder for %s can only build source files of identical suffixes:  %s." % (tnode.path, str(map(lambda t: str(t.path), tnode.sources)))
-                last_suffix = suffix
-            if last_suffix:
-                try:
-                    bdict[last_suffix].__call__(env, target = tnode,
-                                                source = slist)
-                except KeyError:
-                    raise UserError, "The builder for %s can not build files with suffix: %s" % (tnode.path, suffix)
+        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]:
+                kw['src_builder'] = self.sbuild[r][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):
-        return reduce(lambda x, y: x + y,
-                      map(lambda b: b.src_suffixes(),
-                          self.builder_dict.values()))
+    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
index 68810b4bb51b5ed93a29de0121f0a24f5fee4941..fc530abbb5b7b800ea76a04ff135fdee5676cf54 100644 (file)
@@ -70,8 +70,17 @@ env_scanner = None
 count = 0
 
 class Environment:
+    def __init__(self, **kw):
+        self.d = {}
+        for k, v in kw.items():
+            self.d[k] = v
     def subst(self, s):
-        return s
+        try:
+            if s[0] == '$':
+                return self.d.get(s[1:], '')
+        except IndexError:
+            pass
+        return self.d.get(s, s)
     def get_scanner(self, ext):
         return env_scanner
 env = Environment()
@@ -440,32 +449,43 @@ class BuilderTestCase(unittest.TestCase):
                 "Target has unexpected name: %s" % tgts[1].path
 
     def test_src_suffix(self):
-       """Test Builder creation with a specified source file suffix
-       
-       Make sure that the '.' separator is appended to the
-       beginning if it isn't already present.
-       """
-       builder = SCons.Builder.Builder(name = "builder", src_suffix = '.c')
-        assert builder.src_suffixes() == ['.c'], builder.src_suffixes()
+        """Test Builder creation with a specified source file suffix
+        
+        Make sure that the '.' separator is appended to the
+        beginning if it isn't already present.
+        """
+        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)
 
-       tgt = builder(env, target = 'tgt2', source = 'src2')
-       assert tgt.sources[0].path == 'src2.c', \
-               "Source has unexpected name: %s" % tgt.sources[0].path
+        tgt = b1(env, target = 'tgt2', source = 'src2')
+        assert tgt.sources[0].path == 'src2.c', \
+                "Source has unexpected name: %s" % tgt.sources[0].path
 
-        tgt = builder(env, target = 'tgt3', source = 'src3a src3b')
+        tgt = b1(env, target = 'tgt3', source = 'src3a src3b')
         assert tgt.sources[0].path == 'src3a.c', \
-                "Sources[0] has unexpected name: %s" % tgt.sources[0].path
+                "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
         assert tgt.sources[1].path == 'src3b.c', \
-                "Sources[1] has unexpected name: %s" % tgt.sources[1].path
+                "Unexpected tgt.sources[1] name: %s" % tgt.sources[1].path
 
-        b2 = SCons.Builder.Builder(name = "b2", src_suffix = '.2', src_builder = builder)
-        assert b2.src_suffixes() == ['.2', '.c'], b2.src_suffixes()
+        b2 = SCons.Builder.Builder(name = "b2",
+                                   src_suffix = '.2',
+                                   src_builder = b1)
+        assert b2.src_suffixes(env) == ['.2', '.c'], b2.src_suffixes(env)
 
-        b3 = SCons.Builder.Builder(name = "b2", action = {'.3a' : '', '.3b' : ''})
-        s = b3.src_suffixes()
+        b3 = SCons.Builder.Builder(name = "b3",
+                                   action = {'.3a' : '', '.3b' : ''})
+        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)
+
+        b5 = SCons.Builder.Builder(name = "b5", action = {'$YSUFFIX' : ''})
+        assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
+
     def test_suffix(self):
        """Test Builder creation with a specified target suffix
 
index 0a8e4cd575dfb5c32d278c9a53bccef5175f4c6d..baad55ab8aa206bcd4fe003f10e4bf84d50e6cb7 100644 (file)
@@ -68,7 +68,7 @@ CFile = SCons.Builder.Builder(name = 'CFile',
                               action = { '.l'    : '$LEXCOM',
                                          '.y'    : '$YACCCOM',
                                        },
-                              suffix = '.c')
+                              suffix = '$CFILESUFFIX')
 
 CPlusPlusAction = SCons.Action.Action('$CXXCOM')
 
@@ -205,6 +205,7 @@ def make_win32_env_from_paths(include, lib, path):
         'CC'         : 'cl',
         'CCFLAGS'    : '/nologo',
         'CCCOM'      : '$CC $CCFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'CFILESUFFIX' : '.c',
         'CXX'        : '$CC',
         'CXXFLAGS'   : '$CCFLAGS',
         'CXXCOM'     : '$CXX $CXXFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
@@ -264,6 +265,7 @@ if os.name == 'posix':
         'CC'         : 'cc',
         'CCFLAGS'    : '',
         'CCCOM'      : '$CC $CCFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'CFILESUFFIX' : '.c',
         'CXX'        : 'c++',
         'CXXFLAGS'   : '$CCFLAGS',
         'CXXCOM'     : '$CXX $CXXFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
diff --git a/test/CFILESUFFIX.py b/test/CFILESUFFIX.py
new file mode 100644 (file)
index 0000000..002a756
--- /dev/null
@@ -0,0 +1,83 @@
+#!/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 os.path
+import string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+    _exe = '.exe'
+else:
+    _exe = ''
+
+lex = None
+for dir in string.split(os.environ['PATH'], os.pathsep):
+    l = os.path.join(dir, 'lex' + _exe)
+    if os.path.exists(l):
+        lex = l
+        break
+
+test = TestSCons.TestSCons()
+
+test.no_result(not lex)
+
+test.write('SConstruct', """
+Environment().CFile(target = 'foo', source = 'foo.l')
+Environment(CFILESUFFIX = '.xyz').CFile(target = 'bar', source = 'bar.l')
+""")
+
+lex = r"""
+%%%%
+a      printf("A%sA");
+b      printf("B%sB");
+%%%%
+int
+yywrap()
+{
+    return 1;
+}
+
+main()
+{
+    yylex();
+}
+"""
+
+test.write('foo.l', lex % ('foo.l', 'foo.l'))
+
+test.write('bar.l', lex % ('bar.l', 'bar.l'))
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath('foo.c')))
+
+test.fail_test(not os.path.exists(test.workpath('bar.xyz')))
+
+test.pass_test()