diff options
60 files changed, 2632 insertions, 1091 deletions
diff --git a/.svnt/conf b/.svnt/conf new file mode 100644 index 0000000..255a4ac --- /dev/null +++ b/.svnt/conf @@ -0,0 +1,18 @@ +import os +import sys + +python = os.environ.get('PYTHON', sys.executable) + +cmd = '"%(python)s" runtest.py -q --noqmtest %%s' % locals() + +test_inputs = [ + 'src/*.py', + 'QMTest/*.py', +] + +tests = ( + ('src/*Tests.py', cmd), + ('test/*.py', cmd), +) + +valid_regression_results = [0, 2] diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index 196c24e..8d820b6 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -18,9 +18,21 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path +import re import string import sys +import __builtin__ +try: + __builtin__.zip +except AttributeError: + def zip(*lists): + result = [] + for i in xrange(len(lists[0])): + result.append(tuple(map(lambda l, i=i: l[i], lists))) + return result + __builtin__.zip = zip + from TestCommon import * from TestCommon import __all__ @@ -576,6 +588,131 @@ print "self._msvs_versions =", str(env['MSVS']['VERSIONS']) return p return apply(os.path.join, [vs_path] + sub_paths[version][0]) + + NCR = 0 # non-cached rebuild + CR = 1 # cached rebuild (up to date) + NCF = 2 # non-cached build failure + CF = 3 # cached build failure + + if sys.platform == 'win32': + Configure_lib = 'msvcrt' + else: + Configure_lib = 'm' + + # to use cygwin compilers on cmd.exe -> uncomment following line + #Configure_lib = 'm' + + def checkLogAndStdout(self, checks, results, cached, + logfile, sconf_dir, sconstruct, + doCheckLog=1, doCheckStdout=1): + + class NoMatch: + def __init__(self, p): + self.pos = p + + def matchPart(log, logfile, lastEnd): + m = re.match(log, logfile[lastEnd:]) + if not m: + raise NoMatch, lastEnd + return m.end() + lastEnd + try: + #print len(os.linesep) + ls = os.linesep + nols = "(" + for i in range(len(ls)): + nols = nols + "(" + for j in range(i): + nols = nols + ls[j] + nols = nols + "[^" + ls[i] + "])" + if i < len(ls)-1: + nols = nols + "|" + nols = nols + ")" + lastEnd = 0 + logfile = self.read(self.workpath(logfile)) + if (doCheckLog and + string.find( logfile, "scons: warning: The stored build " + "information has an unexpected class." ) >= 0): + self.fail_test() + sconf_dir = sconf_dir + sconstruct = sconstruct + + log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) + log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) + rdstr = "" + cnt = 0 + for check,result,cache_desc in zip(checks, results, cached): + log = re.escape("scons: Configure: " + check) + ls + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) + log = "" + result_cached = 1 + for bld_desc in cache_desc: # each TryXXX + for ext, flag in bld_desc: # each file in TryBuild + file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext)) + if flag == self.NCR: + # rebuild will pass + if ext in ['.c', '.cpp']: + log=log + re.escape(file + " <-") + ls + log=log + r"( \|" + nols + "*" + ls + ")+?" + else: + log=log + "(" + nols + "*" + ls +")*?" + result_cached = 0 + if flag == self.CR: + # up to date + log=log + \ + re.escape("scons: Configure: \"%s\" is up to date." + % file) + ls + log=log+re.escape("scons: Configure: The original builder " + "output was:") + ls + log=log+r"( \|.*"+ls+")+" + if flag == self.NCF: + # non-cached rebuild failure + log=log + "(" + nols + "*" + ls + ")*?" + result_cached = 0 + if flag == self.CF: + # cached rebuild failure + log=log + \ + re.escape("scons: Configure: Building \"%s\" failed " + "in a previous run and all its sources are" + " up to date." % file) + ls + log=log+re.escape("scons: Configure: The original builder " + "output was:") + ls + log=log+r"( \|.*"+ls+")+" + cnt = cnt + 1 + if result_cached: + result = "(cached) " + result + rdstr = rdstr + re.escape(check) + re.escape(result) + "\n" + log=log + re.escape("scons: Configure: " + result) + ls + ls + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) + log = "" + if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd) + if doCheckLog and lastEnd != len(logfile): + raise NoMatch, lastEnd + + except NoMatch, m: + print "Cannot match log file against log regexp." + print "log file: " + print "------------------------------------------------------" + print logfile[m.pos:] + print "------------------------------------------------------" + print "log regexp: " + print "------------------------------------------------------" + print log + print "------------------------------------------------------" + self.fail_test() + + if doCheckStdout: + exp_stdout = self.wrap_stdout(".*", rdstr) + if not self.match_re_dotall(self.stdout(), exp_stdout): + print "Unexpected stdout: " + print "-----------------------------------------------------" + print repr(self.stdout()) + print "-----------------------------------------------------" + print repr(exp_stdout) + print "-----------------------------------------------------" + self.fail_test() + # In some environments, $AR will generate a warning message to stderr # if the library doesn't previously exist and is being created. One # way to fix this is to tell AR to be quiet (sometimes the 'c' flag), diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 73e3df9..c0b91ec 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -1975,6 +1975,59 @@ until the Action object is actually used. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP +.RI AddMethod( object, function ", [" name ]) +.TP +.RI env.AddMethod( function ", [" name ]) +When called with the +.BR AddMethod () +form, +adds the specified +.I function +to the specified +.I object +as the specified method +.IR name . +When called with the +.BR env.AddMethod () +form, +adds the specified +.I function +to the construction environment +.I env +as the specified method +.IR name . +In both cases, if +.I name +is omitted or +.BR None , +the name of the +specified +.I function +itself is used for the method name. + +.ES +# Note that the first argument to the function to +# be attached as a method must be the object through +# which the method will be called; the Python +# convention is to call it 'self'. +def my_method(self, arg): + print "my_method() got", arg + +# Use the global AddMethod() function to add a method +# to the Environment class. This +AddMethod(Environment, my_method) +env = Environment() +env.my_method('arg') + +# Add the function as a method, using the function +# name for the method call. +env = Environment() +env.AddMethod(my_method, 'other_method_name') +env.other_method_name('another arg') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP .RI AddPostAction( target ", " action ) .TP .RI env.AddPostAction( target ", " action ) @@ -4048,16 +4101,19 @@ if not env.has_key('FOO'): env['FOO'] = 'foo' This function provides a way to set a select subset of the scons command line options from a SConscript file. The options supported are: .B clean -which corresponds to -c, --clean, and --remove; +which corresponds to -c, --clean and --remove; .B duplicate -which -corresponds to --duplicate; +which corresponds to --duplicate; +.B help +which corresponds to -h and --help; .B implicit_cache which corresponds to --implicit-cache; .B max_drift which corresponds to --max-drift; .B num_jobs which corresponds to -j and --jobs. +.B random +which corresponds to --random. See the documentation for the corresponding command line object for information about each specific option. Example: diff --git a/doc/user/caching.in b/doc/user/caching.in index 015407b..8dfa731 100644 --- a/doc/user/caching.in +++ b/doc/user/caching.in @@ -430,6 +430,31 @@ </para> + <para> + + If you want to make sure dependencies will be built + in a random order without having to specify + the <literal>--random</literal> on very command line, + you can use the &SetOption; function to + set the <literal>random</litera> option + within any &SConscript; file: + + </para> + + <scons_example name="ex-random"> + <file name="SConstruct" printme="1"> + SetOption('random', 1) + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + </file> + <file name="f1.c">f1.c</file> + <file name="f2.c">f2.c</file> + <file name="f3.c">f3.c</file> + <file name="f4.c">f4.c</file> + <file name="f5.c">f5.c</file> + <file name="f6.c">f6.c</file> + </scons_example> + </section> <!-- diff --git a/doc/user/libraries.in b/doc/user/libraries.in index e5368d1..9a12062 100644 --- a/doc/user/libraries.in +++ b/doc/user/libraries.in @@ -94,6 +94,66 @@ </para> <section> + <title>Building Libraries From Source Code or Object Files</title> + + <para> + + The previous example shows building a library from a + list of source files. + You can, however, also give the &b-link-Library; call + object files, + and it will correctly realize + In fact, you can arbitrarily mix source code files + and object files in the source list: + + </para> + + <para> + + <scons_example name="objects" printme="1"> + <file name="SConstruct" printme="1"> + Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) + </file> + <file name="f1.c"> + void f1() { printf("f1.c\n"); } + </file> + <file name="f2.o"> + object file + </file> + <file name="f3.c"> + void f3() { printf("f3.c\n"); } + </file> + <file name="f4.o"> + object file + </file> + </scons_example> + + <para> + + And SCons realizes that only the source code files + must be compiled into object files + before creating the final library: + + </para> + + <scons_output example="objects" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + Of course, in this example, the object files + must already exist for the build to succeed. + See <xref linkend="chap-nodes">, below, + for information about how you can + build object files explicitly + and include the built files in a library. + + </para> + + </section> + + <section> <title>Building Static Libraries Explicitly: the &b-StaticLibrary; Builder</title> <para> diff --git a/doc/user/libraries.sgml b/doc/user/libraries.sgml index ca2cb97..96814a7 100644 --- a/doc/user/libraries.sgml +++ b/doc/user/libraries.sgml @@ -92,6 +92,56 @@ </para> <section> + <title>Building Libraries From Source Code or Object Files</title> + + <para> + + The previous example shows building a library from a + list of source files. + You can, however, also give the &b-link-Library; call + object files, + and it will correctly realize + In fact, you can arbitrarily mix source code files + and object files in the source list: + + </para> + + <para> + + <programlisting> + Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) + </programlisting> + + <para> + + And SCons realizes that only the source code files + must be compiled into object files + before creating the final library: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o f1.o -c f1.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o f4.o + ranlib libfoo.a + </screen> + + <para> + + Of course, in this example, the object files + must already exist for the build to succeed. + See <xref linkend="chap-nodes">, below, + for information about how you can + build object files explicitly + and include the built files in a library. + + </para> + + </section> + + <section> <title>Building Static Libraries Explicitly: the &b-StaticLibrary; Builder</title> <para> diff --git a/doc/user/main.in b/doc/user/main.in index d864350..aaddb2a 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -117,6 +117,9 @@ XXX SetOption('duplicate') XXX - - duplicate= + XXX GetOption('help') + XXX SetOption('help') + XXX GetOption('num_jobs') XXX SetOption('num_jobs') diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a47992a..9cf69e9 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,6 +8,71 @@ +RELEASE 0.97.X - XXX + + From Mark Bertoglio: + + - Fix listing multiple projects in Visual Studio 7.[01] solution files, + including generating individual project GUIDs instead of re-using + the solution GUID. + + From Jean Brouwers: + + - Add /opt/SUNWspro/bin to the default execution PATH on Solaris. + + From Allan Erskine: + + - Only expect the Microsoft IDL compiler to emit *_p.c and *_data.c + files if the /proxy and /dlldata switches are used (respectively). + + From Steven Knight: + + - Have --debug=explain report if a target is being rebuilt because + AlwaysBuild() is specified (instead of "unknown reasons"). + + - Support {Get,Set}Option('help') to make it easier for SConscript + files to tell if a help option (-h, --help, etc.) has been specified. + + - Support {Get,Set}Option('random') so random-dependency interaction + with CacheDir() is controllable from SConscript files. + + - Push and retrieve built symlinks to/from a CacheDir() as actual + symlinks, not by copying the file contents. + + - Fix how the Action module handles stringifying the shared library + generator in the Tool/mingw.py module. + + - When generating a config.h file, print "#define HAVE_{FEATURE} 1" + instad of just "#define HAVE_{FEATURE}", for more compatibility + with Autoconf-style projects. + + - Fix expansion of $TARGET, $TARGETS, $SOURCE and $SOURCES keywords in + Visual C/C++ PDB file names. + + - Fix locating Visual C/C++ PDB files in build directories. + + - Support an env.AddMethod() method and an AddMethod() global function + for adding a new method, respectively, to a construction environment + or an arbitrary object (such as a class). + + From Leanid Nazdrynau: + + - When applying Tool modules after a construction environment has + already been created, don't overwrite existing $CFILESUFFIX and + $CXXFILESUFFIX value. + + From Jan Nijtmans: + + - Find Java anonymous classes when the next token after the name is + an open parenthesis. + + From Sohail Somani: + + - Add LaTeX scanner support for finding dependencies specified with + the \usepackage{} directive. + + + RELEASE 0.97 - Thu, 17 May 2007 08:59:41 -0500 From Steven Knight: diff --git a/src/RELEASE.txt b/src/RELEASE.txt index b19a39c..6dd66aa 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -25,6 +25,33 @@ RELEASE 0.97 - Thu, 12 Apr 2007 12:36:25 -0500 This is the eighth beta release of SCons. Please consult the CHANGES.txt file for a list of specific changes since last release. + Please note the following important changes since release 0.97: + + -- THE DEFAULT EXECUTION PATH FOR Solaris HAS CHANGED + + On Solaris systems, SCons now adds the "/opt/SUNWspro/bin" + directory to the default execution $PATH variable before the + "/usr/ccs/bin" directory. This was done to reflect the fact + that /opt/SUNWspro/ is the default for SUN tools, but it may + cause a different compiler to be used if you have compilers + installed in both directories. + + -- GENERATED config.h FILES NOW SAY "#define HAVE_{FEATURE} 1" + + When generating a "config.h" file, SCons now defines values that + record the existence of a feature with a "1" value: + + #define HAVE_FEATURE 1 + + Instead of printing the line without a "1", as it used to: + + #define HAVE_FEATURE + + This should not cause any problems in the normal use of "#ifdef + HAVE_{FEATURE}" statements interpreted by a C preprocessor, but + might cause a compatibility issue if a script or other utility + was looking for an exact match of the previous text. + Please note the following important changes since release 0.96.93: -- THE --debug=memoizer OPTION NOW REQUIRES PYTHON 2.2 OR LATER diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 438588b..a010c4a 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -515,9 +515,11 @@ class CommandGeneratorAction(ActionBase): def __str__(self): try: - env = self.presub_env or {} + env = self.presub_env except AttributeError: - env = {} + env = None + if env is None: + env = SCons.Defaults.DefaultEnvironment() act = self._generate([], [], env, 1) return str(act) diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 08373c3..87fb4b0 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -1261,6 +1261,14 @@ class CommandGeneratorActionTestCase(unittest.TestCase): """Test the pre-substitution strings for command generator Actions """ def f(target, source, env, for_signature, self=self): + + # See if "env" is really a construction environment (or + # looks like one) by accessing the FindIxes attribute. + # (The Tool/mingw.py module has a generator that uses this, + # and the __str__() method used to cause problems by passing + # us a regular dictionary as a fallback.) + + env.FindIxes return "FOO" a = SCons.Action.CommandGeneratorAction(f) s = str(a) diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index ecc93c0..c4b1d1d 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -520,6 +520,9 @@ class BuilderBase: t.builder_set(self) new_targets.append(t) + orig_tlist = tlist[:] + orig_slist = slist[:] + target, source = self.emitter(target=tlist, source=slist, env=env) # Now delete the temporary builders that we attached to any @@ -533,8 +536,10 @@ class BuilderBase: # Have to call arg2nodes yet again, since it is legal for # emitters to spit out strings as well as Node instances. - tlist = env.arg2nodes(target, target_factory) - slist = env.arg2nodes(source, source_factory) + tlist = env.arg2nodes(target, target_factory, + target=orig_tlist, source=orig_slist) + slist = env.arg2nodes(source, source_factory, + target=orig_tlist, source=orig_slist) return tlist, slist diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index bb5a69a..3996d5c 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -33,6 +33,7 @@ def Func(): import os.path import sys import types +import StringIO import unittest import UserList @@ -43,6 +44,8 @@ import SCons.Builder import SCons.Environment import SCons.Errors +sys.stdout = StringIO.StringIO() + # Initial setup of the common environment for all tests, # a temporary working directory containing a # script for writing arguments to an output file. @@ -95,7 +98,7 @@ class Environment: source=None, dict=None, conv=None): return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv) - def arg2nodes(self, args, factory): + def arg2nodes(self, args, factory, **kw): global env_arg2nodes_called env_arg2nodes_called = 1 if not SCons.Util.is_List(args): @@ -1210,28 +1213,32 @@ class BuilderTestCase(unittest.TestCase): assert 'baz' in map(str, tgt.sources), map(str, tgt.sources) assert 'bar' in map(str, tgt.sources), map(str, tgt.sources) - # Test that, if an emitter sets a builder on the passed-in - # targets and passes back new targets, the new builder doesn't - # get overwritten. + def test_emitter_preserve_builder(self): + """Test an emitter not overwriting a newly-set builder""" + env = Environment() + new_builder = SCons.Builder.Builder(action='new') node = MyNode('foo8') new_node = MyNode('foo8.new') - def emit3(target, source, env, nb=new_builder, nn=new_node): + + def emit(target, source, env, nb=new_builder, nn=new_node): for t in target: t.builder = nb return [nn], source - builder3=SCons.Builder.Builder(action='foo', - emitter=emit3, - target_factory=MyNode, - source_factory=MyNode) - tgt = builder3(env, target=node, source='bar')[0] + builder=SCons.Builder.Builder(action='foo', + emitter=emit, + target_factory=MyNode, + source_factory=MyNode) + tgt = builder(env, target=node, source='bar')[0] assert tgt is new_node, tgt - assert tgt.builder is builder3, tgt.builder + assert tgt.builder is builder, tgt.builder assert node.builder is new_builder, node.builder - # Test use of a dictionary mapping file suffixes to - # emitter functions + def test_emitter_suffix_map(self): + """Test mapping file suffixes to emitter functions""" + env = Environment() + def emit4a(target, source, env): source = map(str, source) target = map(lambda x: 'emit4a-' + x[:-3], source) @@ -1240,61 +1247,86 @@ class BuilderTestCase(unittest.TestCase): source = map(str, source) target = map(lambda x: 'emit4b-' + x[:-3], source) return (target, source) - builder4 = SCons.Builder.Builder(action='foo', - emitter={'.4a':emit4a, - '.4b':emit4b}, - target_factory=MyNode, - source_factory=MyNode) - tgt = builder4(env, None, source='aaa.4a')[0] + + builder = SCons.Builder.Builder(action='foo', + emitter={'.4a':emit4a, + '.4b':emit4b}, + target_factory=MyNode, + source_factory=MyNode) + tgt = builder(env, None, source='aaa.4a')[0] assert str(tgt) == 'emit4a-aaa', str(tgt) - tgt = builder4(env, None, source='bbb.4b')[0] + tgt = builder(env, None, source='bbb.4b')[0] assert str(tgt) == 'emit4b-bbb', str(tgt) - tgt = builder4(env, None, source='ccc.4c')[0] + tgt = builder(env, None, source='ccc.4c')[0] assert str(tgt) == 'ccc', str(tgt) def emit4c(target, source, env): source = map(str, source) target = map(lambda x: 'emit4c-' + x[:-3], source) return (target, source) - builder4.add_emitter('.4c', emit4c) - tgt = builder4(env, None, source='ccc.4c')[0] + + builder.add_emitter('.4c', emit4c) + tgt = builder(env, None, source='ccc.4c')[0] assert str(tgt) == 'emit4c-ccc', str(tgt) - # Test a list of emitter functions. - def emit5a(target, source, env): + def test_emitter_function_list(self): + """Test lists of emitter functions""" + env = Environment() + + def emit1a(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit5a-' + x[:-2], source) + target = target + map(lambda x: 'emit1a-' + x[:-2], source) return (target, source) - def emit5b(target, source, env): + def emit1b(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit5b-' + x[:-2], source) + target = target + map(lambda x: 'emit1b-' + x[:-2], source) return (target, source) - builder5 = SCons.Builder.Builder(action='foo', - emitter=[emit5a, emit5b], + builder1 = SCons.Builder.Builder(action='foo', + emitter=[emit1a, emit1b], node_factory=MyNode) - tgts = builder5(env, target='target-5', source='aaa.5') + tgts = builder1(env, target='target-1', source='aaa.1') tgts = map(str, tgts) - assert tgts == ['target-5', 'emit5a-aaa', 'emit5b-aaa'], tgts + assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts # Test a list of emitter functions through the environment. - def emit6a(target, source, env): + def emit2a(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit6a-' + x[:-2], source) + target = target + map(lambda x: 'emit2a-' + x[:-2], source) return (target, source) - def emit6b(target, source, env): + def emit2b(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit6b-' + x[:-2], source) + target = target + map(lambda x: 'emit2b-' + x[:-2], source) return (target, source) - builder6 = SCons.Builder.Builder(action='foo', + builder2 = SCons.Builder.Builder(action='foo', emitter='$EMITTERLIST', node_factory=MyNode) - env = Environment(EMITTERLIST = [emit6a, emit6b]) + env = Environment(EMITTERLIST = [emit2a, emit2b]) - tgts = builder6(env, target='target-6', source='aaa.6') + tgts = builder2(env, target='target-2', source='aaa.2') tgts = map(str, tgts) - assert tgts == ['target-6', 'emit6a-aaa', 'emit6b-aaa'], tgts + assert tgts == ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts + + def test_emitter_TARGET_SOURCE(self): + """Test use of $TARGET and $SOURCE in emitter results""" + + env = SCons.Environment.Environment() + + def emit(target, source, env): + return (target + ['${SOURCE}.s1', '${TARGET}.t1'], + source + ['${TARGET}.t2', '${SOURCE}.s2']) + + builder = SCons.Builder.Builder(action='foo', + emitter = emit, + node_factory = MyNode) + + targets = builder(env, target = 'TTT', source ='SSS') + sources = targets[0].sources + targets = map(str, targets) + sources = map(str, sources) + assert targets == ['TTT', 'SSS.s1', 'TTT.t1'], targets + assert sources == ['SSS', 'TTT.t2', 'SSS.s2'], targets def test_no_target(self): """Test deducing the target from the source.""" diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py index 81a8ee4..bb3be56 100644 --- a/src/engine/SCons/Conftest.py +++ b/src/engine/SCons/Conftest.py @@ -462,7 +462,7 @@ def _Have(context, key, have): key_up = re.sub('[^A-Z0-9_]', '_', key_up) context.havedict[key_up] = have if have == 1: - line = "#define %s\n" % key_up + line = "#define %s 1\n" % key_up elif have == 0: line = "/* #undef %s */\n" % key_up elif type(have) == IntType: diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index e5eb40c..94cdf74 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -327,7 +327,7 @@ class SubstitutionEnvironment: def items(self): return self._dict.items() - def arg2nodes(self, args, node_factory=_null, lookup_list=_null): + def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): if node_factory is _null: node_factory = self.fs.File if lookup_list is _null: @@ -351,7 +351,9 @@ class SubstitutionEnvironment: break if not n is None: if SCons.Util.is_String(n): - n = self.subst(n, raw=1) + # n = self.subst(n, raw=1, **kw) + kw['raw'] = 1 + n = apply(self.subst, (n,), kw) if node_factory: n = node_factory(n) if SCons.Util.is_List(n): @@ -359,7 +361,9 @@ class SubstitutionEnvironment: else: nodes.append(n) elif node_factory: - v = node_factory(self.subst(v, raw=1)) + # v = node_factory(self.subst(v, raw=1, **kw)) + kw['raw'] = 1 + v = node_factory(apply(self.subst, (v,), kw)) if SCons.Util.is_List(v): nodes.extend(v) else: @@ -473,6 +477,14 @@ class SubstitutionEnvironment: raise OSError("'%s' exited %d" % (command, status)) return out + def AddMethod(self, function, name=None): + """ + Adds the specified function as a method of this construction + environment with the specified name. If the name is omitted, + the default name is the name of the function itself. + """ + SCons.Util.AddMethod(self, function, name) + def Override(self, overrides): """ Produce a modified environment whose variables are overriden by @@ -1453,7 +1465,7 @@ class Base(SubstitutionEnvironment): def AlwaysBuild(self, *targets): tlist = [] for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.File)) + tlist.extend(self.arg2nodes(t, self.fs.Entry)) for t in tlist: t.set_always_build() return tlist diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 70f9026..a8b718d 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -357,6 +357,22 @@ class SubstitutionTestCase(unittest.TestCase): assert not hasattr(nodes[1], 'bbbb'), nodes[0] assert nodes[1].c == 1, nodes[1] + def test_arg2nodes_target_source(self): + """Test the arg2nodes method with target= and source= keywords + """ + targets = [DummyNode('t1'), DummyNode('t2')] + sources = [DummyNode('s1'), DummyNode('s2')] + env = SubstitutionEnvironment() + nodes = env.arg2nodes(['${TARGET}-a', + '${SOURCE}-b', + '${TARGETS[1]}-c', + '${SOURCES[1]}-d'], + DummyNode, + target=targets, + source=sources) + names = map(lambda n: n.name, nodes) + assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names + def test_gvars(self): """Test the base class gvars() method""" env = SubstitutionEnvironment() @@ -626,6 +642,38 @@ sys.exit(1) finally: sys.stderr = save_stderr + def test_AddMethod(self): + """Test the AddMethod() method""" + env = SubstitutionEnvironment(FOO = 'foo') + + def func(self): + return 'func-' + self['FOO'] + + assert not hasattr(env, 'func') + env.AddMethod(func) + r = env.func() + assert r == 'func-foo', r + + assert not hasattr(env, 'bar') + env.AddMethod(func, 'bar') + r = env.bar() + assert r == 'func-foo', r + + def func2(self, arg=''): + return 'func2-' + self['FOO'] + arg + + env.AddMethod(func2) + r = env.func2() + assert r == 'func2-foo', r + r = env.func2('-xxx') + assert r == 'func2-foo-xxx', r + + env.AddMethod(func2, 'func') + r = env.func() + assert r == 'func2-foo', r + r = env.func('-yyy') + assert r == 'func2-foo-yyy', r + def test_Override(self): "Test overriding construction variables" env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4) @@ -2390,22 +2438,29 @@ def generate(env): def test_AlwaysBuild(self): """Test the AlwaysBuild() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') - t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR') - assert t[0].__class__.__name__ == 'File' + t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR', + env.fs.Dir('dir'), env.fs.File('file')) + assert t[0].__class__.__name__ == 'Entry' assert t[0].path == 'a' assert t[0].always_build - assert t[1].__class__.__name__ == 'File' + assert t[1].__class__.__name__ == 'Entry' assert t[1].path == 'bfff' assert t[1].always_build - assert t[2].__class__.__name__ == 'File' + assert t[2].__class__.__name__ == 'Entry' assert t[2].path == 'c' assert t[2].always_build - assert t[3].__class__.__name__ == 'File' + assert t[3].__class__.__name__ == 'Entry' assert t[3].path == 'd' assert t[3].always_build - assert t[4].__class__.__name__ == 'File' + assert t[4].__class__.__name__ == 'Entry' assert t[4].path == 'bbb' assert t[4].always_build + assert t[5].__class__.__name__ == 'Dir' + assert t[5].path == 'dir' + assert t[5].always_build + assert t[6].__class__.__name__ == 'File' + assert t[6].path == 'file' + assert t[6].always_build def test_BuildDir(self): """Test the BuildDir() method""" diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index a16fee4..c9a1443 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -232,7 +232,10 @@ def CacheRetrieveFunc(target, source, env): return 1 fs.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) if SCons.Action.execute_actions: - fs.copy2(cachefile, t.path) + if fs.islink(cachefile): + fs.symlink(fs.readlink(cachefile), t.path) + else: + fs.copy2(cachefile, t.path) st = fs.stat(cachefile) fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) return 0 @@ -272,7 +275,10 @@ def CachePushFunc(target, source, env): tempfile = cachefile+'.tmp' try: - fs.copy2(t.path, tempfile) + if fs.islink(t.path): + fs.symlink(fs.readlink(t.path), tempfile) + else: + fs.copy2(t.path, tempfile) fs.rename(tempfile, cachefile) st = fs.stat(t.path) fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) @@ -2269,8 +2275,6 @@ class File(Base): self.binfo = self.gen_binfo(calc) return self._cur2() def _cur2(self): - if self.always_build: - return None if not self.exists(): # The file doesn't exist locally... r = self.rfile() diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 6644d7a..f550b5b 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -1046,32 +1046,27 @@ class Node: if not self.exists(): return "building `%s' because it doesn't exist\n" % self + if self.always_build: + return "rebuilding `%s' because AlwaysBuild() is specified\n" % self + old = self.get_stored_info() if old is None: return None old.prepare_dependencies() - def dictify(result, kids, sigs): - for k, s in zip(kids, sigs): - result[k] = s - try: - osig = {} - dictify(osig, old.bsources, old.bsourcesigs) - dictify(osig, old.bdepends, old.bdependsigs) - dictify(osig, old.bimplicit, old.bimplicitsigs) + old_bkids = old.bsources + old.bdepends + old.bimplicit + old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs except AttributeError: return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self new = self.get_binfo() - nsig = {} - dictify(nsig, new.bsources, new.bsourcesigs) - dictify(nsig, new.bdepends, new.bdependsigs) - dictify(nsig, new.bimplicit, new.bimplicitsigs) + new_bkids = new.bsources + new.bdepends + new.bimplicit + new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs - old_bkids = old.bsources + old.bdepends + old.bimplicit - new_bkids = new.bsources + new.bdepends + new.bimplicit + osig = dict(zip(old_bkids, old_bkidsigs)) + nsig = dict(zip(new_bkids, new_bkidsigs)) # The sources and dependencies we'll want to report are all stored # as relative paths to this target's directory, but we want to diff --git a/src/engine/SCons/Platform/sunos.py b/src/engine/SCons/Platform/sunos.py index ed9521e..5029860 100644 --- a/src/engine/SCons/Platform/sunos.py +++ b/src/engine/SCons/Platform/sunos.py @@ -41,4 +41,4 @@ def generate(env): env['MAXLINELENGTH'] = 1045320 env['PKGINFO'] = 'pkginfo' env['PKGCHK'] = '/usr/sbin/pkgchk' - env['ENV']['PATH'] = env['ENV']['PATH'] + ':/usr/ccs/bin' + env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py index d875e6e..645a894 100644 --- a/src/engine/SCons/Scanner/LaTeX.py +++ b/src/engine/SCons/Scanner/LaTeX.py @@ -39,7 +39,7 @@ def LaTeXScanner(fs = SCons.Node.FS.default_fs): ds = LaTeX(name = "LaTeXScanner", suffixes = '$LATEXSUFFIXES', path_variable = 'TEXINPUTS', - regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography){([^}]*)}', + regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}', recursive = 0) return ds @@ -72,6 +72,10 @@ class LaTeX(SCons.Scanner.Classic): base, ext = os.path.splitext( filename ) if ext == "": filename = filename + '.bib' + if include[0] == 'usepackage': + base, ext = os.path.splitext( filename ) + if ext == "": + filename = filename + '.sty' return filename def sort_key(self, include): return SCons.Node.FS._my_normcase(self.latex_name(include)) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index ca7f68c..d80050d 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -40,7 +40,6 @@ import SCons.compat import os import os.path -import random import string import sys import time @@ -789,7 +788,7 @@ class OptParser(OptionParser): help="Read FILE as the top-level SConstruct file.") self.add_option('-h', '--help', action="store_true", default=0, - dest="help_msg", + dest="help", help="Print defined help message, or this one.") self.add_option("-H", "--help-options", @@ -988,13 +987,18 @@ class SConscriptSettableOptions: # This dictionary stores the defaults for all the SConscript # settable options, as well as indicating which options - # are SConscript settable. - self.settable = {'num_jobs':1, - 'max_drift':SCons.Node.FS.default_max_drift, - 'implicit_cache':0, - 'clean':0, - 'duplicate':'hard-soft-copy', - 'diskcheck':diskcheck_all} + # are SConscript settable (and gettable, which for options + # like 'help' is far more important than being settable). + self.settable = { + 'clean' : 0, + 'diskcheck' : diskcheck_all, + 'duplicate' : 'hard-soft-copy', + 'help' : 0, + 'implicit_cache' : 0, + 'max_drift' : SCons.Node.FS.default_max_drift, + 'num_jobs' : 1, + 'random' : 0, + } def get(self, name): if not self.settable.has_key(name): @@ -1118,7 +1122,7 @@ def _main(args, parser): scripts.append(sfile) if not scripts: - if options.help_msg: + if options.help: # 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. @@ -1225,7 +1229,7 @@ def _main(args, parser): fs.chdir(fs.Top) - if options.help_msg: + if ssoptions.get('help'): help_text = SCons.Script.help_text if help_text is None: # They specified -h, but there was no Help() inside the @@ -1341,6 +1345,7 @@ def _main(args, parser): if options.random: def order(dependencies): """Randomize the dependencies.""" + import random # This is cribbed from the implementation of # random.shuffle() in Python 2.X. d = dependencies diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index fde4997..e970989 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -131,6 +131,7 @@ call_stack = _SConscript.call_stack # Action = SCons.Action.Action +AddMethod = SCons.Util.AddMethod AllowSubstExceptions = SCons.Subst.SetAllowableExceptions BoolOption = SCons.Options.BoolOption Builder = SCons.Builder.Builder diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 9a5011b..acb8c9b 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -275,8 +275,9 @@ class Task: """ self.out_of_date = [] for t in self.targets: + t.disambiguate() try: - is_up_to_date = t.disambiguate().current() + is_up_to_date = not t.always_build and t.current() except EnvironmentError, e: raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) if is_up_to_date: diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 757306f..718851d 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -63,6 +63,7 @@ class Node: self.postprocessed = None self._bsig_val = None self._current_val = 0 + self.always_build = None def disambiguate(self): return self @@ -495,7 +496,10 @@ class TaskmasterTestCase(unittest.TestCase): n3 = Node("n3") c4 = Node("c4") c4._current_val = 1 - tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], + a5 = Node("a5") + a5._current_val = 1 + a5.always_build = 1 + tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5], tasker = TaskGen) del ood[:] @@ -514,6 +518,9 @@ class TaskmasterTestCase(unittest.TestCase): t = tm.next_task() assert ood == [], ood + del ood[:] + t = tm.next_task() + assert ood == [a5], ood def test_make_ready_exception(self): """Test handling exceptions from Task.make_ready() diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 177d579..d340d5b 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -53,7 +53,7 @@ if java_parsing: # array declarations "[]"; # semi-colons; # periods. - _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.]|' + + _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + r'[A-Za-z_][\w\.]*|/\*|\*/|\[\])') class OuterState: @@ -90,6 +90,7 @@ if java_parsing: try: return self.anonState except AttributeError: + self.outer_state = self ret = SkipState(1, AnonClassState(self)) self.anonState = ret return ret @@ -154,23 +155,36 @@ if java_parsing: class AnonClassState: """A state that looks for anonymous inner classes.""" - def __init__(self, outer_state): + def __init__(self, old_state): # outer_state is always an instance of OuterState - self.outer_state = outer_state - self.tokens_to_find = 2 + self.outer_state = old_state.outer_state + self.old_state = old_state + self.brace_level = 0 def parseToken(self, token): - # This is an anonymous class if and only if the next - # non-whitespace token is a bracket - if token == '\n': + # This is an anonymous class if and only if the next + # non-whitespace token is a bracket. Everything between + # braces should be parsed as normal java code. + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '\n': + return self + elif token == '(': + self.brace_level = self.brace_level + 1 + return self + if self.brace_level > 0: + if token == 'new': + # look further for anonymous inner class + return SkipState(1, AnonClassState(self)) + elif token in [ '"', "'" ]: + return IgnoreState(token, self) + elif token == ')': + self.brace_level = self.brace_level - 1 return self if token == '{': - self.outer_state.openBracket() self.outer_state.addAnonClass() - elif token == '}': - self.outer_state.closeBracket() - elif token in ['"', "'"]: - return IgnoreState(token, self) - return self.outer_state + return self.old_state.parseToken(token) class SkipState: """A state that will skip a specified number of tokens before diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 853afd4..e848bf9 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -329,6 +329,30 @@ public class A { assert pkg_dir == None, pkg_dir assert classes == ['A$B', 'A'], classes + def test_anonymous_classes_with_parentheses(self): + """Test finding anonymous classes marked by parentheses""" + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\ +import java.io.File; + +public class Foo { + public static void main(String[] args) { + File f = new File( + new File("a") { + public String toString() { + return "b"; + } + } to String() + ) { + public String toString() { + return "c"; + } + }; + } +} +""") + assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes + + if __name__ == "__main__": diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index b2e2eff..dea77fd 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -316,7 +316,8 @@ def createCFileBuilders(env): emitter = {}, suffix = {None:'$CFILESUFFIX'}) env['BUILDERS']['CFile'] = c_file - env['CFILESUFFIX'] = '.c' + + env.SetDefault(CFILESUFFIX = '.c') try: cxx_file = env['BUILDERS']['CXXFile'] @@ -325,7 +326,7 @@ def createCFileBuilders(env): emitter = {}, suffix = {None:'$CXXFILESUFFIX'}) env['BUILDERS']['CXXFile'] = cxx_file - env['CXXFILESUFFIX'] = '.cc' + env.SetDefault(CXXFILESUFFIX = '.cc') return (c_file, cxx_file) diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py index 67579e9..811d573 100644 --- a/src/engine/SCons/Tool/midl.py +++ b/src/engine/SCons/Tool/midl.py @@ -33,6 +33,8 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import string + import SCons.Action import SCons.Builder import SCons.Defaults @@ -46,11 +48,17 @@ def midl_emitter(target, source, env): tlb = target[0] incl = base + '.h' interface = base + '_i.c' - proxy = base + '_p.c' - dlldata = base + '_data.c' + t = [tlb, incl, interface] - t = [tlb, incl, interface, proxy, dlldata] + midlcom = env['MIDLCOM'] + if string.find(midlcom, '/proxy') != -1: + proxy = base + '_p.c' + t.append(proxy) + if string.find(midlcom, '/dlldata') != -1: + dlldata = base + '_data.c' + t.append(dlldata) + return (t,source) idl_scanner = SCons.Scanner.IDL.IDLScan() diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 4b90e3d..c071aa7 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -45,10 +45,10 @@ import SCons.Tool.msvs import SCons.Util def pdbGenerator(env, target, source, for_signature): - if target and env.has_key('PDB') and env['PDB']: - return ['/PDB:%s'%target[0].File(env['PDB']).get_string(for_signature), - '/DEBUG'] - return None + try: + return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] + except (AttributeError, IndexError): + return None def windowsShlinkTargets(target, source, env, for_signature): listCmd = [] @@ -99,7 +99,9 @@ def windowsLibEmitter(target, source, env): "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) if env.has_key('PDB') and env['PDB']: - target.append(env['PDB']) + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + target.append(pdb) + target[0].attributes.pdb = pdb if not no_import_lib and \ not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): @@ -129,7 +131,9 @@ def prog_emitter(target, source, env): "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) if env.has_key('PDB') and env['PDB']: - target.append(env['PDB']) + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + target.append(pdb) + target[0].attributes.pdb = pdb return (target,source) diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index db8e8fd..138f920 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -1018,8 +1018,10 @@ class _GenerateV7DSW(_DSWGenerator): self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n' '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform)) else: - self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' - '\t\t%s.%s.Build.0 = %s|%s\n' %(self.slnguid,variant,variant,platform,self.slnguid,variant,variant,platform)) + for p in self.dspfiles: + guid = _generateGUID(p, '') + self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' + '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform)) self.file.write('\tEndGlobalSection\n') diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index f1e856b..a5ea859 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -950,3 +950,79 @@ class Unbuffered: self.file.flush() def __getattr__(self, attr): return getattr(self.file, attr) + +# The original idea for AddMethod() and RenameFunction() come from the +# following post to the ActiveState Python Cookbook: +# +# ASPN: Python Cookbook : Install bound methods in an instance +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 +# +# That code was a little fragile, though, so the following changes +# have been wrung on it: +# +# * Switched the installmethod() "object" and "function" arguments, +# so the order reflects that the left-hand side is the thing being +# "assigned to" and the right-hand side is the value being assigned. +# +# * Changed explicit type-checking to the "try: klass = object.__class__" +# block in installmethod() below so that it still works with the +# old-style classes that SCons uses. +# +# * Replaced the by-hand creation of methods and functions with use of +# the "new" module, as alluded to in Alex Martelli's response to the +# following Cookbook post: +# +# ASPN: Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 + +def AddMethod(object, function, name = None): + """ + Adds either a bound method to an instance or an unbound method to + a class. If name is ommited the name of the specified function + is used by default. + Example: + a = A() + def f(self, x, y): + self.z = x + y + AddMethod(f, A, "add") + a.add(2, 4) + print a.z + AddMethod(lambda self, i: self.l[i], a, "listIndex") + print a.listIndex(5) + """ + import new + + if name is None: + name = function.func_name + else: + function = RenameFunction(function, name) + + try: + klass = object.__class__ + except AttributeError: + # "object" is really a class, so it gets an unbound method. + object.__dict__[name] = new.instancemethod(function, None, object) + else: + # "object" is really an instance, so it gets a bound method. + object.__dict__[name] = new.instancemethod(function, object, klass) + +def RenameFunction(function, name): + """ + Returns a function identical to the specified function, but with + the specified name. + """ + import new + + # Compatibility for Python 1.5 and 2.1. Can be removed in favor of + # passing function.func_defaults directly to new.function() once + # we base on Python 2.2 or later. + func_defaults = function.func_defaults + if func_defaults is None: + func_defaults = () + + return new.function(function.func_code, + function.func_globals, + name, + func_defaults) + +del __revision__ diff --git a/src/engine/SCons/compat/builtins.py b/src/engine/SCons/compat/builtins.py index a477a48..1124cf0 100644 --- a/src/engine/SCons/compat/builtins.py +++ b/src/engine/SCons/compat/builtins.py @@ -76,11 +76,13 @@ try: dict except NameError: # Pre-2.2 Python has no dict() keyword. - def dict(*arg, **kwargs): + def dict(seq=[], **kwargs): """ New dictionary initialization. """ - d = apply(types.DictType, arg) + d = {} + for k, v in seq: + d[k] = v d.update(kwargs) return d __builtin__.dict = dict diff --git a/test/AddMethod.py b/test/AddMethod.py new file mode 100644 index 0000000..ef4a8d0 --- /dev/null +++ b/test/AddMethod.py @@ -0,0 +1,61 @@ +#!/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__" + +""" +Verify use of the AddMethod() global function (specifically, to add +an unbound method to the Environment class) and its counterpart +construction environment method. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +def foo(self): + return 'foo-' + env['FOO'] + +AddMethod(Environment, foo) +env = Environment(FOO = '111') +print env.foo() + +env = Environment(FOO = '222') +print env.foo() + +env.AddMethod(foo, 'bar') +print env.bar() + +""") + +expect = """\ +foo-111 +foo-222 +foo-222 +""" + +test.run(arguments = '-Q -q', stdout = expect) + +test.pass_test() diff --git a/test/AlwaysBuild.py b/test/AlwaysBuild.py index a107302..d1f07fd 100644 --- a/test/AlwaysBuild.py +++ b/test/AlwaysBuild.py @@ -33,6 +33,9 @@ test = TestSCons.TestSCons() test.subdir('sub') +sub_f3_out = os.path.join('sub', 'f3.out') +_SUBDIR_f3_out = os.path.join('$SUBDIR', 'f3.out') + test.write('SConstruct', """\ def bfunc(target, source, env): import shutil @@ -43,30 +46,37 @@ env = Environment(BUILDERS = { 'B' : B }, SUBDIR='sub') env.B('f1.out', source='f1.in') AlwaysBuild('f1.out') -env.B(r'%s', source='f3.in') -env.AlwaysBuild(r'%s') +env.B(r'%(sub_f3_out)s', source='f3.in') +env.AlwaysBuild(r'%(_SUBDIR_f3_out)s') env.Alias('clean1', [], Delete('clean1-target')) env.AlwaysBuild('clean1') c2 = env.Alias('clean2', [], [Delete('clean2-t1'), Delete('clean2-t2')]) env.AlwaysBuild(c2) -""" % (os.path.join('sub', 'f3.out'), - os.path.join('$SUBDIR', 'f3.out') - )) + +def dir_build(target, source, env): + open('dir_build.txt', 'ab').write('dir_build()\\n') +env.Command(Dir('dir'), None, dir_build) +env.AlwaysBuild('dir') +""" % locals()) test.write('f1.in', "f1.in\n") test.write('f2.in', "1") test.write('f3.in', "f3.in\n") +test.subdir('dir') + test.run(arguments = ".") -test.fail_test(test.read('f1.out') != '1') -test.fail_test(test.read(['sub', 'f3.out']) != '1') +test.must_match('f1.out', '1') +test.must_match(['sub', 'f3.out'], '1') +test.must_match('dir_build.txt', "dir_build()\n") test.write('f2.in', "2") test.run(arguments = ".") -test.fail_test(test.read('f1.out') != '2') -test.fail_test(test.read(['sub', 'f3.out']) != '2') +test.must_match('f1.out', '2') +test.must_match(['sub', 'f3.out'], '2') +test.must_match('dir_build.txt', "dir_build()\ndir_build()\n") test.run(arguments = 'clean1', stdout=test.wrap_stdout("""\ Delete("clean1-target") @@ -77,4 +87,8 @@ Delete("clean2-t1") Delete("clean2-t2") """)) +test.not_up_to_date(arguments = 'dir') + +test.must_match('dir_build.txt', "dir_build()\ndir_build()\ndir_build()\n") + test.pass_test() diff --git a/test/CFILESUFFIX.py b/test/CFILESUFFIX.py index 1fa615a..08c40c2 100644 --- a/test/CFILESUFFIX.py +++ b/test/CFILESUFFIX.py @@ -24,6 +24,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +""" +Verify that we can set CFILESUFFIX to arbitrary values. +""" + import os import os.path import string @@ -49,6 +53,13 @@ test.write('SConstruct', """ env = Environment(LEX = r'%(_python_)s mylex.py', tools = ['lex']) env.CFile(target = 'foo', source = 'foo.l') env.Clone(CFILESUFFIX = '.xyz').CFile(target = 'bar', source = 'bar.l') + +# Make sure that calling a Tool on a construction environment *after* +# we've set CFILESUFFIX doesn't overwrite the value. +env2 = Environment(tools = [], CFILESUFFIX = '.env2') +env2.Tool('lex') +env2['LEX'] = r'%(_python_)s mylex.py' +env2.CFile(target = 'f3', source = 'f3.l') """ % locals()) input = r""" @@ -66,10 +77,14 @@ test.write('foo.l', input % 'foo.l') test.write('bar.l', input % 'bar.l') +test.write('f3.l', input % 'f3.l') + test.run(arguments = '.') -test.fail_test(not os.path.exists(test.workpath('foo.c'))) +test.must_exist(test.workpath('foo.c')) + +test.must_exist(test.workpath('bar.xyz')) -test.fail_test(not os.path.exists(test.workpath('bar.xyz'))) +test.must_exist(test.workpath('f3.env2')) test.pass_test() diff --git a/test/CXX/CXXFILESUFFIX.py b/test/CXX/CXXFILESUFFIX.py index b156f74..fbc34ff 100644 --- a/test/CXX/CXXFILESUFFIX.py +++ b/test/CXX/CXXFILESUFFIX.py @@ -49,6 +49,13 @@ test.write('SConstruct', """ env = Environment(LEX = r'%(_python_)s mylex.py', tools = ['lex']) env.CXXFile(target = 'foo', source = 'foo.ll') env.Clone(CXXFILESUFFIX = '.xyz').CXXFile(target = 'bar', source = 'bar.ll') + +# Make sure that calling a Tool on a construction environment *after* +# we've set CXXFILESUFFIX doesn't overwrite the value. +env2 = Environment(tools = [], CXXFILESUFFIX = '.env2') +env2.Tool('lex') +env2['LEX'] = r'%(_python_)s mylex.py' +env2.CXXFile(target = 'f3', source = 'f3.ll') """ % locals()) input = r""" @@ -66,10 +73,14 @@ test.write('foo.ll', input % 'foo.ll') test.write('bar.ll', input % 'bar.ll') +test.write('f3.ll', input % 'f3.ll') + test.run(arguments = '.') -test.fail_test(not os.path.exists(test.workpath('foo.cc'))) +test.must_exist(test.workpath('foo.cc')) + +test.must_exist(test.workpath('bar.xyz')) -test.fail_test(not os.path.exists(test.workpath('bar.xyz'))) +test.must_exist(test.workpath('f3.env2')) test.pass_test() diff --git a/test/CacheDir/symlink.py b/test/CacheDir/symlink.py new file mode 100644 index 0000000..6dd1f94 --- /dev/null +++ b/test/CacheDir/symlink.py @@ -0,0 +1,68 @@ +#!/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__" + +""" +Verify that we push and retrieve a built symlink to/from a CacheDir() +as an actualy symlink, not by copying the file contents. +""" + +import os +import os.path + +import TestSCons + +test = TestSCons.TestSCons() + +if not hasattr(os, 'symlink'): + import sys + test.skip_test('%s has no os.symlink() method; skipping test\n' % sys.executable) + +test.write('SConstruct', """\ +CacheDir('cache') +import os +Symlink = Action(lambda target, source, env: + os.symlink(str(source[0]), str(target[0])), + "os.symlink($SOURCE, $TARGET)") +Command('file.symlink', 'file.txt', Symlink) +""") + +test.write('file.txt', "file.txt\n") + +test.run(arguments = '.') + +test.fail_test(not os.path.islink('file.symlink')) +test.must_match('file.symlink', "file.txt\n") + +test.run(arguments = '-c .') + +test.must_not_exist('file.symlink') + +test.run(arguments = '.') + +test.fail_test(not os.path.islink('file.symlink')) +test.must_match('file.symlink', "file.txt\n") + +test.pass_test() diff --git a/test/Configure/Action-error.py b/test/Configure/Action-error.py new file mode 100644 index 0000000..0abcc7c --- /dev/null +++ b/test/Configure/Action-error.py @@ -0,0 +1,48 @@ +#!/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__" + +""" +Verify that calling Configure from an Action results in a readable error. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +def ConfigureAction(target, source, env): + env.Configure() + return 0 +env = Environment(BUILDERS = {'MyAction' : + Builder(action=Action(ConfigureAction))}) +env.MyAction('target', []) +""") + +expect = "scons: *** Calling Configure from Builders is not supported.\n" + +test.run(status=2, stderr=expect) + +test.pass_test() diff --git a/test/Configure/BuildDir-SConscript.py b/test/Configure/BuildDir-SConscript.py new file mode 100644 index 0000000..47b7d82 --- /dev/null +++ b/test/Configure/BuildDir-SConscript.py @@ -0,0 +1,159 @@ +#!/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__" + +""" +Verify that Configure calls in SConscript files work when used +with BuildDir. +""" + +import os.path + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.subdir( 'sub', ['sub', 'local'] ) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +opts = Options() +opts.Add('chdir') +env = Environment(options=opts) +if env['chdir'] == 'yes': + SConscriptChdir(1) +else: + SConscriptChdir(0) +BuildDir( 'build', '.' ) +SConscript( 'build/SConscript' ) +""") + +test.write(['sub', 'local', 'local_header.h'], "/* Hello World */" ) + +test.write('SConscript', """\ +SConscript( 'sub/SConscript' ) +""") + +test.write(['sub', 'SConscript'], """\ +def CustomTest(context): + context.Message('Executing Custom Test ... ') + ret = context.TryCompile('#include "local_header.h"', '.c') + context.Result(ret) + return ret + +env = Environment(FOO='fff') +env.Append( CPPPATH='local' ) +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure( env, custom_tests = {'CustomTest' : CustomTest, + '$FOO' : CustomTest} ) +if hasattr(conf, 'fff'): + conf.Message('$FOO should not have been expanded!') + Exit(1) +if not conf.CheckCHeader( 'math.h' ): + Exit(1) +if conf.CheckCHeader( 'no_std_c_header.h' ): + Exit(1) +if not conf.CustomTest(): + Exit(1) +env = conf.Finish() +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write(['sub', 'TestProgram.h'], """\ +/* Just a test header */ +""") + +test.write(['sub', 'TestProgram.c'], """\ +#include "TestProgram.h" +#include <stdio.h> + +int main() { + printf( "Hello\\n" ); +} +""") + +# first with SConscriptChdir(0) +test.run(arguments='chdir=no') +test.checkLogAndStdout( ["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... ", + "Executing Custom Test ... "], + ["yes", "no", "yes"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR))]], + "config.log", + ".sconf_temp", + os.path.join("build", "sub", "SConscript")) + +test.run(arguments='chdir=no') +test.checkLogAndStdout( ["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... ", + "Executing Custom Test ... "], + ["yes", "no", "yes"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))], + [((".c", CR), (_obj, CR))]], + "config.log", + ".sconf_temp", + os.path.join("build", "sub", "SConscript")) + +import shutil +shutil.rmtree(test.workpath(".sconf_temp")) +test.unlink(".sconsign.dblite") + +# now with SConscriptChdir(1) +test.run(arguments='chdir=yes') +test.checkLogAndStdout( ["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... ", + "Executing Custom Test ... "], + ["yes", "no", "yes"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR))]], + "config.log", + ".sconf_temp", + os.path.join("build", "sub", "SConscript")) + +test.run(arguments='chdir=yes') +test.checkLogAndStdout( ["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... ", + "Executing Custom Test ... "], + ["yes", "no", "yes"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))], + [((".c", CR), (_obj, CR))]], + "config.log", + ".sconf_temp", + os.path.join("build", "sub", "SConscript")) + + +test.pass_test() diff --git a/test/Configure/BuildDir.py b/test/Configure/BuildDir.py new file mode 100644 index 0000000..ca3c147 --- /dev/null +++ b/test/Configure/BuildDir.py @@ -0,0 +1,91 @@ +#!/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__" + +""" +Verify that Configure contexts work with basic use of BuildDir. +""" + +import os.path + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +env = Environment(LOGFILE='build/config.log') +import os +env.AppendENVPath('PATH', os.environ['PATH']) +BuildDir( 'build', '.' ) +conf = env.Configure(conf_dir='build/config.tests', log_file='$LOGFILE') +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +# print open( 'build/config.log' ).readlines() +SConscript( 'build/SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include <stdio.h> + +int main() { + printf( "Hello\\n" ); +} +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + os.path.join("build", "config.log"), + os.path.join("build", "config.tests"), + "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + os.path.join("build", "config.log"), + os.path.join("build", "config.tests"), + "SConstruct") + +test.pass_test() diff --git a/test/Configure/Builder-call.py b/test/Configure/Builder-call.py new file mode 100644 index 0000000..a6f2fa5 --- /dev/null +++ b/test/Configure/Builder-call.py @@ -0,0 +1,57 @@ +#!/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__" + +""" +Verify that calling normal Builders from an actual Configure +context environment works correctly. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('cmd.py', r""" +import sys +sys.stderr.write( 'Hello World on stderr\n' ) +sys.stdout.write( 'Hello World on stdout\n' ) +open(sys.argv[1], 'w').write( 'Hello World\n' ) +""") + +test.write('SConstruct', """\ +env = Environment() +def CustomTest(*args): + return 0 +conf = env.Configure(custom_tests = {'MyTest' : CustomTest}) +if not conf.MyTest(): + env.Command("hello", [], '%(_python_)s cmd.py $TARGET') +env = conf.Finish() +""" % locals()) + +test.run(stderr="Hello World on stderr\n") + +test.pass_test() diff --git a/test/Configure/Configure.py b/test/Configure/Configure.py deleted file mode 100644 index 4063699..0000000 --- a/test/Configure/Configure.py +++ /dev/null @@ -1,939 +0,0 @@ -#!/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 re -import shutil -import string -import sys - -import __builtin__ -try: - __builtin__.zip -except AttributeError: - def zip(*lists): - result = [] - for i in xrange(len(lists[0])): - result.append(tuple(map(lambda l, i=i: l[i], lists))) - return result - __builtin__.zip = zip - -import TestCmd -import TestSCons - -if sys.platform == 'win32': - lib = 'msvcrt' -else: - lib = 'm' - -# to use cygwin compilers on cmd.exe -> uncomment following line -#lib = 'm' - -work_cnt = 0 -work_dir = None -python = TestSCons.python -_python_ = TestSCons._python_ -test = TestSCons.TestSCons() -_obj = TestSCons._obj -_exe = TestSCons._exe - -RE = 0 -RE_DOTALL = 1 -EXACT = 2 -def reset(match): - global test, work_dir, work_cnt - work_cnt = work_cnt + 1 - work_dir='test%d' % work_cnt - test.subdir(work_dir) - if match == RE: - test.match_func = TestCmd.match_re - elif match == RE_DOTALL: - test.match_func = TestCmd.match_re_dotall - elif match == EXACT: - test.match_func = TestCmd.match_exact - -def checkFiles(test, files): - global work_dir - for f in files: - test.fail_test( not os.path.isfile( test.workpath(work_dir,f) ) ) - -def checklib(lang, name, up_to_date): - if lang == 'C': - return (".c", _obj, _exe) - elif lang == 'C++': - return (".cc", _obj, _exe) - -class NoMatch: - def __init__(self, p): - self.pos = p - -NCR = 0 # non-cached rebuild -CR = 1 # cached rebuild (up to date) -NCF = 2 # non-cached build failure -CF = 3 # cached build failure - -def checkLogAndStdout(checks, results, cached, - test, logfile, sconf_dir, sconstruct, - doCheckLog=1, doCheckStdout=1): - def matchPart(log, logfile, lastEnd): - m = re.match(log, logfile[lastEnd:]) - if not m: - raise NoMatch, lastEnd - return m.end() + lastEnd - try: - #print len(os.linesep) - ls = os.linesep - nols = "(" - for i in range(len(ls)): - nols = nols + "(" - for j in range(i): - nols = nols + ls[j] - nols = nols + "[^" + ls[i] + "])" - if i < len(ls)-1: - nols = nols + "|" - nols = nols + ")" - lastEnd = 0 - logfile = test.read(test.workpath(work_dir, logfile)) - if (doCheckLog and - string.find( logfile, "scons: warning: The stored build " - "information has an unexpected class." ) >= 0): - test.fail_test() - sconf_dir = sconf_dir - sconstruct = sconstruct - - log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls - if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) - log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls - if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) - rdstr = "" - cnt = 0 - for check,result,cache_desc in zip(checks, results, cached): - log = re.escape("scons: Configure: " + check) + ls - if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) - log = "" - result_cached = 1 - for bld_desc in cache_desc: # each TryXXX - for ext, flag in bld_desc: # each file in TryBuild - file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext)) - if flag == NCR: - # rebuild will pass - if ext in ['.c', '.cpp']: - log=log + re.escape(file + " <-") + ls - log=log + r"( \|" + nols + "*" + ls + ")+?" - else: - log=log + "(" + nols + "*" + ls +")*?" - result_cached = 0 - if flag == CR: - # up to date - log=log + \ - re.escape("scons: Configure: \"%s\" is up to date." - % file) + ls - log=log+re.escape("scons: Configure: The original builder " - "output was:") + ls - log=log+r"( \|.*"+ls+")+" - if flag == NCF: - # non-cached rebuild failure - log=log + "(" + nols + "*" + ls + ")*?" - result_cached = 0 - if flag == CF: - # cached rebuild failure - log=log + \ - re.escape("scons: Configure: Building \"%s\" failed " - "in a previous run and all its sources are" - " up to date." % file) + ls - log=log+re.escape("scons: Configure: The original builder " - "output was:") + ls - log=log+r"( \|.*"+ls+")+" - cnt = cnt + 1 - if result_cached: - result = "(cached) " + result - rdstr = rdstr + re.escape(check) + re.escape(result) + "\n" - log=log + re.escape("scons: Configure: " + result) + ls + ls - if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) - log = "" - if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd) - if doCheckLog and lastEnd != len(logfile): - raise NoMatch, lastEnd - - except NoMatch, m: - print "Cannot match log file against log regexp." - print "log file: " - print "------------------------------------------------------" - print logfile[m.pos:] - print "------------------------------------------------------" - print "log regexp: " - print "------------------------------------------------------" - print log - print "------------------------------------------------------" - test.fail_test() - - if doCheckStdout: - exp_stdout = test.wrap_stdout(".*", rdstr) - if not test.match_re_dotall(test.stdout(), exp_stdout): - print "Unexpected stdout: " - print "-----------------------------------------------------" - print repr(test.stdout()) - print "-----------------------------------------------------" - print repr(exp_stdout) - print "-----------------------------------------------------" - test.fail_test() - -try: - # 1.1 if checks are ok, the cache mechanism should work - - reset(RE) - - test.write([work_dir, 'SConstruct'], """ -if int(ARGUMENTS.get('target_signatures_content', 0)): - TargetSignatures('content') -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckLibWithHeader( '%s', 'math.h', 'c' ) -r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' ) -r3 = conf.CheckLib( '%s', autoadd=0 ) -r4 = conf.CheckLib( None, autoadd=0 ) -r5 = conf.CheckCHeader( 'math.h' ) -r6 = conf.CheckCXXHeader( 'vector' ) -env = conf.Finish() -if not (r1 and r2 and r3 and r4 and r5 and r6): - Exit(1) -""" % (lib,lib)) - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + - [[((".c", NCR), (_obj, NCR))]] + - [[((".cpp", NCR), (_obj, NCR))]], - test, "config.log", ".sconf_temp", "SConstruct") - - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + - [[((".c", CR), (_obj, CR))]] + - [[((".cpp", CR), (_obj, CR))]], - test, "config.log", ".sconf_temp", "SConstruct") - - # same should be true for TargetSignatures('content') - - test.run(chdir=work_dir, arguments='target_signatures_content=1 --config=force') - checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + - [[((".c", NCR), (_obj, NCR))]] + - [[((".cpp", NCR), (_obj, NCR))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir, arguments='target_signatures_content=1') - checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + - [[((".c", CR), (_obj, CR))]] + - [[((".cpp", CR), (_obj, CR))]], - test, "config.log", ".sconf_temp", "SConstruct") - - # 1.2 if checks are not ok, the cache mechanism should work as well - # (via explicit cache) - reset(EXACT) # match exactly, "()" is a regexp thing - - test.write([work_dir, 'SConstruct'], """ -if int(ARGUMENTS.get('target_signatures_content', 0)): - TargetSignatures('content') -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = env.Configure() -r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error -env = conf.Finish() -if not (not r1 and not r2): - print "FAIL: ", r1, r2 - Exit(1) -""") - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR), (_exe, NCF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", CR), (_obj, CF))], - [((".c", CR), (_obj, CR), (_exe, CF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - # 1.3 same should be true for TargetSignatures('content') - test.run(chdir=work_dir, arguments='--config=force target_signatures_content=1') - checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR), (_exe, NCF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir, arguments='target_signatures_content=1') - checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", CR), (_obj, CF))], - [((".c", CR), (_obj, CR), (_exe, CF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - - - # 2.1 test that normal builds work together with Sconf - reset(RE_DOTALL) - - - test.write([work_dir, 'SConstruct'], """ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - test.write([work_dir, 'SConscript'], """ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - test.write([work_dir, 'TestProgram.c'], """ -#include <stdio.h> - -int main() { - printf( "Hello\\n" ); -} -""") - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - # 2.2 test that BuildDir builds work together with Sconf - reset(RE_DOTALL) - - - test.write([work_dir, 'SConstruct'], """ -env = Environment(LOGFILE='build/config.log') -import os -env.AppendENVPath('PATH', os.environ['PATH']) -BuildDir( 'build', '.' ) -conf = env.Configure(conf_dir='build/config.tests', log_file='$LOGFILE') -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -# print open( 'build/config.log' ).readlines() -SConscript( 'build/SConscript' ) -""") - test.write([work_dir, 'SConscript'], """ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - test.write([work_dir, 'TestProgram.c'], """ -#include <stdio.h> - -int main() { - printf( "Hello\\n" ); -} -""") - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - test, - os.path.join("build", "config.log"), - os.path.join("build", "config.tests"), - "SConstruct") - - test.run(chdir=work_dir) - checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - test, - os.path.join("build", "config.log"), - os.path.join("build", "config.tests"), - "SConstruct") - - # 2.3 test that Configure calls in SConscript files work - # even if BuildDir is set - reset(RE_DOTALL) - - test.subdir( [work_dir, 'sub'], [work_dir, 'sub', 'local'] ) - test.write([work_dir, 'SConstruct'], """ -opts = Options() -opts.Add('chdir') -env = Environment(options=opts) -if env['chdir'] == 'yes': - SConscriptChdir(1) -else: - SConscriptChdir(0) -BuildDir( 'build', '.' ) -SConscript( 'build/SConscript' ) -""") - test.write([work_dir, 'sub', 'local', 'local_header.h'], - "/* Hello World */" ) - test.write([work_dir, 'SConscript'], """ -SConscript( 'sub/SConscript' ) -""") - test.write([work_dir, 'sub', 'SConscript'], """ -def CustomTest(context): - context.Message('Executing Custom Test ... ') - ret = context.TryCompile('#include "local_header.h"', '.c') - context.Result(ret) - return ret - -env = Environment(FOO='fff') -env.Append( CPPPATH='local' ) -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure( env, custom_tests = {'CustomTest' : CustomTest, - '$FOO' : CustomTest} ) -if hasattr(conf, 'fff'): - conf.Message('$FOO should not have been expanded!') - Exit(1) -if not conf.CheckCHeader( 'math.h' ): - Exit(1) -if conf.CheckCHeader( 'no_std_c_header.h' ): - Exit(1) -if not conf.CustomTest(): - Exit(1) -env = conf.Finish() -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - test.write([work_dir, 'sub', 'TestProgram.h'], """ -/* Just a test header */ -""") - test.write([work_dir, 'sub', 'TestProgram.c'], """ -#include "TestProgram.h" -#include <stdio.h> - -int main() { - printf( "Hello\\n" ); -} -""") - - # first with SConscriptChdir(0) - test.run(chdir=work_dir, arguments='chdir=no') - checkLogAndStdout( ["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... ", - "Executing Custom Test ... "], - ["yes", "no", "yes"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR))]], - test, "config.log", - ".sconf_temp", - os.path.join("build", "sub", "SConscript")) - - test.run(chdir=work_dir, arguments='chdir=no') - checkLogAndStdout( ["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... ", - "Executing Custom Test ... "], - ["yes", "no", "yes"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))], - [((".c", CR), (_obj, CR))]], - test, "config.log", - ".sconf_temp", - os.path.join("build", "sub", "SConscript")) - - shutil.rmtree(test.workpath(work_dir, ".sconf_temp")) - os.unlink(test.workpath(work_dir, ".sconsign.dblite")) - - # now with SConscriptChdir(1) - test.run(chdir=work_dir, arguments='chdir=yes') - checkLogAndStdout( ["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... ", - "Executing Custom Test ... "], - ["yes", "no", "yes"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR))]], - test, "config.log", - ".sconf_temp", - os.path.join("build", "sub", "SConscript")) - - test.run(chdir=work_dir, arguments='chdir=yes') - checkLogAndStdout( ["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... ", - "Executing Custom Test ... "], - ["yes", "no", "yes"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))], - [((".c", CR), (_obj, CR))]], - test, "config.log", - ".sconf_temp", - os.path.join("build", "sub", "SConscript")) - - # 3.1 test custom tests - reset(RE_DOTALL) - - compileOK = '#include <stdio.h>\\nint main() {printf("Hello");return 0;}' - compileFAIL = "syntax error" - linkOK = compileOK - linkFAIL = "void myFunc(); int main() { myFunc(); }" - runOK = compileOK - runFAIL = "int main() { return 1; }" - test.write([work_dir, 'pyAct.py'], 'import sys\nprint sys.argv[1]\nsys.exit(int(sys.argv[1]))\n') - test.write([work_dir, 'SConstruct'], """ -def CheckCustom(test): - test.Message( 'Executing MyTest ... ' ) - retCompileOK = test.TryCompile( '%s', '.c' ) - retCompileFAIL = test.TryCompile( '%s', '.c' ) - retLinkOK = test.TryLink( '%s', '.c' ) - retLinkFAIL = test.TryLink( '%s', '.c' ) - (retRunOK, outputRunOK) = test.TryRun( '%s', '.c' ) - (retRunFAIL, outputRunFAIL) = test.TryRun( '%s', '.c' ) - (retActOK, outputActOK) = test.TryAction( '%s pyAct.py 0 > $TARGET' ) - (retActFAIL, outputActFAIL) = test.TryAction( '%s pyAct.py 1 > $TARGET' ) - resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello" - resOK = resOK and retActOK and int(outputActOK)==0 - resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!="" - resFAIL = resFAIL or retActFAIL or outputActFAIL!="" - test.Result( int(resOK and not resFAIL) ) - return resOK and not resFAIL - -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} ) -conf.CheckCustom() -env = conf.Finish() -""" % (compileOK, compileFAIL, linkOK, linkFAIL, runOK, runFAIL, - python, python ) ) - test.run(chdir=work_dir) - checkLogAndStdout(["Executing MyTest ... "], - ["yes"], - [[(('.c', NCR), (_obj, NCR)), - (('.c', NCR), (_obj, NCF)), - (('.c', NCR), (_obj, NCR), (_exe, NCR)), - (('.c', NCR), (_obj, NCR), (_exe, NCF)), - (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCR)), - (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCF)), - (('', NCR),), - (('', NCF),)]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir) - checkLogAndStdout(["Executing MyTest ... "], - ["yes"], - [[(('.c', CR), (_obj, CR)), - (('.c', CR), (_obj, CF)), - (('.c', CR), (_obj, CR), (_exe, CR)), - (('.c', CR), (_obj, CR), (_exe, CF)), - (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CR)), - (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CF)), - (('', CR),), - (('', CF),)]], - test, "config.log", ".sconf_temp", "SConstruct") - - # 4.1 test that calling normal builders from an actual configuring - # environment works - reset(RE_DOTALL) - - test.write([work_dir, 'cmd.py'], r""" -import sys -sys.stderr.write( 'Hello World on stderr\n' ) -sys.stdout.write( 'Hello World on stdout\n' ) -open(sys.argv[1], 'w').write( 'Hello World\n' ) -""") - - test.write([work_dir, 'SConstruct'], """ -env = Environment() -def CustomTest(*args): - return 0 -conf = env.Configure(custom_tests = {'MyTest' : CustomTest}) -if not conf.MyTest(): - env.Command("hello", [], '%(_python_)s cmd.py $TARGET') -env = conf.Finish() -""" % locals()) - test.run(chdir=work_dir, stderr="Hello World on stderr\n") - - # 4.2 test that calling Configure from a builder results in a - # readable Error - reset(EXACT) - - test.write([work_dir, 'SConstruct'], """ -def ConfigureAction(target, source, env): - env.Configure() - return 0 -env = Environment(BUILDERS = {'MyAction' : - Builder(action=Action(ConfigureAction))}) -env.MyAction('target', []) -""") - test.run(chdir=work_dir, status=2, - stderr="scons: *** Calling Configure from Builders is not supported.\n") - - # 4.3 test the calling Configure from multiple subsidiary, - # nested SConscript files does *not* result in an error. - - test.subdir([work_dir, 'dir1'], - [work_dir, 'dir2'], - [work_dir, 'dir2', 'sub1'], - [work_dir, 'dir2', 'sub1', 'sub2']) - test.write([work_dir, 'SConstruct'], """ -env = Environment() -SConscript(dirs=['dir1', 'dir2'], exports="env") -""") - test.write([work_dir, 'dir1', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -""") - test.write([work_dir, 'dir2', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -SConscript(dirs=['sub1'], exports="env") -""") - test.write([work_dir, 'dir2', 'sub1', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -SConscript(dirs=['sub2'], exports="env") -""") - test.write([work_dir, 'dir2', 'sub1', 'sub2', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -""") - test.run(chdir=work_dir) - - # 5.1 test the ConfigureDryRunError - - reset(EXACT) # exact match - - SConstruct_path = test.workpath(work_dir, 'SConstruct') - - test.write(SConstruct_path, """ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckLib('%s') # will pass -r2 = conf.CheckLib('hopefullynolib') # will fail -env = conf.Finish() -if not (r1 and not r2): - Exit(1) -""" % (lib)) - - test.run(chdir=work_dir, arguments='-n', status=2, stderr=""" -scons: *** Cannot create configure directory ".sconf_temp" within a dry-run. -File "%(SConstruct_path)s", line 5, in ? -""" % locals()) - test.must_not_exist([work_dir, 'config.log']) - test.subdir([work_dir, '.sconf_temp']) - - conftest_0_c = os.path.join(".sconf_temp", "conftest_0.c") - - test.run(chdir=work_dir, arguments='-n', status=2, stderr=""" -scons: *** Cannot update configure test "%(conftest_0_c)s" within a dry-run. -File "%(SConstruct_path)s", line 6, in ? -""" % locals()) - - test.run(chdir=work_dir) - checkLogAndStdout( ["Checking for C library %s... " % lib, - "Checking for C library hopefullynolib... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - test, "config.log", ".sconf_temp", "SConstruct") - oldLog = test.read(test.workpath(work_dir, 'config.log')) - - test.run(chdir=work_dir, arguments='-n') - checkLogAndStdout( ["Checking for C library %s... " % lib, - "Checking for C library hopefullynolib... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - test, "config.log", ".sconf_temp", "SConstruct", - doCheckLog=0) - newLog = test.read(test.workpath(work_dir, 'config.log')) - if newLog != oldLog: - print "Unexpected update of log file within a dry run" - test.fail_test() - - # 5.2 test the --config=<auto|force|cache> option - reset(EXACT) # exact match - - SConstruct_path = test.workpath(work_dir, 'SConstruct') - - test.write(SConstruct_path, """ -env = Environment(CPPPATH='#/include') -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckCHeader('non_system_header1.h') -r2 = conf.CheckCHeader('non_system_header2.h') -env = conf.Finish() -""") - test.subdir([work_dir, 'include']) - test.write([work_dir, 'include', 'non_system_header1.h'], """ -/* A header */ -""") - - conftest_0_c = os.path.join(".sconf_temp", "conftest_0.c") - - test.run(chdir=work_dir, arguments='--config=cache', status=2, stderr=""" -scons: *** "%(conftest_0_c)s" is not yet built and cache is forced. -File "%(SConstruct_path)s", line 6, in ? -""" % locals()) - - test.run(chdir=work_dir, arguments='--config=auto') - checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", - "Checking for C header file non_system_header2.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - test, "config.log", ".sconf_temp", "SConstruct") - test.run(chdir=work_dir, arguments='--config=auto') - checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", - "Checking for C header file non_system_header2.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir, arguments='--config=force') - checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", - "Checking for C header file non_system_header2.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir, arguments='--config=cache') - checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", - "Checking for C header file non_system_header2.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.write([work_dir, 'include', 'non_system_header2.h'], """ -/* Another header */ -""") - test.unlink([work_dir, 'include', 'non_system_header1.h']) - test.run(chdir=work_dir, arguments='--config=cache') - checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", - "Checking for C header file non_system_header2.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - test, "config.log", ".sconf_temp", "SConstruct") - - test.run(chdir=work_dir, arguments='--config=auto') - checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", - "Checking for C header file non_system_header2.h... "], - ["no", "yes"], - [[((".c", CR), (_obj, NCF))], - [((".c", CR), (_obj, NCR))]], - test, "config.log", ".sconf_temp", "SConstruct") - - # 5.3 test -Q option - reset(EXACT) - test.write([work_dir, 'SConstruct'], """ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckCHeader('stdio.h') -env = conf.Finish() -""") - test.run(chdir=work_dir, arguments='-Q', - stdout="scons: `.' is up to date.\n", stderr="") - - - # 6. check config.h support - reset(EXACT) - test.write([work_dir, 'SConstruct'], """ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env, config_h = 'config.h') -r1 = conf.CheckFunc('printf') -r2 = conf.CheckFunc('noFunctionCall') -r3 = conf.CheckType('int') -r4 = conf.CheckType('noType') -r5 = conf.CheckCHeader('stdio.h', '<>') -r6 = conf.CheckCHeader('hopefullynoc-header.h') -r7 = conf.CheckCXXHeader('vector', '<>') -r8 = conf.CheckCXXHeader('hopefullynocxx-header.h') -env = conf.Finish() -conf = Configure(env, config_h = 'config.h') -r9 = conf.CheckLib('%s', 'sin') -r10 = conf.CheckLib('hopefullynolib', 'sin') -r11 = conf.CheckLibWithHeader('%s', 'math.h', 'c') -r12 = conf.CheckLibWithHeader('%s', 'hopefullynoheader2.h', 'c') -r13 = conf.CheckLibWithHeader('hopefullynolib2', 'math.h', 'c') -env = conf.Finish() -""" % (lib, lib, lib)) - - expected_read_str = """\ -Checking for C function printf()... yes -Checking for C function noFunctionCall()... no -Checking for C type int... yes -Checking for C type noType... no -Checking for C header file stdio.h... yes -Checking for C header file hopefullynoc-header.h... no -Checking for C++ header file vector... yes -Checking for C++ header file hopefullynocxx-header.h... no -Checking for sin() in C library %(lib)s... yes -Checking for sin() in C library hopefullynolib... no -Checking for C library %(lib)s... yes -Checking for C library %(lib)s... no -Checking for C library hopefullynolib2... no -""" % {'lib' : lib} - - expected_build_str = """\ -scons: Configure: creating config.h -""" - - expected_stdout = test.wrap_stdout(build_str=expected_build_str, - read_str=expected_read_str) - - expected_config_h = string.replace("""#ifndef CONFIG_H_SEEN -#define CONFIG_H_SEEN - -#define HAVE_PRINTF -/* #undef HAVE_NOFUNCTIONCALL */ -#define HAVE_INT -/* #undef HAVE_NOTYPE */ -#define HAVE_STDIO_H -/* #undef HAVE_HOPEFULLYNOC_HEADER_H */ -#define HAVE_VECTOR -/* #undef HAVE_HOPEFULLYNOCXX_HEADER_H */ -#define HAVE_%(LIB)s -/* #undef HAVE_LIBHOPEFULLYNOLIB */ -#define HAVE_%(LIB)s -/* #undef HAVE_%(LIB)s */ -/* #undef HAVE_LIBHOPEFULLYNOLIB2 */ - -#endif /* CONFIG_H_SEEN */ -""" % {'LIB' : "LIB" + string.upper(lib) }, "\n", os.linesep) - - test.run(chdir=work_dir, stdout=expected_stdout) - config_h = test.read(test.workpath(work_dir, 'config.h')) - if expected_config_h != config_h: - print "Unexpected config.h" - print "Expected: " - print "---------------------------------------------------------" - print repr(expected_config_h) - print "---------------------------------------------------------" - print "Found: " - print "---------------------------------------------------------" - print repr(config_h) - print "---------------------------------------------------------" - print "Stdio: " - print "---------------------------------------------------------" - print test.stdout() - print "---------------------------------------------------------" - test.fail_test() - - expected_read_str = re.sub(r'\b((yes)|(no))\b', - r'(cached) \1', - expected_read_str) - expected_build_str = "scons: `.' is up to date.\n" - expected_stdout = test.wrap_stdout(build_str=expected_build_str, - read_str=expected_read_str) - #expected_stdout = string.replace(expected_stdout, "\n", os.linesep) - test.run(chdir=work_dir, stdout=expected_stdout) - config_h = test.read(test.workpath(work_dir, 'config.h')) - if expected_config_h != config_h: - print "Unexpected config.h" - print "Expected: " - print "---------------------------------------------------------" - print repr(expected_config_h) - print "---------------------------------------------------------" - print "Found: " - print "---------------------------------------------------------" - print repr(config_h) - print "---------------------------------------------------------" - print "Stdio: " - print "---------------------------------------------------------" - print test.stdout() - print "---------------------------------------------------------" - test.fail_test() - -finally: - pass - #os.system( 'find . -type f -exec ls -l {} \;' ) - #print "-------------config.log------------------" - #print test.read( test.workpath(work_dir, 'config.log')) - #print "-------------build/config.log------------" - #print test.read( test.workpath('build/config.log' )) - - -test.pass_test() diff --git a/test/Configure/ConfigureDryRunError.py b/test/Configure/ConfigureDryRunError.py new file mode 100644 index 0000000..04fbc75 --- /dev/null +++ b/test/Configure/ConfigureDryRunError.py @@ -0,0 +1,99 @@ +#!/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__" + +""" +Verify the ConfigureDryRunError. +""" + +import os.path + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """ +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckLib('%s') # will pass +r2 = conf.CheckLib('hopefullynolib') # will fail +env = conf.Finish() +if not (r1 and not r2): + Exit(1) +""" % (lib)) + +test.run(arguments='-n', status=2, stderr=""" +scons: *** Cannot create configure directory ".sconf_temp" within a dry-run. +File "%(SConstruct_path)s", line 5, in ? +""" % locals()) + +test.must_not_exist('config.log') +test.subdir('.sconf_temp') + +conftest_0_c = os.path.join(".sconf_temp", "conftest_0.c") + +test.run(arguments='-n', status=2, stderr=""" +scons: *** Cannot update configure test "%(conftest_0_c)s" within a dry-run. +File "%(SConstruct_path)s", line 6, in ? +""" % locals()) + +test.run() +test.checkLogAndStdout( ["Checking for C library %s... " % lib, + "Checking for C library hopefullynolib... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +oldLog = test.read(test.workpath('config.log')) + +test.run(arguments='-n') +test.checkLogAndStdout( ["Checking for C library %s... " % lib, + "Checking for C library hopefullynolib... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct", + doCheckLog=0) + +newLog = test.read(test.workpath('config.log')) +if newLog != oldLog: + print "Unexpected update of log file within a dry run" + test.fail_test() + +test.pass_test() diff --git a/test/Configure/SConscript.py b/test/Configure/SConscript.py new file mode 100644 index 0000000..2bfe2f7 --- /dev/null +++ b/test/Configure/SConscript.py @@ -0,0 +1,74 @@ +#!/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__" + +""" +Verify that Configure contexts from multiple subsidiary SConscript +files work without error. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['dir1'], + ['dir2'], + ['dir2', 'sub1'], + ['dir2', 'sub1', 'sub2']) + +test.write('SConstruct', """\ +env = Environment() +SConscript(dirs=['dir1', 'dir2'], exports="env") +""") + +test.write(['dir1', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +""") + +test.write(['dir2', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +SConscript(dirs=['sub1'], exports="env") +""") + +test.write(['dir2', 'sub1', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +SConscript(dirs=['sub2'], exports="env") +""") + +test.write(['dir2', 'sub1', 'sub2', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +""") + +test.run() + +test.pass_test() diff --git a/test/Configure/basic.py b/test/Configure/basic.py new file mode 100644 index 0000000..677f40f --- /dev/null +++ b/test/Configure/basic.py @@ -0,0 +1,83 @@ +#!/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__" + +""" +Verify that basic builds work with Configure contexts. +""" + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include <stdio.h> + +int main() { + printf( "Hello\\n" ); +} +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() diff --git a/test/Configure/cache-not-ok.py b/test/Configure/cache-not-ok.py new file mode 100644 index 0000000..82e32f6 --- /dev/null +++ b/test/Configure/cache-not-ok.py @@ -0,0 +1,96 @@ +#!/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__" + +""" +Verify that the cache mechanism works when checks are not ok. +""" + +import os.path + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +if int(ARGUMENTS.get('target_signatures_content', 0)): + TargetSignatures('content') +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = env.Configure() +r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error +env = conf.Finish() +if not (not r1 and not r2): + print "FAIL: ", r1, r2 + Exit(1) +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", CR), (_obj, CF))], + [((".c", CR), (_obj, CR), (_exe, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +# same should be true for TargetSignatures('content') + +test.run(arguments='--config=force target_signatures_content=1') +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='target_signatures_content=1') +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", CR), (_obj, CF))], + [((".c", CR), (_obj, CR), (_exe, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() diff --git a/test/Configure/cache-ok.py b/test/Configure/cache-ok.py new file mode 100644 index 0000000..d8eac77 --- /dev/null +++ b/test/Configure/cache-ok.py @@ -0,0 +1,118 @@ +#!/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__" + +""" +Verify that the cache mechanism works when checks are ok. +""" + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re) + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +if int(ARGUMENTS.get('target_signatures_content', 0)): + TargetSignatures('content') +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckLibWithHeader( '%(lib)s', 'math.h', 'c' ) +r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' ) +r3 = conf.CheckLib( '%(lib)s', autoadd=0 ) +r4 = conf.CheckLib( None, autoadd=0 ) +r5 = conf.CheckCHeader( 'math.h' ) +r6 = conf.CheckCXXHeader( 'vector' ) +env = conf.Finish() +if not (r1 and r2 and r3 and r4 and r5 and r6): + Exit(1) +""" % locals()) + +test.run() +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + + [[((".c", NCR), (_obj, NCR))]] + + [[((".cpp", NCR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + + +test.run() +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + + [[((".c", CR), (_obj, CR))]] + + [[((".cpp", CR), (_obj, CR))]], + "config.log", ".sconf_temp", "SConstruct") + +# same should be true for TargetSignatures('content') + +test.run(arguments='target_signatures_content=1 --config=force') +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + + [[((".c", NCR), (_obj, NCR))]] + + [[((".cpp", NCR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='target_signatures_content=1') +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + + [[((".c", CR), (_obj, CR))]] + + [[((".cpp", CR), (_obj, CR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() diff --git a/test/Configure/config-h.py b/test/Configure/config-h.py new file mode 100644 index 0000000..7bc8645 --- /dev/null +++ b/test/Configure/config-h.py @@ -0,0 +1,155 @@ +#!/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__" + +""" +Verify creation of a config.h file from a Configure context. +""" + +import os +import re +import string + +import TestSCons + +test = TestSCons.TestSCons(match = TestSCons.match_exact) + +lib = test.Configure_lib +LIB = "LIB" + string.upper(lib) + +test.write('SConstruct', """\ +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env, config_h = 'config.h') +r1 = conf.CheckFunc('printf') +r2 = conf.CheckFunc('noFunctionCall') +r3 = conf.CheckType('int') +r4 = conf.CheckType('noType') +r5 = conf.CheckCHeader('stdio.h', '<>') +r6 = conf.CheckCHeader('hopefullynoc-header.h') +r7 = conf.CheckCXXHeader('vector', '<>') +r8 = conf.CheckCXXHeader('hopefullynocxx-header.h') +env = conf.Finish() +conf = Configure(env, config_h = 'config.h') +r9 = conf.CheckLib('%(lib)s', 'sin') +r10 = conf.CheckLib('hopefullynolib', 'sin') +r11 = conf.CheckLibWithHeader('%(lib)s', 'math.h', 'c') +r12 = conf.CheckLibWithHeader('%(lib)s', 'hopefullynoheader2.h', 'c') +r13 = conf.CheckLibWithHeader('hopefullynolib2', 'math.h', 'c') +env = conf.Finish() +""" % locals()) + +expected_read_str = """\ +Checking for C function printf()... yes +Checking for C function noFunctionCall()... no +Checking for C type int... yes +Checking for C type noType... no +Checking for C header file stdio.h... yes +Checking for C header file hopefullynoc-header.h... no +Checking for C++ header file vector... yes +Checking for C++ header file hopefullynocxx-header.h... no +Checking for sin() in C library %(lib)s... yes +Checking for sin() in C library hopefullynolib... no +Checking for C library %(lib)s... yes +Checking for C library %(lib)s... no +Checking for C library hopefullynolib2... no +""" % locals() + +expected_build_str = """\ +scons: Configure: creating config.h +""" + +expected_stdout = test.wrap_stdout(build_str=expected_build_str, + read_str=expected_read_str) + +expected_config_h = string.replace("""\ +#ifndef CONFIG_H_SEEN +#define CONFIG_H_SEEN + +#define HAVE_PRINTF 1 +/* #undef HAVE_NOFUNCTIONCALL */ +#define HAVE_INT 1 +/* #undef HAVE_NOTYPE */ +#define HAVE_STDIO_H 1 +/* #undef HAVE_HOPEFULLYNOC_HEADER_H */ +#define HAVE_VECTOR 1 +/* #undef HAVE_HOPEFULLYNOCXX_HEADER_H */ +#define HAVE_%(LIB)s 1 +/* #undef HAVE_LIBHOPEFULLYNOLIB */ +#define HAVE_%(LIB)s 1 +/* #undef HAVE_%(LIB)s */ +/* #undef HAVE_LIBHOPEFULLYNOLIB2 */ + +#endif /* CONFIG_H_SEEN */ +""" % locals(), "\n", os.linesep) + +test.run(stdout=expected_stdout) + +config_h = test.read(test.workpath('config.h')) +if expected_config_h != config_h: + print "Unexpected config.h" + print "Expected: " + print "---------------------------------------------------------" + print repr(expected_config_h) + print "---------------------------------------------------------" + print "Found: " + print "---------------------------------------------------------" + print repr(config_h) + print "---------------------------------------------------------" + print "Stdio: " + print "---------------------------------------------------------" + print test.stdout() + print "---------------------------------------------------------" + test.fail_test() + +expected_read_str = re.sub(r'\b((yes)|(no))\b', + r'(cached) \1', + expected_read_str) +expected_build_str = "scons: `.' is up to date.\n" +expected_stdout = test.wrap_stdout(build_str=expected_build_str, + read_str=expected_read_str) +#expected_stdout = string.replace(expected_stdout, "\n", os.linesep) + +test.run(stdout=expected_stdout) + +config_h = test.read(test.workpath('config.h')) +if expected_config_h != config_h: + print "Unexpected config.h" + print "Expected: " + print "---------------------------------------------------------" + print repr(expected_config_h) + print "---------------------------------------------------------" + print "Found: " + print "---------------------------------------------------------" + print repr(config_h) + print "---------------------------------------------------------" + print "Stdio: " + print "---------------------------------------------------------" + print test.stdout() + print "---------------------------------------------------------" + test.fail_test() + +test.pass_test() diff --git a/test/Configure/custom-tests.py b/test/Configure/custom-tests.py new file mode 100644 index 0000000..7fb0ab7 --- /dev/null +++ b/test/Configure/custom-tests.py @@ -0,0 +1,111 @@ +#!/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__" + +""" +Verify execution of custom test cases. +""" + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +compileOK = '#include <stdio.h>\\nint main() {printf("Hello");return 0;}' +compileFAIL = "syntax error" +linkOK = compileOK +linkFAIL = "void myFunc(); int main() { myFunc(); }" +runOK = compileOK +runFAIL = "int main() { return 1; }" + +test.write('pyAct.py', """\ +import sys +print sys.argv[1] +sys.exit(int(sys.argv[1])) +""") + +test.write('SConstruct', """\ +def CheckCustom(test): + test.Message( 'Executing MyTest ... ' ) + retCompileOK = test.TryCompile( '%(compileOK)s', '.c' ) + retCompileFAIL = test.TryCompile( '%(compileFAIL)s', '.c' ) + retLinkOK = test.TryLink( '%(linkOK)s', '.c' ) + retLinkFAIL = test.TryLink( '%(linkFAIL)s', '.c' ) + (retRunOK, outputRunOK) = test.TryRun( '%(runOK)s', '.c' ) + (retRunFAIL, outputRunFAIL) = test.TryRun( '%(runFAIL)s', '.c' ) + (retActOK, outputActOK) = test.TryAction( '%(_python_)s pyAct.py 0 > $TARGET' ) + (retActFAIL, outputActFAIL) = test.TryAction( '%(_python_)s pyAct.py 1 > $TARGET' ) + resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello" + resOK = resOK and retActOK and int(outputActOK)==0 + resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!="" + resFAIL = resFAIL or retActFAIL or outputActFAIL!="" + test.Result( int(resOK and not resFAIL) ) + return resOK and not resFAIL + +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} ) +conf.CheckCustom() +env = conf.Finish() +""" % locals()) + +test.run() + +test.checkLogAndStdout(["Executing MyTest ... "], + ["yes"], + [[(('.c', NCR), (_obj, NCR)), + (('.c', NCR), (_obj, NCF)), + (('.c', NCR), (_obj, NCR), (_exe, NCR)), + (('.c', NCR), (_obj, NCR), (_exe, NCF)), + (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCR)), + (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCF)), + (('', NCR),), + (('', NCF),)]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() + +test.checkLogAndStdout(["Executing MyTest ... "], + ["yes"], + [[(('.c', CR), (_obj, CR)), + (('.c', CR), (_obj, CF)), + (('.c', CR), (_obj, CR), (_exe, CR)), + (('.c', CR), (_obj, CR), (_exe, CF)), + (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CR)), + (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CF)), + (('', CR),), + (('', CF),)]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() diff --git a/test/Configure/option--Q.py b/test/Configure/option--Q.py new file mode 100644 index 0000000..ab9d488 --- /dev/null +++ b/test/Configure/option--Q.py @@ -0,0 +1,46 @@ +#!/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__" + +""" +Verify that the -Q option suppresses Configure context output. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckCHeader('stdio.h') +env = conf.Finish() +""") + +test.run(arguments='-Q', stdout="scons: `.' is up to date.\n", stderr="") + +test.pass_test() diff --git a/test/Configure/option--config.py b/test/Configure/option--config.py new file mode 100644 index 0000000..95a0d3b --- /dev/null +++ b/test/Configure/option--config.py @@ -0,0 +1,122 @@ +#!/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__" + +""" +Verify use of the --config=<auto|force|cache> option. +""" + +import os.path + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +test.subdir('include') + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """ +env = Environment(CPPPATH='#/include') +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckCHeader('non_system_header1.h') +r2 = conf.CheckCHeader('non_system_header2.h') +env = conf.Finish() +""") + +test.write(['include', 'non_system_header1.h'], """ +/* A header */ +""") + +conftest_0_c = os.path.join(".sconf_temp", "conftest_0.c") + +test.run(arguments='--config=cache', status=2, stderr=""" +scons: *** "%(conftest_0_c)s" is not yet built and cache is forced. +File "%(SConstruct_path)s", line 6, in ? +""" % locals()) + +test.run(arguments='--config=auto') +test.checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", + "Checking for C header file non_system_header2.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='--config=auto') +test.checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", + "Checking for C header file non_system_header2.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='--config=force') +test.checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", + "Checking for C header file non_system_header2.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='--config=cache') +test.checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", + "Checking for C header file non_system_header2.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.write(['include', 'non_system_header2.h'], """ +/* Another header */ +""") +test.unlink(['include', 'non_system_header1.h']) + +test.run(arguments='--config=cache') +test.checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", + "Checking for C header file non_system_header2.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='--config=auto') +test.checkLogAndStdout( ["Checking for C header file non_system_header1.h... ", + "Checking for C header file non_system_header2.h... "], + ["no", "yes"], + [[((".c", CR), (_obj, NCF))], + [((".c", CR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() diff --git a/test/GetOption/help.py b/test/GetOption/help.py new file mode 100644 index 0000000..37f9555 --- /dev/null +++ b/test/GetOption/help.py @@ -0,0 +1,54 @@ +#!/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__" + +""" +Test use of GetOption('help') to short-circuit work. +""" + +import string + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +if GetOption('help'): + print "GetOption('help') set" +else: + print "no help for you" +""") + +test.run(arguments = '-q -Q', stdout = "no help for you\n") + +expect = "GetOption('help') set" + +test.run(arguments = '-q -Q -h') +test.fail_test(string.split(test.stdout(), '\n')[0] != expect) + +test.run(arguments = '-q -Q --help') +test.fail_test(string.split(test.stdout(), '\n')[0] != expect) + +test.pass_test() diff --git a/test/IDL/MIDLCOM.py b/test/IDL/MIDLCOM.py index 79857d0..b285568 100644 --- a/test/IDL/MIDLCOM.py +++ b/test/IDL/MIDLCOM.py @@ -37,12 +37,18 @@ test = TestSCons.TestSCons() test.write('mymidl.py', """ +import os.path import sys -outfile = open(sys.argv[1], 'wb') +out_tlb = open(sys.argv[1], 'wb') +base = os.path.splitext(sys.argv[1])[0] +out_h = open(base + '.h', 'wb') +out_c = open(base + '_i.c', 'wb') for f in sys.argv[2:]: infile = open(f, 'rb') for l in filter(lambda l: l != '/*midl*/\\n', infile.readlines()): - outfile.write(l) + out_tlb.write(l) + out_h.write(l) + out_c.write(l) sys.exit(0) """) @@ -57,6 +63,10 @@ test.write('aaa.idl', "aaa.idl\n/*midl*/\n") test.run(arguments = '.') test.must_match('aaa.tlb', "aaa.idl\n") +test.must_match('aaa.h', "aaa.idl\n") +test.must_match('aaa_i.c', "aaa.idl\n") + +test.up_to_date(options = '--debug=explain', arguments = 'aaa.tlb') diff --git a/test/MSVC/multiple-pdb.py b/test/MSVC/multiple-pdb.py new file mode 100644 index 0000000..f359fb9 --- /dev/null +++ b/test/MSVC/multiple-pdb.py @@ -0,0 +1,83 @@ +#!/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__"
+
+"""
+Verify that setting $PDB to '${TARGET}.pdb allows us to build multiple
+programs with separate .pdb files from the same environment.
+
+Under the covers, this verifies that emitters support expansion of the
+$TARGET variable (and implicitly $SOURCE), using the original specified
+list(s).
+"""
+
+import sys
+
+import TestSCons
+
+_exe = TestSCons._exe
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+ msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+ test.skip_test(msg)
+
+test.write('SConstruct', """\
+env = Environment(PDB = '${TARGET}.pdb')
+env.Program('test1.cpp')
+env.Program('test2.cpp')
+""")
+
+test.write('test1.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+ printf("test1.cpp\\n");
+ exit (0);
+}
+""")
+
+test.write('test2.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+ printf("test2.cpp\\n");
+ exit (0);
+}
+""")
+
+test.run(arguments = '.')
+
+test.must_exist('test1%s' % _exe)
+test.must_exist('test1%s.pdb' % _exe)
+test.must_exist('test2%s' % _exe)
+test.must_exist('test2%s.pdb' % _exe)
+
+test.pass_test()
diff --git a/test/MSVC/pdb-BuildDir-path.py b/test/MSVC/pdb-BuildDir-path.py new file mode 100644 index 0000000..223e535 --- /dev/null +++ b/test/MSVC/pdb-BuildDir-path.py @@ -0,0 +1,76 @@ +#!/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__"
+
+"""
+Verify that .pdb files get put in a build_dir correctly.
+"""
+
+import sys
+
+import TestSCons
+
+_exe = TestSCons._exe
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+ msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+ test.skip_test(msg)
+
+test.subdir('src')
+
+test.write('SConstruct', """\
+env = Environment()
+env.Append(BINDIR = '#bin')
+
+Export('env')
+SConscript('#src/SConscript', duplicate = 0, build_dir = '#.build')
+""")
+
+test.write(['src', 'SConscript'], """\
+Import('env')
+env['PDB'] = '${TARGET}.pdb'
+p = env.Program('test.exe', 'test.cpp')
+env.Install(env['BINDIR'], p)
+""")
+
+test.write(['src', 'test.cpp'], """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+ printf("test.cpp\\n");
+ exit (0);
+}
+""")
+
+test.run(arguments = '.')
+
+test.must_exist(['.build', 'test%s' % _exe])
+test.must_exist(['.build', 'test%s.pdb' % _exe])
+
+test.pass_test()
diff --git a/test/MSVS/vs-7.0-files.py b/test/MSVS/vs-7.0-files.py index b9827e7..aaffd7d 100644 --- a/test/MSVS/vs-7.0-files.py +++ b/test/MSVS/vs-7.0-files.py @@ -53,8 +53,8 @@ Global \tGlobalSection(ProjectDependencies) = postSolution \tEndGlobalSection \tGlobalSection(ProjectConfiguration) = postSolution -\t\t{SLNGUID}.Release.ActiveCfg = Release|Win32 -\t\t{SLNGUID}.Release.Build.0 = Release|Win32 +\t\t{E5466E26-0003-F18B-8F8A-BCD76C86388D}.Release.ActiveCfg = Release|Win32 +\t\t{E5466E26-0003-F18B-8F8A-BCD76C86388D}.Release.Build.0 = Release|Win32 \tEndGlobalSection \tGlobalSection(ExtensibilityGlobals) = postSolution \tEndGlobalSection diff --git a/test/MSVS/vs-7.1-files.py b/test/MSVS/vs-7.1-files.py index 057f34f..98bd9da 100644 --- a/test/MSVS/vs-7.1-files.py +++ b/test/MSVS/vs-7.1-files.py @@ -53,8 +53,8 @@ Global \t\tConfigName.0 = Release \tEndGlobalSection \tGlobalSection(ProjectConfiguration) = postSolution -\t\t{SLNGUID}.Release.ActiveCfg = Release|Win32 -\t\t{SLNGUID}.Release.Build.0 = Release|Win32 +\t\t{E5466E26-0003-F18B-8F8A-BCD76C86388D}.Release.ActiveCfg = Release|Win32 +\t\t{E5466E26-0003-F18B-8F8A-BCD76C86388D}.Release.Build.0 = Release|Win32 \tEndGlobalSection \tGlobalSection(ExtensibilityGlobals) = postSolution \tEndGlobalSection diff --git a/test/TEX/usepackage.py b/test/TEX/usepackage.py new file mode 100644 index 0000000..4be7f58 --- /dev/null +++ b/test/TEX/usepackage.py @@ -0,0 +1,74 @@ +#!/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__" + +""" +Validate that we can set the LATEX string to our own utility, that +the produced .dvi, .aux and .log files get removed by the -c option, +and that we can use this to wrap calls to the real latex utility. +""" + +import os +import os.path +import string +import sys +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +latex = test.where_is('latex') + +if not latex: + test.skip_test('could not find latex; skipping test\n') + +test.write('SConstruct', """ +import os +ENV = { 'PATH' : os.environ['PATH'], + 'TEXINPUTS' : [ 'subdir', os.environ.get('TEXINPUTS', '') ] } +foo = Environment(ENV = ENV) +foo.DVI(target = 'foo.dvi', source = 'foo.ltx') +""" % locals()) + +test.write('foo.ltx', r""" +\documentclass{letter} +\usepackage{bar} +\begin{document} +This is the foo.ltx file. +\end{document} +""") + +test.write('bar.sty', "\n") + +test.run(arguments = 'foo.dvi', stderr = None) + +test.write('bar.sty', "\n\n\n") + +test.not_up_to_date(arguments = 'foo.dvi', stderr = None) + + + +test.pass_test() diff --git a/test/explain.py b/test/explain.py index f69b00f..9b721d2 100644 --- a/test/explain.py +++ b/test/explain.py @@ -41,8 +41,8 @@ test.subdir('work1', ['work1', 'src'], ['work1', 'src', 'subdir'], 'work4', ['work4', 'src'], ['work4', 'src', 'subdir'], 'work5') -subdir_file6 = os.path.join('subdir', 'file6') -subdir_file6_in = os.path.join('subdir', 'file6.in') +subdir_file7 = os.path.join('subdir', 'file7') +subdir_file7_in = os.path.join('subdir', 'file7.in') cat_py = test.workpath('cat.py') test.write(cat_py, r""" @@ -113,7 +113,9 @@ env.Command('file4', 'file4.in', r'%(_python_)s %(cat_py)s $TARGET $FILE4FLAG $SOURCES', FILE4FLAG='-') env.Cat('file5', 'file5.k') -env.Cat('subdir/file6', 'subdir/file6.in') +file6 = env.Cat('file6', 'file6.in') +AlwaysBuild(file6) +env.Cat('subdir/file7', 'subdir/file7.in') """ % locals()) test.write(['work1', 'src', 'aaa'], "aaa 1\n") @@ -149,7 +151,9 @@ include ../inc/bbb.k file5.k 1 line 4 """) -test.write(['work1', 'src', 'subdir', 'file6.in'], "subdir/file6.in 1\n") +test.write(['work1', 'src', 'file6.in'], "file6.in 1\n") + +test.write(['work1', 'src', 'subdir', 'file7.in'], "subdir/file7.in 1\n") work1_inc_aaa = test.workpath('work1', 'inc', 'aaa') work1_inc_ddd = test.workpath('work1', 'inc', 'ddd') @@ -176,8 +180,10 @@ scons: building `%(work1_inc_bbb_k)s' because it doesn't exist Install file: "bbb.k" as "%(work1_inc_bbb_k)s" scons: building `file5' because it doesn't exist %(_python_)s %(cat_py)s file5 file5.k -scons: building `%(subdir_file6)s' because it doesn't exist -%(_python_)s %(cat_py)s %(subdir_file6)s %(subdir_file6_in)s +scons: building `file6' because it doesn't exist +%(_python_)s %(cat_py)s file6 file6.in +scons: building `%(subdir_file7)s' because it doesn't exist +%(_python_)s %(cat_py)s %(subdir_file7)s %(subdir_file7_in)s """ % locals()) test.run(chdir='work1/src', arguments=args, stdout=expect) @@ -200,6 +206,7 @@ ddd 1 eee.in 1 file5.k 1 line 4 """) +test.must_match(['work1', 'src', 'file6'], "file6.in 1\n") # test.write(['work1', 'src', 'file1.in'], "file1.in 2\n") @@ -223,6 +230,8 @@ scons: rebuilding `%(work1_inc_bbb_k)s' because: Install file: "bbb.k" as "%(work1_inc_bbb_k)s" scons: rebuilding `file5' because `%(work1_inc_bbb_k)s' changed %(_python_)s %(cat_py)s file5 file5.k +scons: rebuilding `file6' because AlwaysBuild() is specified +%(_python_)s %(cat_py)s file6 file6.in """ % locals()) test.run(chdir='work1/src', arguments=args, stdout=expect) diff --git a/test/option--random.py b/test/option--random.py index f806bbe..8c7ef1e 100644 --- a/test/option--random.py +++ b/test/option--random.py @@ -34,7 +34,7 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', """ +test.write('SConscript', """\ def cat(env, source, target): target = str(target[0]) source = map(str, source) @@ -53,23 +53,66 @@ test.write('aaa.in', "aaa.in\n") test.write('bbb.in', "bbb.in\n") test.write('ccc.in', "ccc.in\n") -test.run(arguments = '--random .') -test.fail_test(test.read('all') != "aaa.in\nbbb.in\nccc.in\n") + +test.write('SConstruct', """\ +SetOption('random', 1) +SConscript('SConscript') +""") + +test.run(arguments = '-n -Q') +non_random_output = test.stdout() + +tries = 0 +max_tries = 3 +while test.stdout() == non_random_output: + if tries >= max_tries: + print "--random generated the non-random output %s times!" % max_tries + test.fail_test() + tries = tries + 1 + test.run(arguments = '-n -Q --random') + + + +test.write('SConstruct', """\ +SConscript('SConscript') +""") + +test.run(arguments = '-n -Q') +non_random_output = test.stdout() + +tries = 0 +max_tries = 3 +while test.stdout() == non_random_output: + if tries >= max_tries: + print "--random generated the non-random output %s times!" % max_tries + test.fail_test() + tries = tries + 1 + test.run(arguments = '-n -Q --random') + + + +test.run(arguments = '-Q --random') + +test.must_match('all', "aaa.in\nbbb.in\nccc.in\n") test.run(arguments = '-q --random .') test.run(arguments = '-c --random .') -test.fail_test(os.path.exists(test.workpath('aaa.out'))) -test.fail_test(os.path.exists(test.workpath('bbb.out'))) -test.fail_test(os.path.exists(test.workpath('ccc.out'))) -test.fail_test(os.path.exists(test.workpath('all'))) +test.must_not_exist(test.workpath('aaa.out')) +test.must_not_exist(test.workpath('bbb.out')) +test.must_not_exist(test.workpath('ccc.out')) +test.must_not_exist(test.workpath('all')) test.run(arguments = '-q --random .', status = 1) test.run(arguments = '--random .') -test.fail_test(test.read('all') != "aaa.in\nbbb.in\nccc.in\n") +test.must_match('all', "aaa.in\nbbb.in\nccc.in\n") + +test.run(arguments = '-c --random .') + + test.pass_test() |