From 283dd73743e1dad11cf8c27cbcc6928a45af29dc Mon Sep 17 00:00:00 2001 From: stevenknight Date: Thu, 7 Mar 2002 07:31:13 +0000 Subject: [PATCH] Make the CFile Builder's SUFFIX configurable. git-svn-id: http://scons.tigris.org/svn/scons/trunk@283 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- doc/man/scons.1 | 8 +++ src/CHANGES.txt | 3 + src/engine/SCons/Builder.py | 112 +++++++++++++++++-------------- src/engine/SCons/BuilderTests.py | 56 +++++++++++----- src/engine/SCons/Defaults.py | 4 +- test/CFILESUFFIX.py | 83 +++++++++++++++++++++++ 6 files changed, 195 insertions(+), 71 deletions(-) create mode 100644 test/CFILESUFFIX.py diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 9614a0fc..302b2ebe 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -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 diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 5835996a..8b4269c1 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -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 diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 0b8fc494..2c87b6ad 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -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 diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 68810b4b..fc530abb 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -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 diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 0a8e4cd5..baad55ab 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -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 index 00000000..002a756e --- /dev/null +++ b/test/CFILESUFFIX.py @@ -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() -- 2.26.2